Compare commits

..

231 Commits

Author SHA1 Message Date
sk
937304f27b improve profile metadata editor styles 2023-08-24 00:05:36 +02:00
sk
6b4ce0ea69 hide roles when in edit mode 2023-08-23 23:48:42 +02:00
sk
7f0c4860f8 clean up code 2023-08-23 23:47:06 +02:00
sk
9b4c70a5ed fix jumping profile layout
closes sk22#656
2023-08-23 23:46:23 +02:00
sk
49137273ae clean up code 2023-08-23 23:38:45 +02:00
sk
647e3e5e85 progress indicator for emoji reactions 2023-08-23 23:38:12 +02:00
sk
4920bf63e3 oops. accidentally removed a setting 2023-08-23 23:12:11 +02:00
sk
0afcdb2cdf emoji reactions for announcements! 2023-08-23 22:56:11 +02:00
sk
d96c3c3c8a fix paddings around dummys, text and spoilers
closes sk22#638
2023-08-23 22:12:14 +02:00
sk
f5e5408d70 Merge remote-tracking branch 'upstream/master' 2023-08-23 20:02:06 +02:00
sk
d62899c990 fix reaction data and binding inconsistencies 2023-08-23 19:49:07 +02:00
Grishka
e5bdeba1d7 Fix strings 2023-08-23 00:44:40 +03:00
Grishka
8d7db7774f Merge branch 'l10n_master' 2023-08-23 00:40:45 +03:00
Grishka
78d22c670c Fix reporting 2023-08-23 00:40:38 +03:00
Eugen Rochko
0a679109f5 New translations strings.xml (Arabic) 2023-08-22 17:14:41 +02:00
Eugen Rochko
e843142b7e New translations strings.xml (Arabic) 2023-08-22 13:15:25 +02:00
Eugen Rochko
72e728f655 New translations strings.xml (French) 2023-08-22 13:15:23 +02:00
Eugen Rochko
ef56792f56 New translations strings.xml (Arabic) 2023-08-22 11:39:27 +02:00
Eugen Rochko
504a6959e8 New translations strings.xml (French) 2023-08-22 11:39:25 +02:00
sk
6054a3d65c Merge remote-tracking branch 'weblate/main' 2023-08-21 22:43:23 +02:00
sk
f50eac02d8 move adding reactions to reactions item 2023-08-21 22:40:19 +02:00
snerk
9634db9061 Translated using Weblate (Norwegian Nynorsk)
Currently translated at 20.8% (75 of 360 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/nn/
2023-08-21 22:40:19 +02:00
snerk
97e3e283dd Translated using Weblate (Norwegian Nynorsk)
Currently translated at 18.8% (68 of 360 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/nn/
2023-08-21 22:40:19 +02:00
snerk
f1e233569b Added translation using Weblate (Norwegian Nynorsk) 2023-08-21 22:40:19 +02:00
alextecplayz
04dd637fa9 Translated using Weblate (Romanian)
Currently translated at 100.0% (360 of 360 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/ro/
2023-08-21 22:40:19 +02:00
gallegonovato
c48a4105a9 Translated using Weblate (Spanish)
Currently translated at 100.0% (360 of 360 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/es/
2023-08-21 22:40:19 +02:00
EndermanCo
aac53d949b Translated using Weblate (Persian)
Currently translated at 38.8% (7 of 18 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/fa/
2023-08-21 22:40:19 +02:00
EndermanCo
9bb4e5b467 Translated using Weblate (Persian)
Currently translated at 100.0% (360 of 360 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/fa/
2023-08-21 22:40:19 +02:00
0eoc
fb0391d5cd Translated using Weblate (Russian)
Currently translated at 61.1% (11 of 18 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/ru/
2023-08-21 22:40:19 +02:00
ihor_ck
e4d898c903 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (360 of 360 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/uk/
2023-08-21 22:40:19 +02:00
Linerly
da222f75bb Translated using Weblate (Indonesian)
Currently translated at 100.0% (360 of 360 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/id/
2023-08-21 22:40:19 +02:00
Choukajohn
25fbd91eb3 Translated using Weblate (French)
Currently translated at 100.0% (360 of 360 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/fr/
2023-08-21 22:40:19 +02:00
0eoc
d458cca7bf Translated using Weblate (Russian)
Currently translated at 96.1% (323 of 336 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/ru/
2023-08-21 22:40:19 +02:00
gallegonovato
3933a61b5a Translated using Weblate (Spanish)
Currently translated at 100.0% (336 of 336 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/es/
2023-08-21 22:40:19 +02:00
Codeberg Translate
29092bbf36 Update translation files
Updated by "Remove blank strings" hook in Weblate.

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/
2023-08-21 22:40:19 +02:00
edxkl
a33d2578c9 Translated using Weblate (Portuguese (Brazil))
Currently translated at 94.4% (17 of 18 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/pt_BR/
2023-08-21 22:40:19 +02:00
edxkl
9afe4b5ac6 Translated using Weblate (Portuguese (Brazil))
Currently translated at 97.3% (327 of 336 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/pt_BR/
2023-08-21 22:40:19 +02:00
EndermanCo
6782006b05 Translated using Weblate (Persian)
Currently translated at 27.7% (5 of 18 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/fa/
2023-08-21 22:40:19 +02:00
0eoc
90bdbefd48 Translated using Weblate (Russian)
Currently translated at 61.1% (11 of 18 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/ru/
2023-08-21 22:40:19 +02:00
gicorada
b7bcf1082e Translated using Weblate (Italian)
Currently translated at 100.0% (336 of 336 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/it/
2023-08-21 22:40:19 +02:00
EndermanCo
ba9bbc5b6e Translated using Weblate (Persian)
Currently translated at 100.0% (336 of 336 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/fa/
2023-08-21 22:40:19 +02:00
0eoc
0fecfbd50c Translated using Weblate (Russian)
Currently translated at 81.2% (273 of 336 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/ru/
2023-08-21 22:40:19 +02:00
gallegonovato
0031dc6119 Translated using Weblate (Spanish)
Currently translated at 99.1% (333 of 336 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/es/
2023-08-21 22:40:19 +02:00
EndermanCo
79e606698e Translated using Weblate (Persian)
Currently translated at 100.0% (336 of 336 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/fa/
2023-08-21 22:40:19 +02:00
EndermanCo
3c9fc43780 Translated using Weblate (Persian)
Currently translated at 98.5% (331 of 336 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/fa/
2023-08-21 22:40:19 +02:00
ihor_ck
adb9b7394a Translated using Weblate (Ukrainian)
Currently translated at 100.0% (336 of 336 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/uk/
2023-08-21 22:40:19 +02:00
Eryk Michalak
6191fdfaef Translated using Weblate (Polish)
Currently translated at 91.3% (307 of 336 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/pl/
2023-08-21 22:40:19 +02:00
Linerly
446754e8a6 Translated using Weblate (Indonesian)
Currently translated at 100.0% (336 of 336 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/id/
2023-08-21 22:40:19 +02:00
Choukajohn
30c67b0b39 Translated using Weblate (French)
Currently translated at 100.0% (336 of 336 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/fr/
2023-08-21 22:40:19 +02:00
Hudobni Volk
1043ea7b11 Translated using Weblate (Slovenian)
Currently translated at 100.0% (336 of 336 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/sl/
2023-08-21 22:40:19 +02:00
alextecplayz
b449bcd006 Translated using Weblate (Romanian)
Currently translated at 100.0% (336 of 336 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/ro/
2023-08-21 22:40:19 +02:00
EndermanCo
a9d513b564 Translated using Weblate (Persian)
Currently translated at 98.5% (330 of 335 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/fa/
2023-08-21 22:40:19 +02:00
Hudobni Volk
5cef527810 Translated using Weblate (Slovenian)
Currently translated at 61.1% (11 of 18 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/sl/
2023-08-21 22:40:19 +02:00
ganjibaiemade
8bb907747d Translated using Weblate (Chinese (Traditional))
Currently translated at 18.2% (61 of 335 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/zh_Hant/
2023-08-21 22:40:19 +02:00
Hudobni Volk
9c889f8df3 Translated using Weblate (Slovenian)
Currently translated at 100.0% (335 of 335 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/sl/
2023-08-21 22:40:19 +02:00
Linerly
cbc164d844 Translated using Weblate (Indonesian)
Currently translated at 100.0% (335 of 335 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/id/
2023-08-21 22:40:19 +02:00
Eugen Rochko
b8e3060887 New translations strings.xml (Arabic) 2023-08-21 21:00:00 +02:00
Eugen Rochko
1aa1ede421 New translations strings.xml (Arabic) 2023-08-21 20:03:05 +02:00
Eugen Rochko
480dba7629 New translations strings.xml (Arabic) 2023-08-21 19:05:39 +02:00
Eugen Rochko
9b9c66a149 New translations strings.xml (Arabic) 2023-08-21 17:43:00 +02:00
Eugen Rochko
0f5eb923ee New translations strings.xml (Arabic) 2023-08-21 16:13:06 +02:00
snerk
7f521b3129 Translated using Weblate (Norwegian Nynorsk)
Currently translated at 100.0% (369 of 369 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/nn/
2023-08-21 09:41:52 +00:00
EndermanCo
9ce217d1f2 Translated using Weblate (Persian)
Currently translated at 100.0% (369 of 369 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/fa/
2023-08-21 09:41:51 +00:00
ihor_ck
7b1bd3ccad Translated using Weblate (Ukrainian)
Currently translated at 100.0% (369 of 369 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/uk/
2023-08-21 09:41:51 +00:00
Oliebol
13b1cbde6b Translated using Weblate (Dutch)
Currently translated at 82.9% (306 of 369 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/nl/
2023-08-21 09:41:51 +00:00
Linerly
8d898a1a78 Translated using Weblate (Indonesian)
Currently translated at 100.0% (369 of 369 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/id/
2023-08-21 09:41:51 +00:00
Choukajohn
51c2890ede Translated using Weblate (French)
Currently translated at 100.0% (369 of 369 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/fr/
2023-08-21 09:41:51 +00:00
gallegonovato
03642faa9c Translated using Weblate (Spanish)
Currently translated at 100.0% (369 of 369 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/es/
2023-08-21 09:41:51 +00:00
sk22
084c6e1e59 Translated using Weblate (German)
Currently translated at 94.5% (349 of 369 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/de/
2023-08-21 09:41:51 +00:00
Eugen Rochko
90ed28e7a0 New translations strings.xml (French) 2023-08-20 17:34:05 +01:00
Eugen Rochko
d2b45c1c84 New translations strings.xml (Swedish) 2023-08-20 12:43:05 +01:00
Eugen Rochko
a119ba5f80 New translations strings.xml (Danish) 2023-08-19 17:08:25 +01:00
Eugen Rochko
8c1191a08f New translations strings.xml (Danish) 2023-08-19 16:09:27 +01:00
Eugen Rochko
4275d596e6 New translations strings.xml (Thai) 2023-08-19 12:46:09 +01:00
snerk
2709d5226d Translated using Weblate (Norwegian Nynorsk)
Currently translated at 21.6% (78 of 360 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/nn/
2023-08-18 16:18:22 +00:00
snerk
8f613e3255 Translated using Weblate (Norwegian Nynorsk)
Currently translated at 20.8% (75 of 360 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/nn/
2023-08-18 16:18:22 +00:00
snerk
6831e846cf Translated using Weblate (Norwegian Nynorsk)
Currently translated at 18.8% (68 of 360 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/nn/
2023-08-18 16:18:22 +00:00
snerk
034eb9427d Added translation using Weblate (Norwegian Nynorsk) 2023-08-18 16:18:22 +00:00
alextecplayz
f73c325db3 Translated using Weblate (Romanian)
Currently translated at 100.0% (360 of 360 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/ro/
2023-08-18 16:18:22 +00:00
gallegonovato
5e2b11c504 Translated using Weblate (Spanish)
Currently translated at 100.0% (360 of 360 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/es/
2023-08-18 16:18:22 +00:00
EndermanCo
ec13133431 Translated using Weblate (Persian)
Currently translated at 38.8% (7 of 18 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/fa/
2023-08-18 16:18:22 +00:00
EndermanCo
a8a56a3ed8 Translated using Weblate (Persian)
Currently translated at 100.0% (360 of 360 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/fa/
2023-08-18 16:18:22 +00:00
0eoc
4e9c7c4de2 Translated using Weblate (Russian)
Currently translated at 61.1% (11 of 18 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/ru/
2023-08-18 16:18:22 +00:00
ihor_ck
ffb7894098 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (360 of 360 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/uk/
2023-08-18 16:18:22 +00:00
Linerly
0d9520ac45 Translated using Weblate (Indonesian)
Currently translated at 100.0% (360 of 360 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/id/
2023-08-18 16:18:22 +00:00
Choukajohn
be852e57df Translated using Weblate (French)
Currently translated at 100.0% (360 of 360 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/fr/
2023-08-18 16:18:22 +00:00
0eoc
157b38b8ae Translated using Weblate (Russian)
Currently translated at 96.1% (323 of 336 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/ru/
2023-08-18 16:18:22 +00:00
gallegonovato
83196a1a0d Translated using Weblate (Spanish)
Currently translated at 100.0% (336 of 336 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/es/
2023-08-18 16:18:22 +00:00
Codeberg Translate
306225b054 Update translation files
Updated by "Remove blank strings" hook in Weblate.

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/
2023-08-18 16:18:22 +00:00
edxkl
6efc71d8d2 Translated using Weblate (Portuguese (Brazil))
Currently translated at 94.4% (17 of 18 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/pt_BR/
2023-08-18 16:18:21 +00:00
edxkl
cc4cd4d3f8 Translated using Weblate (Portuguese (Brazil))
Currently translated at 97.3% (327 of 336 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/pt_BR/
2023-08-18 16:18:21 +00:00
EndermanCo
00e3292205 Translated using Weblate (Persian)
Currently translated at 27.7% (5 of 18 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/fa/
2023-08-18 16:18:21 +00:00
0eoc
316952423c Translated using Weblate (Russian)
Currently translated at 61.1% (11 of 18 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/ru/
2023-08-18 16:18:21 +00:00
gicorada
61c2abd014 Translated using Weblate (Italian)
Currently translated at 100.0% (336 of 336 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/it/
2023-08-18 16:18:21 +00:00
EndermanCo
ee5f299b90 Translated using Weblate (Persian)
Currently translated at 100.0% (336 of 336 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/fa/
2023-08-18 16:18:21 +00:00
0eoc
f153846381 Translated using Weblate (Russian)
Currently translated at 81.2% (273 of 336 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/ru/
2023-08-18 16:18:21 +00:00
gallegonovato
0656db0858 Translated using Weblate (Spanish)
Currently translated at 99.1% (333 of 336 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/es/
2023-08-18 16:18:21 +00:00
EndermanCo
7f250cb8df Translated using Weblate (Persian)
Currently translated at 100.0% (336 of 336 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/fa/
2023-08-18 16:18:21 +00:00
EndermanCo
a1e73eca89 Translated using Weblate (Persian)
Currently translated at 98.5% (331 of 336 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/fa/
2023-08-18 16:18:21 +00:00
ihor_ck
1dc6936da6 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (336 of 336 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/uk/
2023-08-18 16:18:21 +00:00
Eryk Michalak
0431d80a8d Translated using Weblate (Polish)
Currently translated at 91.3% (307 of 336 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/pl/
2023-08-18 16:18:21 +00:00
Linerly
eaa78093f7 Translated using Weblate (Indonesian)
Currently translated at 100.0% (336 of 336 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/id/
2023-08-18 16:18:21 +00:00
Choukajohn
25b7151fde Translated using Weblate (French)
Currently translated at 100.0% (336 of 336 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/fr/
2023-08-18 16:18:21 +00:00
Hudobni Volk
0438b579b6 Translated using Weblate (Slovenian)
Currently translated at 100.0% (336 of 336 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/sl/
2023-08-18 16:18:21 +00:00
alextecplayz
afa50a4e8c Translated using Weblate (Romanian)
Currently translated at 100.0% (336 of 336 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/ro/
2023-08-18 16:18:21 +00:00
EndermanCo
85bdb0067b Translated using Weblate (Persian)
Currently translated at 98.5% (330 of 335 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/fa/
2023-08-18 16:18:21 +00:00
Hudobni Volk
760cbc7f9a Translated using Weblate (Slovenian)
Currently translated at 61.1% (11 of 18 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/sl/
2023-08-18 16:18:21 +00:00
ganjibaiemade
d0e34fcd90 Translated using Weblate (Chinese (Traditional))
Currently translated at 18.2% (61 of 335 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/zh_Hant/
2023-08-18 16:18:21 +00:00
Hudobni Volk
da434b9a9b Translated using Weblate (Slovenian)
Currently translated at 100.0% (335 of 335 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/sl/
2023-08-18 16:18:21 +00:00
Linerly
48863dd510 Translated using Weblate (Indonesian)
Currently translated at 100.0% (335 of 335 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/id/
2023-08-18 16:18:21 +00:00
FineFindus
2ca34278f9 build: add F-Droid flavor (#760)
* build: add fdroid flavor

* feat: disable fcm registration in f-droid flavor
2023-08-18 18:15:57 +02:00
Jacoco
a79779f813 Emoji Reactions Support (#645)
* Display Pleroma emoji reactions

* Interact with existing Pleroma emoji reactions

* Setting for emoji reaction support

* Setting for displaying reactions in timelines

* More horizontal padding on reactions display item

* List accounts who reacted

* Arbitrary emoji reaction from status footer

* Hide custom emoji keyboard when emoji is selected

* Clear preferences before applying
All preferences get written anyways so nothing will be lost

* Reset react visibility state on bind

* Fix custom emoji turning black when reacting

* Load reactions when a new one is added

* Emoji reactions grid

* Load custom emoji in reactions list fragment

* New reaction toast messages and Unicode emoji regex

* Make custom emoji picker for reactions scrollable

* Scroll down to show custom emoji picker when reacting

* Divider after reaction custom emoji picker

* Animate react button opacity back in

* fix plural strings

* re-implement reactions using horizontal recycler view

* update reactions with event

* tweak emoji font size

* tweak button styles (a tiny bit)

* change footer react button behavior

* fix emoji reaction status item padding

* move emoji reactions below content items

* add content description and tooltip

* use custom emoji keyboard to enter unicode emoji

* fix reactions clearing on status counter updates

* fix space next to emoji reactions not clickable

* make compatible with glitch-soc

* Remove now unused EmojiReactionsView class

* improve handling of reaction padding

---------

Co-authored-by: sk <sk22@mailbox.org>
2023-08-18 18:14:33 +02:00
Eugen Rochko
cc83f2baf3 New translations strings.xml (Danish) 2023-08-18 01:51:02 +01:00
Eugen Rochko
728496b831 New translations strings.xml (Danish) 2023-08-18 00:06:12 +01:00
Eugen Rochko
bbc99162c6 New translations strings.xml (Danish) 2023-08-17 16:47:55 +01:00
Eugen Rochko
eed3af9e3e New translations strings.xml (Arabic) 2023-08-16 22:54:50 +02:00
Eugen Rochko
50187ff376 New translations strings.xml (Arabic) 2023-08-16 21:48:52 +02:00
Eugen Rochko
5f30919fb4 New translations strings.xml (Arabic) 2023-08-16 18:38:58 +02:00
Eugen Rochko
14c3cfac85 New translations strings.xml (French) 2023-08-16 15:50:45 +02:00
Eugen Rochko
e978f02765 New translations strings.xml (French) 2023-08-16 14:09:02 +02:00
Eugen Rochko
8d877c480f New translations strings.xml (French) 2023-08-16 12:20:32 +02:00
Eugen Rochko
c53efee9a1 New translations strings.xml (Thai) 2023-08-15 22:47:32 +02:00
Eugen Rochko
148c461e86 New translations strings.xml (Danish) 2023-08-15 16:40:55 +02:00
Eugen Rochko
fcadb9883d New translations strings.xml (Danish) 2023-08-15 14:59:58 +02:00
Eugen Rochko
bb6491e10a New translations full_description.txt (Danish) 2023-08-15 14:59:57 +02:00
Eugen Rochko
6248ccf376 New translations full_description.txt (Danish) 2023-08-15 13:53:21 +02:00
Eugen Rochko
c9e08f36fa New translations full_description.txt (Danish) 2023-08-15 03:19:07 +02:00
Eugen Rochko
10b95d753b New translations full_description.txt (Danish) 2023-08-15 02:15:33 +02:00
Eugen Rochko
c3989083cf New translations strings.xml (Portuguese) 2023-08-14 15:33:01 +02:00
Eugen Rochko
01db585094 New translations strings.xml (Basque) 2023-08-14 01:42:56 +02:00
Eugen Rochko
cc67cb330c New translations full_description.txt (Basque) 2023-08-14 00:10:18 +02:00
Eugen Rochko
52ed3c5a04 New translations strings.xml (Basque) 2023-08-14 00:10:17 +02:00
Eugen Rochko
5976f6230a New translations strings.xml (Chinese Simplified) 2023-08-13 18:58:06 +02:00
Eugen Rochko
3553f03a95 New translations strings.xml (Slovenian) 2023-08-13 11:06:06 +02:00
Eugen Rochko
d6e2d889c3 New translations strings.xml (Slovenian) 2023-08-12 16:02:40 +02:00
Eugen Rochko
a777b3b450 New translations strings.xml (Slovenian) 2023-08-12 14:07:49 +02:00
Eugen Rochko
9957efbea0 New translations strings.xml (Norwegian) 2023-08-12 01:06:29 +02:00
Eugen Rochko
22e7b9730f New translations strings.xml (Vietnamese) 2023-08-11 17:50:37 +02:00
Eugen Rochko
91470b8509 New translations strings.xml (Vietnamese) 2023-08-11 16:21:15 +02:00
Eugen Rochko
c9d5327328 New translations strings.xml (Russian) 2023-08-11 12:41:37 +02:00
Eugen Rochko
1aa61b72e5 New translations strings.xml (Russian) 2023-08-11 10:45:11 +02:00
Eugen Rochko
3ca5edc3fc New translations strings.xml (Russian) 2023-08-11 09:19:15 +02:00
Eugen Rochko
a092ebaeb3 New translations strings.xml (Russian) 2023-08-11 08:03:02 +02:00
Eugen Rochko
5b9e84c255 New translations strings.xml (Russian) 2023-08-11 07:07:00 +02:00
Eugen Rochko
9c058b926f New translations strings.xml (Russian) 2023-08-10 12:30:16 +02:00
Eugen Rochko
4f2d2ae6e8 New translations strings.xml (Russian) 2023-08-10 11:10:22 +02:00
Eugen Rochko
75aa26a018 New translations strings.xml (Russian) 2023-08-09 14:50:26 +02:00
Eugen Rochko
0f795254e5 New translations strings.xml (Russian) 2023-08-09 13:28:03 +02:00
Eugen Rochko
33592f0a83 New translations strings.xml (Swedish) 2023-08-07 12:43:38 +02:00
Eugen Rochko
d6fd01eaca New translations strings.xml (Swedish) 2023-08-07 10:56:51 +02:00
Mark Hansen
1cdc58378a Sort Hashtags you follow menu (#695)
* Sort Hashtags you follow menu

* change sorting method

---------

Co-authored-by: Mark Hansen <mhansen@accusoft.com>
Co-authored-by: sk <sk22@mailbox.org>
2023-08-05 20:43:23 +02:00
FineFindus
584b11fce3 feat: allow playing multiple media (#755)
* feat: allow playing multiple media

* replace audio overlay icon

---------

Co-authored-by: sk <sk22@mailbox.org>
2023-08-05 20:35:57 +02:00
FineFindus
fe2039062b fix(remote): send remoteAccount (#754)
closes sk22#664

* fix(remote): send remoteAccount

* fix(server-about): set adming account as remote

* fix(remote): show remote local accounts as remote
2023-08-05 20:30:41 +02:00
FineFindus
0269756b52 feat: change mute duration (#751)
* feat: add mute timer

* fix: missing ressources

* refactor(add-mute-timer): change of the mute timer popup layout

* tweak mute dialog

---------

Co-authored-by: LucasGGamerM <lucassggabriel@gmail.com>
Co-authored-by: sk <sk22@mailbox.org>
2023-08-05 20:17:40 +02:00
FineFindus
df1a6cf764 feat(openURL): open about in app (#750) 2023-08-05 19:44:18 +02:00
FineFindus
6d2385b6b3 feat: support UnifiedPush notifications (#749)
* build: add unified push dependency

* feat(notification): allow arbitrary push notification endpoint

* feat(notification/unified-push): show notification

* refactor(unifiedPush): use more consise null check

* feat(settings/notification): add UnifiedPush toggle

* feat(settings/notification): show no distributor message

* feat(settings/notification): disable unifiedpush when no distributor is available

* change icon name

---------

Co-authored-by: sk <sk22@mailbox.org>
2023-08-05 19:42:10 +02:00
FineFindus
44eaa36cef fix: copy link invisible on EMUI (#753) 2023-08-05 19:33:36 +02:00
sk
50b40c4a07 Merge remote-tracking branch 'upstream/master' 2023-08-05 19:32:28 +02:00
Eugen Rochko
ee6e0ff26c New translations strings.xml (Russian) 2023-08-04 19:26:18 +02:00
Eugen Rochko
4d9574bf38 New translations strings.xml (Russian) 2023-08-04 17:51:30 +02:00
Eugen Rochko
813be9a2be New translations strings.xml (Russian) 2023-08-04 16:30:43 +02:00
Eugen Rochko
cc76ebfafb New translations strings.xml (Russian) 2023-08-04 14:58:02 +02:00
Eugen Rochko
7989ee0243 New translations strings.xml (Russian) 2023-08-04 13:58:08 +02:00
Eugen Rochko
3aa1997cfd New translations strings.xml (Russian) 2023-08-04 12:51:54 +02:00
Grishka
c3da15552e Merge branch 'l10n_master' 2023-08-03 17:22:04 +03:00
Grishka
a014fe9443 Fix media layout with unknown sizes 2023-08-03 17:21:40 +03:00
Eugen Rochko
92551d4ca3 New translations strings.xml (Portuguese, Brazilian) 2023-08-03 03:27:17 +02:00
Eugen Rochko
8010858e85 New translations strings.xml (Portuguese, Brazilian) 2023-08-03 02:26:22 +02:00
Eugen Rochko
4efb4875b0 New translations strings.xml (Swedish) 2023-08-02 09:16:24 +02:00
Eugen Rochko
c5d041e46d New translations strings.xml (Portuguese, Brazilian) 2023-08-01 23:11:09 +02:00
Eugen Rochko
53c2223aae New translations strings.xml (Portuguese, Brazilian) 2023-08-01 21:25:07 +02:00
Eugen Rochko
25034ac0ae New translations strings.xml (Portuguese, Brazilian) 2023-08-01 01:41:52 +02:00
Eugen Rochko
ac9de72b75 New translations strings.xml (Portuguese, Brazilian) 2023-08-01 00:42:41 +02:00
Eugen Rochko
1f48ad93f2 New translations strings.xml (Portuguese, Brazilian) 2023-07-31 23:46:36 +02:00
Eugen Rochko
38f7f7aa00 New translations strings.xml (Czech) 2023-07-31 14:32:20 +02:00
Eugen Rochko
fe8175c63a New translations strings.xml (French) 2023-07-30 19:39:54 +02:00
Eugen Rochko
2d9e01bbc1 New translations strings.xml (French) 2023-07-30 18:44:39 +02:00
Eugen Rochko
022a227b08 New translations strings.xml (Turkish) 2023-07-28 00:29:16 +02:00
Eugen Rochko
a2228259f1 New translations full_description.txt (Turkish) 2023-07-27 23:33:53 +02:00
Eugen Rochko
a61af7c56f New translations strings.xml (Turkish) 2023-07-27 23:33:52 +02:00
Eugen Rochko
5d6a646976 New translations strings.xml (French) 2023-07-27 23:33:51 +02:00
Eugen Rochko
628d0d7492 New translations strings.xml (French) 2023-07-27 22:05:11 +02:00
Eugen Rochko
b76c8745ec New translations strings.xml (Norwegian) 2023-07-25 22:57:26 +02:00
Eugen Rochko
51e67bc441 New translations strings.xml (Norwegian) 2023-07-25 21:52:29 +02:00
Gregory K
8887f75b70 Merge pull request #655 from nilathedragon/patch-1
Do not assume languages array will contain entries
2023-07-25 19:12:56 +03:00
Nila
9436a838c0 Do not assume languages array will contain entries 2023-07-25 17:28:41 +02:00
Eugen Rochko
ef120fa36f New translations strings.xml (Swedish) 2023-07-25 00:55:54 +02:00
Eugen Rochko
8c6385e2c5 New translations strings.xml (Swedish) 2023-07-24 23:57:42 +02:00
Eugen Rochko
0bd85d9905 New translations strings.xml (Persian) 2023-07-24 13:40:53 +02:00
Eugen Rochko
ce0dab7b28 New translations strings.xml (Czech) 2023-07-24 09:55:45 +02:00
sk
bdcebf1576 boop verisom 2023-07-23 22:03:25 +02:00
Eugen Rochko
ddcc5670ce New translations strings.xml (Bengali) 2023-07-22 17:48:06 +02:00
Eugen Rochko
86afa184e2 New translations strings.xml (Bengali) 2023-07-22 16:26:35 +02:00
Eugen Rochko
77f341f139 New translations strings.xml (German) 2023-07-22 12:27:40 +02:00
Eugen Rochko
918b5d99c2 New translations strings.xml (Swedish) 2023-07-21 22:09:32 +02:00
Eugen Rochko
7a098d6eff New translations strings.xml (Swedish) 2023-07-21 21:06:04 +02:00
sk
239b6f8202 fix pill-less navigation bar with labels 2023-07-21 11:41:34 +02:00
Jacoco
8404c79148 Re-implement some Akkoma specific things + hide filter settings (#729)
* Replace missing blurhash with accent color

* Correct Akkoma max account fields

* Skip discover on Akkoma

* Akkoma poll limits

* Hide filter settings on Akkoma

* clear search fragment on back

---------

Co-authored-by: sk <sk22@mailbox.org>
2023-07-21 02:29:38 +02:00
sk
5b2d04e09d add navigation bar tab labels - with option to hide them 2023-07-21 01:45:08 +02:00
sk
6bd13f99d2 add a readme link to the sauna repo 2023-07-21 01:15:35 +02:00
sk
2e8e12c1c8 add font tracking to label_medium 2023-07-21 01:08:07 +02:00
sk
17929a6b2d upstream was right about the weird padding
see "Navigation bar target size and margins" in
https://m3.material.io/components/navigation-bar/specs
2023-07-21 00:37:05 +02:00
sk
9455eaf820 parse html in pronouns 2023-07-21 00:30:30 +02:00
sk
cc054487ba configurable pronoun display 2023-07-21 00:22:52 +02:00
sk
e2df320d00 fix edit history header lines 2023-07-21 00:03:11 +02:00
sk
d74313f996 remove code for below-header reply lines 2023-07-20 23:58:18 +02:00
sk
b3cab67049 fix null pointer when accessing draft content type
closes sk22#734
2023-07-20 23:43:46 +02:00
sk
996f0b22b9 use theme color for alt badge text 2023-07-20 23:39:11 +02:00
sk
67952ea98e fix bugged alt text badge state 2023-07-20 23:39:00 +02:00
sk
7a02ca435f make alt badge more transparent
closes sk22#735
2023-07-20 23:19:47 +02:00
Eugen Rochko
71f81283f5 New translations strings.xml (Polish) 2023-07-20 13:51:45 +02:00
Eugen Rochko
058c7c3c33 New translations strings.xml (Danish) 2023-07-20 10:31:41 +02:00
Eugen Rochko
870e33879b New translations strings.xml (Indonesian) 2023-07-19 07:48:32 +02:00
Eugen Rochko
3ca82bdfc5 New translations strings.xml (Japanese) 2023-07-19 05:08:28 +02:00
Eugen Rochko
4721bad286 New translations short_description.txt (Armenian) 2023-07-18 16:30:06 +02:00
Eugen Rochko
f040cf2f07 New translations full_description.txt (Armenian) 2023-07-18 16:30:05 +02:00
Eugen Rochko
8d50717c90 New translations strings.xml (Armenian) 2023-07-18 16:30:04 +02:00
Eugen Rochko
2512ad3c95 New translations strings.xml (Armenian) 2023-07-18 14:49:00 +02:00
sk
8d55f62da9 round inset notifications
closes sk22#665
2023-07-18 12:54:04 +02:00
Eugen Rochko
bc7e007634 New translations strings.xml (Swedish) 2023-07-18 12:03:42 +02:00
Eugen Rochko
1f3c87e0c7 New translations strings.xml (Swedish) 2023-07-18 10:31:42 +02:00
sk
ee0048a406 fix wrong margins for media posts with cw / without text 2023-07-18 09:59:47 +02:00
sk
14dcc769f2 fix monochrome icon
closes sk22#719
2023-07-18 09:01:13 +02:00
sk
f2e6255eb3 remove flagship instance reference 2023-07-18 08:52:40 +02:00
sk
7d392e20fb fix trending hashtags not loading
closes sk22#724
2023-07-18 08:40:54 +02:00
Eugen Rochko
73e08faee9 New translations strings.xml (Persian) 2023-07-13 20:45:58 +02:00
Eugen Rochko
02dc7711e4 New translations strings.xml (Persian) 2023-07-13 19:38:29 +02:00
Eugen Rochko
67b4d80e5b New translations strings.xml (Spanish) 2023-07-13 16:08:11 +02:00
Eugen Rochko
5168d2bb39 New translations strings.xml (Spanish) 2023-07-13 14:59:49 +02:00
Eugen Rochko
57190a75bf New translations strings.xml (Indonesian) 2023-07-13 11:54:32 +02:00
Eugen Rochko
f10e865895 New translations strings.xml (Indonesian) 2023-07-13 10:48:50 +02:00
146 changed files with 5698 additions and 742 deletions

View File

@@ -54,9 +54,15 @@ You can create drafts, edit them, send them manually later or set a scheduled da
## Installation
### IzzyOnDroid
### Google Play Store
[apt.izzysoft.de/fdroid/index/apk/org.joinmastodon.android.sk](https://apt.izzysoft.de/fdroid/index/apk/org.joinmastodon.android.sk)
[https://play.google.com/store/apps/details?id=org.joinmastodon.android.sk](https://play.google.com/store/apps/details?id=org.joinmastodon.android.sk)
<a href="https://play.google.com/store/apps/details?id=org.joinmastodon.android.sk"><img height="50" alt="Get it on Google Play" src="img/google-play-badge.png"></a>
### F-Droid via IzzyOnDroid
[https://apt.izzysoft.de/fdroid/index/apk/org.joinmastodon.android.sk](https://apt.izzysoft.de/fdroid/index/apk/org.joinmastodon.android.sk)
<a href="https://apt.izzysoft.de/fdroid/index/apk/org.joinmastodon.android.sk"><img height="50" alt="Get it on IzzyOnDroid" src="img/izzy-badge.png"></a>
@@ -64,11 +70,11 @@ Note that you'll need to add Izzy's F-Droid repository to your F-Droid app first
[`https://apt.izzysoft.de/fdroid/repo`](https://apt.izzysoft.de/fdroid/repo)
### Google Play Store
### F-Droid via saunarepo
[play.google.com/store/apps/details?id=org.joinmastodon.android.sk](https://play.google.com/store/apps/details?id=org.joinmastodon.android.sk)
[https://repo.the-sauna.icu](https://repo.the-sauna.icu/)
<a href="https://play.google.com/store/apps/details?id=org.joinmastodon.android.sk"><img height="50" alt="Get it on Google Play" src="img/google-play-badge.png"></a>
<a href="https://repo.the-sauna.icu"><img height="28" alt="Get it on SaunaRepo" src="img/saunarepo-badge.svg"></a>
### F-Droid

View File

@@ -3,6 +3,12 @@ buildscript {
repositories {
google()
mavenCentral()
maven {
url "https://www.jitpack.io"
content {
includeModule 'com.github.UnifiedPush', 'android-connector'
}
}
}
dependencies {
classpath 'com.android.tools.build:gradle:8.0.0'

1
img/saunarepo-badge.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="124.25" height="28" role="img" aria-label="SAUNAREPO"><title>SAUNAREPO</title><g shape-rendering="crispEdges"><rect width="124.25" height="28" fill="#fb8441"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="100"><image x="9" y="7" width="14" height="14" xlink:href="data:image/svg+xml;base64,PHN2ZyBmaWxsPSJ3aGl0ZSIgcm9sZT0iaW1nIiB2aWV3Qm94PSIwIDAgMjQgMjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHRpdGxlPkFuZHJvaWQ8L3RpdGxlPjxwYXRoIGQ9Ik0xNy41MjMgMTUuMzQxNGMtLjU1MTEgMC0uOTk5My0uNDQ4Ni0uOTk5My0uOTk5N3MuNDQ4My0uOTk5My45OTkzLS45OTkzYy41NTExIDAgLjk5OTMuNDQ4My45OTkzLjk5OTMuMDAwMS41NTExLS40NDgyLjk5OTctLjk5OTMuOTk5N20tMTEuMDQ2IDBjLS41NTExIDAtLjk5OTMtLjQ0ODYtLjk5OTMtLjk5OTdzLjQ0ODItLjk5OTMuOTk5My0uOTk5M2MuNTUxMSAwIC45OTkzLjQ0ODMuOTk5My45OTkzIDAgLjU1MTEtLjQ0ODMuOTk5Ny0uOTk5My45OTk3bTExLjQwNDUtNi4wMmwxLjk5NzMtMy40NTkyYS40MTYuNDE2IDAgMDAtLjE1MjEtLjU2NzYuNDE2LjQxNiAwIDAwLS41Njc2LjE1MjFsLTIuMDIyMyAzLjUwM0MxNS41OTAyIDguMjQzOSAxMy44NTMzIDcuODUwOCAxMiA3Ljg1MDhzLTMuNTkwMi4zOTMxLTUuMTM2NyAxLjA5ODlMNC44NDEgNS40NDY3YS40MTYxLjQxNjEgMCAwMC0uNTY3Ny0uMTUyMS40MTU3LjQxNTcgMCAwMC0uMTUyMS41Njc2bDEuOTk3MyAzLjQ1OTJDMi42ODg5IDExLjE4NjcuMzQzMiAxNC42NTg5IDAgMTguNzYxaDI0Yy0uMzQzNS00LjEwMjEtMi42ODkyLTcuNTc0My02LjExODUtOS40Mzk2Ii8+PC9zdmc+"/><text transform="scale(.1)" x="721.25" y="175" textLength="802.5" fill="#fff" font-weight="bold">SAUNAREPO</text></g></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -15,8 +15,8 @@ android {
applicationId "org.joinmastodon.android.sk"
minSdk 23
targetSdk 33
versionCode 96
versionName "2.0.1+fork.96"
versionCode 97
versionName "2.0.3+fork.97"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
resourceConfigurations += ['ar-rSA', 'ar-rDZ', 'be-rBY', 'bn-rBD', 'bs-rBA', 'ca-rES', 'cs-rCZ', 'da-rDK', 'de-rDE', 'el-rGR', 'es-rES', 'eu-rES', 'fa-rIR', 'fi-rFI', 'fil-rPH', 'fr-rFR', 'ga-rIE', 'gd-rGB', 'gl-rES', 'hi-rIN', 'hr-rHR', 'hu-rHU', 'hy-rAM', 'ig-rNG', 'in-rID', 'is-rIS', 'it-rIT', 'iw-rIL', 'ja-rJP', 'kab', 'ko-rKR', 'my-rMM', 'nl-rNL', 'no-rNO', 'oc-rFR', 'pl-rPL', 'pt-rBR', 'pt-rPT', 'ro-rRO', 'ru-rRU', 'si-rLK', 'sl-rSI', 'sv-rSE', 'th-rTH', 'tr-rTR', 'uk-rUA', 'ur-rIN', 'vi-rVN', 'zh-rCN', 'zh-rTW']
}
@@ -34,6 +34,7 @@ android {
}
githubRelease { initWith release }
playRelease { initWith release }
fdroidRelease { initWith release }
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_17
@@ -78,6 +79,7 @@ dependencies {
implementation 'com.github.bottom-software-foundation:bottom-java:2.1.0'
annotationProcessor 'org.parceler:parceler:1.1.12'
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.3'
implementation 'com.github.UnifiedPush:android-connector:2.1.1'
androidTestImplementation 'androidx.test:core:1.5.0'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'

View File

@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<manifest xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
@@ -36,18 +37,6 @@
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.BROWSABLE"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="https" android:host="mastodon.social" android:pathPrefix="/@"/>
</intent-filter>
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.BROWSABLE"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="https" android:host="mastodon.online" android:pathPrefix="/@"/>
</intent-filter>
</activity>
<activity
android:name=".PanicResponderActivity"
@@ -57,7 +46,6 @@
android:theme="@android:style/Theme.NoDisplay">
<intent-filter>
<action android:name="info.guardianproject.panic.action.TRIGGER" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
@@ -100,6 +88,15 @@
<category android:name="me.grishka.fcmtest"/>
</intent-filter>
</receiver>
<receiver android:exported="true" android:enabled="true" android:name=".UnifiedPushNotificationReceiver"
tools:ignore="ExportedReceiver">
<intent-filter>
<action android:name="org.unifiedpush.android.connector.MESSAGE"/>
<action android:name="org.unifiedpush.android.connector.UNREGISTERED"/>
<action android:name="org.unifiedpush.android.connector.NEW_ENDPOINT"/>
<action android:name="org.unifiedpush.android.connector.REGISTRATION_FAILED"/>
</intent-filter>
</receiver>
</application>

View File

@@ -169,7 +169,8 @@ public class AudioPlayerService extends Service{
}
updateNotification(false, false);
getSystemService(AudioManager.class).requestAudioFocus(audioFocusChangeListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
int audiofocus = GlobalUserPreferences.overlayMedia ? AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK : AudioManager.AUDIOFOCUS_GAIN;
getSystemService(AudioManager.class).requestAudioFocus(audioFocusChangeListener, AudioManager.STREAM_MUSIC, audiofocus);
player=new MediaPlayer();
player.setOnPreparedListener(this::onPlayerPrepared);

View File

@@ -57,6 +57,9 @@ public class GlobalUserPreferences{
public static AutoRevealMode autoRevealEqualSpoilers;
public static ColorPreference color;
public static boolean disableM3PillActiveIndicator;
public static boolean showNavigationLabels;
public static boolean displayPronounsInTimelines, displayPronounsInThreads, displayPronounsInUserListings;
public static boolean overlayMedia;
private static SharedPreferences getPrefs(){
return MastodonApp.context.getSharedPreferences("global", Context.MODE_PRIVATE);
@@ -111,6 +114,11 @@ public class GlobalUserPreferences{
autoRevealEqualSpoilers=AutoRevealMode.valueOf(prefs.getString("autoRevealEqualSpoilers", AutoRevealMode.THREADS.name()));
forwardReportDefault=prefs.getBoolean("forwardReportDefault", true);
disableM3PillActiveIndicator=prefs.getBoolean("disableM3PillActiveIndicator", false);
showNavigationLabels=prefs.getBoolean("showNavigationLabels", true);
displayPronounsInTimelines=prefs.getBoolean("displayPronounsInTimelines", true);
displayPronounsInThreads=prefs.getBoolean("displayPronounsInThreads", true);
displayPronounsInUserListings=prefs.getBoolean("displayPronounsInUserListings", true);
overlayMedia=prefs.getBoolean("overlayMedia", false);
if (prefs.contains("prefixRepliesWithRe")) {
prefixReplies = prefs.getBoolean("prefixRepliesWithRe", false)
@@ -164,6 +172,11 @@ public class GlobalUserPreferences{
.putString("autoRevealEqualSpoilers", autoRevealEqualSpoilers.name())
.putBoolean("forwardReportDefault", forwardReportDefault)
.putBoolean("disableM3PillActiveIndicator", disableM3PillActiveIndicator)
.putBoolean("showNavigationLabels", showNavigationLabels)
.putBoolean("displayPronounsInTimelines", displayPronounsInTimelines)
.putBoolean("displayPronounsInThreads", displayPronounsInThreads)
.putBoolean("displayPronounsInUserListings", displayPronounsInUserListings)
.putBoolean("overlayMedia", overlayMedia)
.apply();
}

View File

@@ -14,6 +14,7 @@ import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.util.Log;
@@ -32,6 +33,7 @@ import org.joinmastodon.android.model.Preferences;
import org.joinmastodon.android.model.PushNotification;
import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.model.StatusPrivacy;
import org.joinmastodon.android.ui.text.HtmlParser;
import org.joinmastodon.android.ui.utils.UiUtils;
import org.parceler.Parcels;
@@ -58,7 +60,7 @@ public class PushNotificationReceiver extends BroadcastReceiver{
private static final int SUMMARY_ID = 791;
private static int notificationId = 0;
private static Map<String, Integer> notificationIdsForAccounts = new HashMap<>();
private static final Map<String, Integer> notificationIdsForAccounts = new HashMap<>();
@Override
public void onReceive(Context context, Intent intent){
@@ -148,6 +150,11 @@ public class PushNotificationReceiver extends BroadcastReceiver{
}
}
public void notifyUnifiedPush(Context context, String accountID, org.joinmastodon.android.model.Notification notification) {
// push notifications are only created from the official push notification, so we create a fake from by transforming the notification
PushNotificationReceiver.this.notify(context, PushNotification.fromNotification(context, notification), accountID, notification);
}
private void notify(Context context, PushNotification pn, String accountID, org.joinmastodon.android.model.Notification notification){
NotificationManager nm=context.getSystemService(NotificationManager.class);
AccountSession session=AccountSessionManager.get(accountID);

View File

@@ -0,0 +1,81 @@
package org.joinmastodon.android;
import android.content.Context;
import android.util.Log;
import org.jetbrains.annotations.NotNull;
import org.joinmastodon.android.api.MastodonAPIController;
import org.joinmastodon.android.api.session.AccountSession;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.model.Notification;
import org.joinmastodon.android.model.PaginatedResponse;
import org.unifiedpush.android.connector.MessagingReceiver;
import java.util.List;
import me.grishka.appkit.api.Callback;
import me.grishka.appkit.api.ErrorResponse;
public class UnifiedPushNotificationReceiver extends MessagingReceiver{
private static final String TAG="UnifiedPushNotificationReceiver";
public UnifiedPushNotificationReceiver() {
super();
}
@Override
public void onNewEndpoint(@NotNull Context context, @NotNull String endpoint, @NotNull String instance) {
// Called when a new endpoint be used for sending push messages
Log.d(TAG, "onNewEndpoint: New Endpoint " + endpoint + " for "+ instance);
AccountSession account = AccountSessionManager.getInstance().getLastActiveAccount();
if (account != null)
account.getPushSubscriptionManager().registerAccountForPush(null);
}
@Override
public void onRegistrationFailed(@NotNull Context context, @NotNull String instance) {
// called when the registration is not possible, eg. no network
Log.d(TAG, "onRegistrationFailed: " + instance);
//re-register for gcm
AccountSession account = AccountSessionManager.getInstance().getLastActiveAccount();
if (account != null)
account.getPushSubscriptionManager().registerAccountForPush(null);
}
@Override
public void onUnregistered(@NotNull Context context, @NotNull String instance) {
// called when this application is unregistered from receiving push messages
Log.d(TAG, "onUnregistered: " + instance);
//re-register for gcm
AccountSession account = AccountSessionManager.getInstance().getLastActiveAccount();
if (account != null)
account.getPushSubscriptionManager().registerAccountForPush(null);
}
@Override
public void onMessage(@NotNull Context context, @NotNull byte[] message, @NotNull String instance) {
// Called when a new message is received. The message contains the full POST body of the push message
AccountSession account = AccountSessionManager.getInstance().getAccount(instance);
//this is stupid
// Mastodon stores the info to decrypt the message in the HTTP headers, which are not accessible in UnifiedPush,
// thus it is not possible to decrypt them. SO we need to re-request them from the server and transform them later on
// The official uses fcm and moves the headers to extra data, see
// https://github.com/mastodon/webpush-fcm-relay/blob/cac95b28d5364b0204f629283141ac3fb749e0c5/webpush-fcm-relay.go#L116
// https://github.com/tuskyapp/Tusky/pull/2303#issue-1112080540
account.getCacheController().getNotifications(null, 1, false, false, true, new Callback<>(){
@Override
public void onSuccess(PaginatedResponse<List<Notification>> result){
result.items
.stream()
.findFirst()
.ifPresent(value->MastodonAPIController.runInBackground(()->new PushNotificationReceiver().notifyUnifiedPush(context, instance, value)));
}
@Override
public void onError(ErrorResponse error){
//professional error handling
}
});
}
}

View File

@@ -121,13 +121,13 @@ public abstract class MastodonAPIRequest<T> extends APIRequest<T>{
.orElseGet(() -> this.execNoAuth(domain));
}
public MastodonAPIRequest<T> wrapProgress(Activity activity, @StringRes int message, boolean cancelable){
return wrapProgress(activity, message, cancelable, null);
public MastodonAPIRequest<T> wrapProgress(Context context, @StringRes int message, boolean cancelable){
return wrapProgress(context, message, cancelable, null);
}
public MastodonAPIRequest<T> wrapProgress(Activity activity, @StringRes int message, boolean cancelable, Consumer<ProgressDialog> transform){
progressDialog=new ProgressDialog(activity);
progressDialog.setMessage(activity.getString(message));
public MastodonAPIRequest<T> wrapProgress(Context context, @StringRes int message, boolean cancelable, Consumer<ProgressDialog> transform){
progressDialog=new ProgressDialog(context);
progressDialog.setMessage(context.getString(message));
progressDialog.setCancelable(cancelable);
if (transform != null) transform.accept(progressDialog);
if(cancelable){

View File

@@ -120,9 +120,22 @@ public class PushSubscriptionManager{
return !TextUtils.isEmpty(deviceToken);
}
public void registerAccountForPush(PushSubscription subscription){
// this function is used for registering push notifications using FCM
// to avoid NonFreeNet in F-Droid, this registration is disabled in it
// see https://github.com/LucasGGamerM/moshidon/issues/206 for more context
if(BuildConfig.BUILD_TYPE.equals("fdroidRelease"))
return;
if(TextUtils.isEmpty(deviceToken))
throw new IllegalStateException("No device push token available");
String endpoint = "https://app.joinmastodon.org/relay-to/fcm/"+deviceToken+"/"+accountID;
registerAccountForPush(subscription, endpoint);
}
public void registerAccountForPush(PushSubscription subscription, String endpoint){
MastodonAPIController.runInBackground(()->{
Log.d(TAG, "registerAccountForPush: started for "+accountID);
String encodedPublicKey, encodedAuthKey, pushAccountID;
@@ -151,12 +164,11 @@ public class PushSubscriptionManager{
Log.e(TAG, "registerAccountForPush: error generating encryption key", e);
return;
}
new RegisterForPushNotifications(deviceToken,
new RegisterForPushNotifications(endpoint,
encodedPublicKey,
encodedAuthKey,
subscription==null ? PushSubscription.Alerts.ofAll() : subscription.alerts,
subscription==null ? PushSubscription.Policy.ALL : subscription.policy,
pushAccountID)
subscription==null ? PushSubscription.Policy.ALL : subscription.policy)
.setCallback(new Callback<>(){
@Override
public void onSuccess(PushSubscription result){

View File

@@ -4,8 +4,15 @@ import org.joinmastodon.android.api.MastodonAPIRequest;
import org.joinmastodon.android.model.Relationship;
public class SetAccountMuted extends MastodonAPIRequest<Relationship>{
public SetAccountMuted(String id, boolean muted){
public SetAccountMuted(String id, boolean muted, long duration){
super(HttpMethod.POST, "/accounts/"+id+"/"+(muted ? "mute" : "unmute"), Relationship.class);
setRequestBody(new Object());
setRequestBody(new Request(duration));
}
private static class Request{
public long duration;
public Request(long duration){
this.duration=duration;
}
}
}

View File

@@ -0,0 +1,11 @@
package org.joinmastodon.android.api.requests.announcements;
import org.joinmastodon.android.api.MastodonAPIRequest;
import org.joinmastodon.android.model.Status;
public class AddAnnouncementReaction extends MastodonAPIRequest<Object> {
public AddAnnouncementReaction(String id, String emoji) {
super(HttpMethod.PUT, "/announcements/" + id + "/reactions/" + emoji, Object.class);
setRequestBody(new Object());
}
}

View File

@@ -0,0 +1,9 @@
package org.joinmastodon.android.api.requests.announcements;
import org.joinmastodon.android.api.MastodonAPIRequest;
public class DeleteAnnouncementReaction extends MastodonAPIRequest<Object> {
public DeleteAnnouncementReaction(String id, String emoji) {
super(HttpMethod.DELETE, "/announcements/" + id + "/reactions/" + emoji, Object.class);
}
}

View File

@@ -4,10 +4,10 @@ import org.joinmastodon.android.api.MastodonAPIRequest;
import org.joinmastodon.android.model.PushSubscription;
public class RegisterForPushNotifications extends MastodonAPIRequest<PushSubscription>{
public RegisterForPushNotifications(String deviceToken, String encryptionKey, String authKey, PushSubscription.Alerts alerts, PushSubscription.Policy policy, String accountID){
public RegisterForPushNotifications(String endpoint, String encryptionKey, String authKey, PushSubscription.Alerts alerts, PushSubscription.Policy policy){
super(HttpMethod.POST, "/push/subscription", PushSubscription.class);
Request r=new Request();
r.subscription.endpoint="https://app.joinmastodon.org/relay-to/fcm/"+deviceToken+"/"+accountID;
r.subscription.endpoint=endpoint;
r.data.alerts=alerts;
r.policy=policy;
r.subscription.keys.p256dh=encryptionKey;

View File

@@ -0,0 +1,11 @@
package org.joinmastodon.android.api.requests.statuses;
import org.joinmastodon.android.api.MastodonAPIRequest;
import org.joinmastodon.android.model.Status;
public class AddStatusReaction extends MastodonAPIRequest<Status> {
public AddStatusReaction(String id, String emoji) {
super(HttpMethod.POST, "/statuses/" + id + "/react/" + emoji, Status.class);
setRequestBody(new Object());
}
}

View File

@@ -0,0 +1,11 @@
package org.joinmastodon.android.api.requests.statuses;
import org.joinmastodon.android.api.MastodonAPIRequest;
import org.joinmastodon.android.model.Status;
public class DeleteStatusReaction extends MastodonAPIRequest<Status> {
public DeleteStatusReaction(String id, String emoji) {
super(HttpMethod.POST, "/statuses/" + id + "/unreact/" + emoji, Status.class);
setRequestBody(new Object());
}
}

View File

@@ -0,0 +1,11 @@
package org.joinmastodon.android.api.requests.statuses;
import org.joinmastodon.android.api.MastodonAPIRequest;
import org.joinmastodon.android.model.Status;
public class PleromaAddStatusReaction extends MastodonAPIRequest<Status> {
public PleromaAddStatusReaction(String id, String emoji) {
super(HttpMethod.PUT, "/pleroma/statuses/" + id + "/reactions/" + emoji, Status.class);
setRequestBody(new Object());
}
}

View File

@@ -0,0 +1,10 @@
package org.joinmastodon.android.api.requests.statuses;
import org.joinmastodon.android.api.MastodonAPIRequest;
import org.joinmastodon.android.model.Status;
public class PleromaDeleteStatusReaction extends MastodonAPIRequest<Status> {
public PleromaDeleteStatusReaction(String id, String emoji) {
super(HttpMethod.DELETE, "/pleroma/statuses/" + id + "/reactions/" + emoji, Status.class);
}
}

View File

@@ -0,0 +1,14 @@
package org.joinmastodon.android.api.requests.statuses;
import com.google.gson.reflect.TypeToken;
import org.joinmastodon.android.api.MastodonAPIRequest;
import org.joinmastodon.android.model.EmojiReaction;
import java.util.List;
public class PleromaGetStatusReactions extends MastodonAPIRequest<List<EmojiReaction>> {
public PleromaGetStatusReactions(String id, String emoji) {
super(HttpMethod.GET, "/pleroma/statuses/" + id + "/reactions/" + (emoji != null ? emoji : ""), new TypeToken<>(){});
}
}

View File

@@ -38,6 +38,9 @@ public class AccountLocalPreferences{
public String timelineReplyVisibility; // akkoma-only
public boolean keepOnlyLatestNotification;
public boolean emojiReactionsEnabled;
public boolean showEmojiReactionsInLists;
private final static Type recentLanguagesType = new TypeToken<ArrayList<String>>() {}.getType();
private final static Type timelinesType = new TypeToken<ArrayList<TimelineDefinition>>() {}.getType();
@@ -62,6 +65,8 @@ public class AccountLocalPreferences{
publishButtonText=prefs.getString("publishButtonText", null);
timelineReplyVisibility=prefs.getString("timelineReplyVisibility", null);
keepOnlyLatestNotification=prefs.getBoolean("keepOnlyLatestNotification", false);
emojiReactionsEnabled=prefs.getBoolean("emojiReactionsEnabled", session.getInstance().isPresent() && session.getInstance().get().isAkkoma());
showEmojiReactionsInLists=prefs.getBoolean("showEmojiReactionsInLists", false);
}
public long getNotificationsPauseEndTime(){
@@ -93,6 +98,8 @@ public class AccountLocalPreferences{
.putString("publishButtonText", publishButtonText)
.putString("timelineReplyVisibility", timelineReplyVisibility)
.putBoolean("keepOnlyLatestNotification", keepOnlyLatestNotification)
.putBoolean("emojiReactionsEnabled", emojiReactionsEnabled)
.putBoolean("showEmojiReactionsInLists", showEmojiReactionsInLists)
.apply();
}
}

View File

@@ -1,15 +1,27 @@
package org.joinmastodon.android.events;
import androidx.recyclerview.widget.RecyclerView;
import org.joinmastodon.android.api.CacheController;
import org.joinmastodon.android.model.EmojiReaction;
import org.joinmastodon.android.model.Status;
import java.util.ArrayList;
import java.util.List;
public class StatusCountersUpdatedEvent{
public String id;
public long favorites, reblogs, replies;
public boolean favorited, reblogged, bookmarked, pinned;
public List<EmojiReaction> reactions;
public Status status;
public RecyclerView.ViewHolder viewHolder;
public StatusCountersUpdatedEvent(Status s){
this(s, null);
}
public StatusCountersUpdatedEvent(Status s, RecyclerView.ViewHolder vh){
id=s.id;
status=s;
favorites=s.favouritesCount;
@@ -19,5 +31,7 @@ public class StatusCountersUpdatedEvent{
reblogged=s.reblogged;
bookmarked=s.bookmarked;
pinned=s.pinned;
reactions=new ArrayList<>(s.reactions);
viewHolder=vh;
}
}

View File

@@ -26,6 +26,8 @@ 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.DummyStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.EmojiReactionsStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.HeaderStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.TextStatusDisplayItem;
@@ -71,7 +73,8 @@ public class AnnouncementsFragment extends BaseStatusListFragment<Announcement>
textItem.textSelectable = true;
return List.of(
HeaderStatusDisplayItem.fromAnnouncement(a, fakeStatus, instanceUser, this, accountID, this::onMarkAsRead),
textItem
textItem,
new EmojiReactionsStatusDisplayItem(a.id, this, fakeStatus, accountID, false, true)
);
}

View File

@@ -31,6 +31,7 @@ import org.joinmastodon.android.model.Relationship;
import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.ui.BetterItemAnimator;
import org.joinmastodon.android.ui.displayitems.AccountStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.EmojiReactionsStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.ExtendedFooterStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.FooterStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.GapStatusDisplayItem;
@@ -607,6 +608,15 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
if (header != null) header.rebind();
}
public void updateEmojiReactions(Status status, String itemID){
EmojiReactionsStatusDisplayItem.Holder reactions=findHolderOfType(itemID, EmojiReactionsStatusDisplayItem.Holder.class);
if(reactions != null){
reactions.getItem().status.reactions.clear();
reactions.getItem().status.reactions.addAll(status.reactions);
reactions.rebind();
}
}
public void onGapClick(GapStatusDisplayItem.Holder item){}
public void onWarningClick(WarningFilteredStatusDisplayItem.Holder warning){
@@ -782,6 +792,10 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
}
}
public void scrollBy(int x, int y) {
list.scrollBy(x, y);
}
protected class DisplayItemsAdapter extends UsableRecyclerView.Adapter<BindableViewHolder<StatusDisplayItem>> implements ImageLoaderRecyclerAdapter{
public DisplayItemsAdapter(){

View File

@@ -300,6 +300,13 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
onCustomEmojiClick(emoji);
}
@Override
public void onEmojiSelected(String emoji){
if(getActivity().getCurrentFocus() instanceof EditText edit && edit == mainEditText){
edit.getText().replace(edit.getSelectionStart(), edit.getSelectionEnd(), emoji);
}
}
@Override
public void onBackspace(){
getActivity().dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL));
@@ -449,7 +456,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
} else if (getArguments().containsKey("sourceContentType")) {
try {
String val = getArguments().getString("sourceContentType");
contentType = val == null ? null : ContentType.valueOf(val);
if (val != null) contentType = ContentType.valueOf(val);
} catch (IllegalArgumentException ignored) {}
}
@@ -1435,8 +1442,8 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
}
private void updateHeaders() {
UiUtils.setExtraTextInfo(getContext(), selfExtraText, null, false, localOnly || statusVisibility==StatusPrivacy.LOCAL, null);
if (replyTo != null) UiUtils.setExtraTextInfo(getContext(), extraText, pronouns, false, replyTo.localOnly || replyTo.visibility==StatusPrivacy.LOCAL, replyTo.account);
UiUtils.setExtraTextInfo(getContext(), selfExtraText, null, false, false, localOnly || statusVisibility==StatusPrivacy.LOCAL, null);
if (replyTo != null) UiUtils.setExtraTextInfo(getContext(), extraText, pronouns, true, false, replyTo.localOnly || replyTo.visibility==StatusPrivacy.LOCAL, replyTo.account);
}
private void buildVisibilityPopup(View v){

View File

@@ -4,6 +4,7 @@ import android.annotation.SuppressLint;
import android.app.Fragment;
import android.app.NotificationManager;
import android.app.assist.AssistContent;
import android.graphics.drawable.RippleDrawable;
import android.os.Build;
import android.os.Bundle;
import android.service.notification.StatusBarNotification;
@@ -126,12 +127,31 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
tabBarWrap=content.findViewById(R.id.tabbar_wrap);
// this one's for the pill haters (https://m3.material.io/components/navigation-bar/overview)
if (GlobalUserPreferences.disableM3PillActiveIndicator) {
for(int i=0; i<tabBar.getChildCount(); i++){
ViewGroup f=(ViewGroup) tabBar.getChildAt(i);
f.setBackgroundResource(R.drawable.bg_tabbar_tab_ripple);
if(GlobalUserPreferences.disableM3PillActiveIndicator){
tabBar.findViewById(R.id.tab_home_pill).setBackground(null);
tabBar.findViewById(R.id.tab_search_pill).setBackground(null);
tabBar.findViewById(R.id.tab_notifications_pill).setBackground(null);
tabBar.findViewById(R.id.tab_profile_pill).setBackgroundResource(R.drawable.bg_tab_profile);
View[] tabs={
tabBar.findViewById(R.id.tab_home),
tabBar.findViewById(R.id.tab_search),
tabBar.findViewById(R.id.tab_notifications),
tabBar.findViewById(R.id.tab_profile)
};
for(View tab : tabs){
tab.setBackgroundResource(R.drawable.bg_tabbar_tab_ripple);
((RippleDrawable) tab.getBackground())
.setRadius(V.dp(GlobalUserPreferences.showNavigationLabels ? 56 : 42));
}
tabBar.findViewById(R.id.tab_profile).setBackgroundResource(R.drawable.bg_tab_profile);
}
if(!GlobalUserPreferences.showNavigationLabels){
tabBar.findViewById(R.id.tab_home_label).setVisibility(View.GONE);
tabBar.findViewById(R.id.tab_search_label).setVisibility(View.GONE);
tabBar.findViewById(R.id.tab_notifications_label).setVisibility(View.GONE);
tabBar.findViewById(R.id.tab_profile_label).setVisibility(View.GONE);
}
tabBarAvatar=tabBar.findViewById(R.id.tab_profile_ava);

View File

@@ -62,6 +62,7 @@ import org.joinmastodon.android.utils.ElevationOnScrollListener;
import org.joinmastodon.android.utils.ProvidesAssistContent;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -336,11 +337,13 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
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);
});
hashtagsItems.entrySet().stream()
.sorted(Comparator.comparing(x -> x.getValue().name, String.CASE_INSENSITIVE_ORDER))
.forEach(entry -> {
MenuItem item = hashtagsMenu.add(Menu.NONE, entry.getKey(), Menu.NONE, entry.getValue().name);
item.setIcon(R.drawable.ic_fluent_number_symbol_24_regular);
UiUtils.insetPopupMenuIcon(ctx, item);
});
}
public void updateToolbarLogo(){

View File

@@ -12,8 +12,8 @@ import android.view.View;
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.session.AccountSession;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.events.PollUpdatedEvent;
import org.joinmastodon.android.events.RemoveAccountPostsEvent;
@@ -22,6 +22,7 @@ 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.EmojiReactionsStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.ExtendedFooterStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.FooterStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.NotificationHeaderStatusDisplayItem;
@@ -99,7 +100,11 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
return items;
}
if(n.status!=null){
int flags=titleItem==null ? 0 : (StatusDisplayItem.FLAG_NO_FOOTER | StatusDisplayItem.FLAG_INSET); // | StatusDisplayItem.FLAG_NO_HEADER);
int flags=titleItem==null ? 0 : (StatusDisplayItem.FLAG_NO_FOOTER | StatusDisplayItem.FLAG_INSET | StatusDisplayItem.FLAG_NO_EMOJI_REACTIONS); // | StatusDisplayItem.FLAG_NO_HEADER);
if (GlobalUserPreferences.spectatorMode)
flags |= StatusDisplayItem.FLAG_NO_FOOTER;
if (!getLocalPrefs().showEmojiReactionsInLists)
flags |= StatusDisplayItem.FLAG_NO_EMOJI_REACTIONS;
ArrayList<StatusDisplayItem> items=StatusDisplayItem.buildItems(this, n.status, accountID, n, knownAccounts, null, flags);
if(titleItem!=null)
items.add(0, titleItem);
@@ -254,6 +259,8 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
footer.rebind();
}else if(holder instanceof ExtendedFooterStatusDisplayItem.Holder footer && footer.getItem().status==n.status.getContentStatus()){
footer.rebind();
}else if(holder instanceof EmojiReactionsStatusDisplayItem.Holder reactions && ev.viewHolder!=holder){
reactions.rebind();
}
}
}

View File

@@ -1,7 +1,5 @@
package org.joinmastodon.android.fragments;
import static org.joinmastodon.android.fragments.ProfileAboutFragment.MAX_FIELDS;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
@@ -175,6 +173,8 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
private MenuItem editSaveMenuItem;
private boolean savingEdits;
private int maxFields = ProfileAboutFragment.MAX_FIELDS;
// from ProfileAboutFragment
public UsableRecyclerView list;
private AboutAdapter adapter;
@@ -200,6 +200,9 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
loaded=true;
if(!isOwnProfile)
loadRelationship();
else if (isInstanceAkkoma()) {
maxFields = getInstance().get().pleroma.metadata.fieldsLimits.maxFields;
}
}else{
profileAccountID=getArguments().getString("profileAccountID");
if(!getArguments().getBoolean("noAutoLoad", false))
@@ -1073,6 +1076,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
);
name.setVisibility(View.GONE);
rolesView.setVisibility(View.GONE);
username.setVisibility(View.GONE);
bio.setVisibility(View.GONE);
countersLayout.setVisibility(View.GONE);
@@ -1121,6 +1125,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
nameEditWrap.setVisibility(View.GONE);
bioEditWrap.setVisibility(View.GONE);
name.setVisibility(View.VISIBLE);
rolesView.setVisibility(View.VISIBLE);
username.setVisibility(View.VISIBLE);
bio.setVisibility(View.VISIBLE);
countersLayout.setVisibility(View.VISIBLE);
@@ -1385,7 +1390,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
public int getItemCount(){
if(isInEditMode){
int size=fields.size();
if(size<MAX_FIELDS)
if(size<maxFields)
size++;
return size;
}
@@ -1510,7 +1515,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
@Override
public void onClick(){
fields.add(new AccountField());
if(fields.size()==MAX_FIELDS){ // replace this row with new row
if(fields.size()==maxFields){ // replace this row with new row
adapter.notifyItemChanged(fields.size()-1);
}else{
adapter.notifyItemInserted(fields.size()-1);

View File

@@ -80,7 +80,7 @@ public class ScheduledStatusListFragment extends BaseStatusListFragment<Schedule
@Override
protected List<StatusDisplayItem> buildDisplayItems(ScheduledStatus s) {
return StatusDisplayItem.buildItems(this, s.toStatus(), accountID, s, knownAccounts, false, false, true, null);
return StatusDisplayItem.buildItems(this, s.toStatus(), accountID, s, knownAccounts, false, false, false, true, null);
}
@Override

View File

@@ -57,7 +57,7 @@ public class StatusEditHistoryFragment extends StatusListFragment{
@Override
protected List<StatusDisplayItem> buildDisplayItems(Status s){
List<StatusDisplayItem> items=StatusDisplayItem.buildItems(this, s, accountID, s, knownAccounts, null, StatusDisplayItem.FLAG_NO_FOOTER | StatusDisplayItem.FLAG_INSET);
List<StatusDisplayItem> items=StatusDisplayItem.buildItems(this, s, accountID, s, knownAccounts, null, StatusDisplayItem.FLAG_NO_FOOTER | StatusDisplayItem.FLAG_INSET | StatusDisplayItem.FLAG_NO_EMOJI_REACTIONS);
int idx=data.indexOf(s);
if(idx>=0){
String date=UiUtils.DATE_TIME_FORMATTER.format(s.createdAt.atZone(ZoneId.systemDefault()));

View File

@@ -16,6 +16,7 @@ import org.joinmastodon.android.events.StatusDeletedEvent;
import org.joinmastodon.android.events.StatusUpdatedEvent;
import org.joinmastodon.android.model.FilterContext;
import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.ui.displayitems.EmojiReactionsStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.ExtendedFooterStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.FooterStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
@@ -33,9 +34,13 @@ public abstract class StatusListFragment extends BaseStatusListFragment<Status>
protected EventListener eventListener=new EventListener();
protected List<StatusDisplayItem> buildDisplayItems(Status s){
boolean addFooter = !GlobalUserPreferences.spectatorMode ||
(this instanceof ThreadFragment t && s.id.equals(t.mainStatus.id));
return StatusDisplayItem.buildItems(this, s, accountID, s, knownAccounts, getFilterContext(), addFooter ? 0 : StatusDisplayItem.FLAG_NO_FOOTER);
boolean isMainThreadStatus = this instanceof ThreadFragment t && s.id.equals(t.mainStatus.id);
int flags = 0;
if (GlobalUserPreferences.spectatorMode)
flags |= StatusDisplayItem.FLAG_NO_FOOTER;
if (!getLocalPrefs().showEmojiReactionsInLists)
flags |= StatusDisplayItem.FLAG_NO_EMOJI_REACTIONS;
return StatusDisplayItem.buildItems(this, s, accountID, s, knownAccounts, getFilterContext(), isMainThreadStatus ? 0 : flags);
}
protected abstract FilterContext getFilterContext();
@@ -218,6 +223,8 @@ public abstract class StatusListFragment extends BaseStatusListFragment<Status>
footer.rebind();
}else if(holder instanceof ExtendedFooterStatusDisplayItem.Holder footer && footer.getItem().status==s.getContentStatus()){
footer.rebind();
}else if(holder instanceof EmojiReactionsStatusDisplayItem.Holder reactions && reactions.getItem().status==s.getContentStatus() && ev.viewHolder!=holder){
reactions.rebind();
}
}
}

View File

@@ -417,6 +417,10 @@ public class ThreadFragment extends StatusListFragment implements ProvidesAssist
adapter.notifyDataSetChanged();
}
public Status getMainStatus(){
return mainStatus;
}
@Override
public boolean isItemEnabled(String id){
return !id.equals(mainStatus.id) || !mainStatus.filterRevealed;

View File

@@ -14,6 +14,7 @@ import org.joinmastodon.android.model.viewmodel.AccountViewModel;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import me.grishka.appkit.api.Callback;
@@ -136,6 +137,10 @@ public abstract class PaginatedAccountListFragment<T> extends BaseAccountListFra
List<AccountViewModel> items = result.stream()
.filter(a -> d.size() > 1000 || d.stream()
.noneMatch(i -> i.account.url.equals(a.url)))
.peek(account ->{
if (account.getDomainFromURL().equals(getRemoteDomain()))
account.acct=account.getFullyQualifiedName();
})
.map(a->new AccountViewModel(a, accountID))
.collect(Collectors.toList());

View File

@@ -0,0 +1,97 @@
package org.joinmastodon.android.fragments.account_list;
import android.net.Uri;
import android.os.Bundle;
import android.text.SpannableStringBuilder;
import android.view.View;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.requests.statuses.PleromaGetStatusReactions;
import org.joinmastodon.android.model.Emoji;
import org.joinmastodon.android.model.EmojiReaction;
import org.joinmastodon.android.model.viewmodel.AccountViewModel;
import org.joinmastodon.android.ui.text.HtmlParser;
import org.joinmastodon.android.ui.utils.UiUtils;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import me.grishka.appkit.api.ErrorResponse;
import me.grishka.appkit.api.SimpleCallback;
public class StatusEmojiReactionsListFragment extends BaseAccountListFragment {
private String id;
private String emojiName;
private String url;
private int count;
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
id = getArguments().getString("statusID");
emojiName = getArguments().getString("emoji");
url = getArguments().getString("url");
count = getArguments().getInt("count");
SpannableStringBuilder title = new SpannableStringBuilder(getResources().getQuantityString(R.plurals.sk_users_reacted_with, count,
count, url == null ? emojiName : ":"+emojiName+":"));
if (url != null) {
Emoji emoji = new Emoji();
emoji.shortcode = emojiName;
emoji.url = url;
HtmlParser.parseCustomEmoji(title, Collections.singletonList(emoji));
}
setTitle(title);
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (url != null) {
UiUtils.loadCustomEmojiInTextView(toolbarTitleView);
}
}
@Override
public void dataLoaded() {
super.dataLoaded();
footerProgress.setVisibility(View.GONE);
}
@Override
protected void doLoadData(int offset, int count){
currentRequest = new PleromaGetStatusReactions(id, emojiName)
.setCallback(new SimpleCallback<>(StatusEmojiReactionsListFragment.this){
@Override
public void onSuccess(List<EmojiReaction> result) {
if (getActivity() == null)
return;
List<AccountViewModel> items = result.get(0).accounts.stream()
.map(a -> new AccountViewModel(a, accountID))
.collect(Collectors.toList());
onDataLoaded(items);
}
@Override
public void onError(ErrorResponse error) {
super.onError(error);
}
})
.exec(accountID);
}
@Override
public void onResume(){
super.onResume();
if(!loaded && !dataLoading)
loadData();
}
@Override
public Uri getWebUri(Uri.Builder base) {
return null;
}
}

View File

@@ -54,6 +54,8 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
private String accountID;
private String currentQuery;
private boolean disableDiscover;
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
@@ -155,6 +157,7 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
}
});
disableDiscover=getArguments().getBoolean("disableDiscover");
searchView=view.findViewById(R.id.search_fragment);
if(searchFragment==null){
searchFragment=new SearchFragment();
@@ -170,8 +173,9 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
searchBack.setOnClickListener(v->{
if(searchActive) exitSearch(); else openSearch();
});
if(searchActive){
searchBack.setImageResource(R.drawable.ic_fluent_arrow_left_24_regular);
if(searchActive) searchBack.setImageResource(R.drawable.ic_fluent_arrow_left_24_regular);
else searchBack.setEnabled(false);
if(searchActive || disableDiscover){
pager.setVisibility(View.GONE);
tabLayout.setVisibility(View.GONE);
searchView.setVisibility(View.VISIBLE);
@@ -211,8 +215,8 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
}
public void loadData(){
if(postsFragment!=null && !postsFragment.loaded && !postsFragment.dataLoading)
postsFragment.loadData();
if(hashtagsFragment!=null && !hashtagsFragment.loaded && !hashtagsFragment.dataLoading)
hashtagsFragment.loadData();
}
private void enterSearch(){
@@ -232,15 +236,18 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
if(!searchActive)
return;
searchActive=false;
pager.setVisibility(View.VISIBLE);
tabLayout.setVisibility(View.VISIBLE);
searchView.setVisibility(View.GONE);
searchText.setText(R.string.sk_search_fediverse);
searchBack.setImageResource(R.drawable.ic_fluent_search_24_regular);
searchBack.setEnabled(false);
searchBack.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
tabsDivider.setVisibility(View.VISIBLE);
currentQuery=null;
searchFragment.clear();
if(disableDiscover) return;
pager.setVisibility(View.VISIBLE);
tabLayout.setVisibility(View.VISIBLE);
searchView.setVisibility(View.GONE);
tabsDivider.setVisibility(View.VISIBLE);
}
private Fragment getFragmentForPage(int page){

View File

@@ -7,6 +7,7 @@ import android.os.Bundle;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import org.joinmastodon.android.GlobalUserPreferences;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.requests.search.GetSearchResults;
import org.joinmastodon.android.api.session.AccountSessionManager;
@@ -54,7 +55,7 @@ public class SearchFragment extends BaseStatusListFragment<SearchResult>{
super.onCreate(savedInstanceState);
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.N)
setRetainInstance(true);
setEmptyText(R.string.no_search_results);
setEmptyText(R.string.sk_recent_searches_placeholder);
loadData();
}
@@ -69,7 +70,7 @@ public class SearchFragment extends BaseStatusListFragment<SearchResult>{
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, FilterContext.PUBLIC, 0);
case STATUS -> StatusDisplayItem.buildItems(this, s.status, accountID, s, knownAccounts, FilterContext.PUBLIC, !getLocalPrefs().showEmojiReactionsInLists ? StatusDisplayItem.FLAG_NO_EMOJI_REACTIONS : 0);
};
}
@@ -173,13 +174,16 @@ public class SearchFragment extends BaseStatusListFragment<SearchResult>{
}
public void setQuery(String q, SearchResult.Type filter){
if(q.isBlank())
if(q.isBlank()) {
setEmptyText(R.string.sk_recent_searches_placeholder);
return;
}
if(currentRequest!=null){
currentRequest.cancel();
currentRequest=null;
}
currentQuery=q;
setEmptyText(R.string.no_search_results);
if(filter==null)
currentFilter=EnumSet.allOf(SearchResult.Type.class);
else
@@ -221,6 +225,13 @@ public class SearchFragment extends BaseStatusListFragment<SearchResult>{
}
}
public void clear() {
data.clear();
preloadedData.clear();
adapter.notifyDataSetChanged();
V.setVisibilityAnimated(content, View.GONE);
}
@Override
public Uri getWebUri(Uri.Builder base) {
Uri.Builder searchUri = base.path("/search");

View File

@@ -102,7 +102,6 @@ public class ReportAddPostsChoiceFragment extends StatusListFragment{
selectedIDs.remove(id);
else
selectedIDs.add(id);
btn.setEnabled(!selectedIDs.isEmpty());
CheckableHeaderStatusDisplayItem.Holder holder=findHolderOfType(id, CheckableHeaderStatusDisplayItem.Holder.class);
if(holder!=null)
holder.rebind();
@@ -112,7 +111,6 @@ public class ReportAddPostsChoiceFragment extends StatusListFragment{
public void onViewCreated(View view, Bundle savedInstanceState){
super.onViewCreated(view, savedInstanceState);
btn=view.findViewById(R.id.btn_next);
btn.setEnabled(!selectedIDs.isEmpty());
btn.setOnClickListener(this::onButtonClick);
buttonBar=view.findViewById(R.id.button_bar);

View File

@@ -30,7 +30,7 @@ public class SettingsBehaviorFragment extends BaseSettingsFragment<Void> impleme
// MEGALODON
private MastodonLanguage.LanguageResolver languageResolver;
private ListItem<Void> prefixRepliesItem, replyVisibilityItem;
private CheckableListItem<Void> forwardReportsItem, remoteLoadingItem, showBoostsItem, showRepliesItem, loadNewPostsItem, seeNewPostsBtnItem;
private CheckableListItem<Void> forwardReportsItem, remoteLoadingItem, showBoostsItem, showRepliesItem, loadNewPostsItem, seeNewPostsBtnItem, overlayMediaItem;
@Override
public void onCreate(Bundle savedInstanceState){
@@ -47,6 +47,7 @@ public class SettingsBehaviorFragment extends BaseSettingsFragment<Void> impleme
languageItem=new ListItem<>(getString(R.string.default_post_language), postLanguage!=null ? postLanguage.getDisplayName(getContext()) : null, R.drawable.ic_fluent_local_language_24_regular, this::onDefaultLanguageClick),
altTextItem=new CheckableListItem<>(R.string.settings_alt_text_reminders, 0, CheckableListItem.Style.SWITCH, GlobalUserPreferences.altTextReminders, R.drawable.ic_fluent_image_alt_text_24_regular, ()->toggleCheckableItem(altTextItem)),
playGifsItem=new CheckableListItem<>(R.string.settings_gif, 0, CheckableListItem.Style.SWITCH, GlobalUserPreferences.playGifs, R.drawable.ic_fluent_gif_24_regular, ()->toggleCheckableItem(playGifsItem)),
overlayMediaItem=new CheckableListItem<>(R.string.sk_settings_continues_playback, R.string.sk_settings_continues_playback_summary, CheckableListItem.Style.SWITCH, GlobalUserPreferences.overlayMedia, R.drawable.ic_fluent_play_circle_hint_24_regular, ()->toggleCheckableItem(overlayMediaItem)),
customTabsItem=new CheckableListItem<>(R.string.settings_custom_tabs, 0, CheckableListItem.Style.SWITCH, GlobalUserPreferences.useCustomTabs, R.drawable.ic_fluent_link_24_regular, ()->toggleCheckableItem(customTabsItem)),
confirmUnfollowItem=new CheckableListItem<>(R.string.settings_confirm_unfollow, 0, CheckableListItem.Style.SWITCH, GlobalUserPreferences.confirmUnfollow, R.drawable.ic_fluent_person_delete_24_regular, ()->toggleCheckableItem(confirmUnfollowItem)),
confirmBoostItem=new CheckableListItem<>(R.string.settings_confirm_boost, 0, CheckableListItem.Style.SWITCH, GlobalUserPreferences.confirmBoost, R.drawable.ic_fluent_arrow_repeat_all_24_regular, ()->toggleCheckableItem(confirmBoostItem)),
@@ -162,6 +163,7 @@ public class SettingsBehaviorFragment extends BaseSettingsFragment<Void> impleme
protected void onHidden(){
super.onHidden();
GlobalUserPreferences.playGifs=playGifsItem.checked;
GlobalUserPreferences.overlayMedia=overlayMediaItem.checked;
GlobalUserPreferences.useCustomTabs=customTabsItem.checked;
GlobalUserPreferences.altTextReminders=altTextItem.checked;
GlobalUserPreferences.confirmUnfollow=customTabsItem.checked;

View File

@@ -37,8 +37,9 @@ public class SettingsDisplayFragment extends BaseSettingsFragment<Void>{
private CheckableListItem<Void> revealCWsItem, hideSensitiveMediaItem, interactionCountsItem, emojiInNamesItem;
// MEGALODON
private CheckableListItem<Void> trueBlackModeItem, marqueeItem, disableSwipeItem, reduceMotionItem, altIndicatorItem, noAltIndicatorItem, collapsePostsItem, spectatorModeItem, hideFabItem, translateOpenedItem, disablePillItem;
private CheckableListItem<Void> trueBlackModeItem, marqueeItem, disableSwipeItem, reduceMotionItem, altIndicatorItem, noAltIndicatorItem, collapsePostsItem, spectatorModeItem, hideFabItem, translateOpenedItem, disablePillItem, showNavigationLabelsItem;
private ListItem<Void> colorItem, publishTextItem, autoRevealCWsItem;
private CheckableListItem<Void> pronounsInUserListingsItem, pronounsInTimelinesItem, pronounsInThreadsItem;
private AccountLocalPreferences lp;
@@ -67,7 +68,11 @@ public class SettingsDisplayFragment extends BaseSettingsFragment<Void>{
spectatorModeItem=new CheckableListItem<>(R.string.sk_settings_hide_interaction, 0, CheckableListItem.Style.SWITCH, GlobalUserPreferences.spectatorMode, R.drawable.ic_fluent_star_off_24_regular, ()->toggleCheckableItem(spectatorModeItem)),
hideFabItem=new CheckableListItem<>(R.string.sk_settings_hide_fab, 0, CheckableListItem.Style.SWITCH, GlobalUserPreferences.autoHideFab, R.drawable.ic_fluent_edit_24_regular, ()->toggleCheckableItem(hideFabItem)),
translateOpenedItem=new CheckableListItem<>(R.string.sk_settings_translate_only_opened, 0, CheckableListItem.Style.SWITCH, GlobalUserPreferences.translateButtonOpenedOnly, R.drawable.ic_fluent_translate_24_regular, ()->toggleCheckableItem(translateOpenedItem)),
disablePillItem=new CheckableListItem<>(R.string.sk_disable_pill_shaped_active_indicator, 0, CheckableListItem.Style.SWITCH, GlobalUserPreferences.disableM3PillActiveIndicator, R.drawable.ic_fluent_pill_24_regular, ()->toggleCheckableItem(disablePillItem))
disablePillItem=new CheckableListItem<>(R.string.sk_disable_pill_shaped_active_indicator, 0, CheckableListItem.Style.SWITCH, GlobalUserPreferences.disableM3PillActiveIndicator, R.drawable.ic_fluent_pill_24_regular, ()->toggleCheckableItem(disablePillItem)),
showNavigationLabelsItem=new CheckableListItem<>(R.string.sk_settings_show_labels_in_navigation_bar, 0, CheckableListItem.Style.SWITCH, GlobalUserPreferences.showNavigationLabels, R.drawable.ic_fluent_tag_24_regular, ()->toggleCheckableItem(showNavigationLabelsItem), true),
pronounsInTimelinesItem=new CheckableListItem<>(R.string.sk_settings_display_pronouns_in_timelines, 0, CheckableListItem.Style.CHECKBOX, GlobalUserPreferences.displayPronounsInTimelines, 0, ()->toggleCheckableItem(pronounsInTimelinesItem)),
pronounsInThreadsItem=new CheckableListItem<>(R.string.sk_settings_display_pronouns_in_threads, 0, CheckableListItem.Style.CHECKBOX, GlobalUserPreferences.displayPronounsInThreads, 0, ()->toggleCheckableItem(pronounsInThreadsItem)),
pronounsInUserListingsItem=new CheckableListItem<>(R.string.sk_settings_display_pronouns_in_user_listings, 0, CheckableListItem.Style.CHECKBOX, GlobalUserPreferences.displayPronounsInUserListings, 0, ()->toggleCheckableItem(pronounsInUserListingsItem))
));
trueBlackModeItem.checkedChangeListener=checked->onTrueBlackModeClick();
}
@@ -90,7 +95,8 @@ public class SettingsDisplayFragment extends BaseSettingsFragment<Void>{
super.onHidden();
boolean restartPlease=
GlobalUserPreferences.disableM3PillActiveIndicator!=disablePillItem.checked;
GlobalUserPreferences.disableM3PillActiveIndicator!=disablePillItem.checked ||
GlobalUserPreferences.showNavigationLabels!=showNavigationLabelsItem.checked;
lp.revealCWs=revealCWsItem.checked;
lp.hideSensitiveMedia=hideSensitiveMediaItem.checked;
@@ -107,6 +113,10 @@ public class SettingsDisplayFragment extends BaseSettingsFragment<Void>{
GlobalUserPreferences.autoHideFab=hideFabItem.checked;
GlobalUserPreferences.translateButtonOpenedOnly=translateOpenedItem.checked;
GlobalUserPreferences.disableM3PillActiveIndicator=disablePillItem.checked;
GlobalUserPreferences.showNavigationLabels=showNavigationLabelsItem.checked;
GlobalUserPreferences.displayPronounsInTimelines=pronounsInTimelinesItem.checked;
GlobalUserPreferences.displayPronounsInThreads=pronounsInThreadsItem.checked;
GlobalUserPreferences.displayPronounsInUserListings=pronounsInUserListingsItem.checked;
GlobalUserPreferences.save();
if(restartPlease) restartActivityToApplyNewTheme();
else E.post(new StatusDisplaySettingsChangedEvent(accountID));

View File

@@ -19,7 +19,7 @@ import java.util.List;
import me.grishka.appkit.Nav;
public class SettingsInstanceFragment extends BaseSettingsFragment<Void> implements HasAccountID{
private CheckableListItem<Void> contentTypesItem, localOnlyItem, glitchModeItem;
private CheckableListItem<Void> contentTypesItem, emojiReactionsItem, emojiReactionsInListsItem, localOnlyItem, glitchModeItem;
private ListItem<Void> defaultContentTypeItem;
private AccountLocalPreferences lp;
@@ -36,11 +36,15 @@ public class SettingsInstanceFragment extends BaseSettingsFragment<Void> impleme
new ListItem<>(R.string.sk_settings_auth, 0, R.drawable.ic_fluent_open_24_regular, ()->UiUtils.launchWebBrowser(getActivity(), "https://"+s.domain+"/auth/edit"), 0, true),
contentTypesItem=new CheckableListItem<>(R.string.sk_settings_content_types, R.string.sk_settings_content_types_explanation, CheckableListItem.Style.SWITCH, lp.contentTypesEnabled, R.drawable.ic_fluent_text_edit_style_24_regular, this::onContentTypeClick),
defaultContentTypeItem=new ListItem<>(R.string.sk_settings_default_content_type, lp.defaultContentType.getName(), R.drawable.ic_fluent_text_bold_24_regular, this::onDefaultContentTypeClick),
emojiReactionsItem=new CheckableListItem<>(R.string.sk_settings_emoji_reactions, R.string.sk_settings_emoji_reactions_explanation, CheckableListItem.Style.SWITCH, lp.emojiReactionsEnabled, R.drawable.ic_fluent_emoji_laugh_24_regular, this::onEmojiReactionsClick),
emojiReactionsInListsItem=new CheckableListItem<>(R.string.sk_settings_emoji_reactions_in_lists, R.string.sk_settings_emoji_reactions_in_lists_explanation, CheckableListItem.Style.SWITCH, lp.showEmojiReactionsInLists, R.drawable.ic_fluent_emoji_24_regular, ()->toggleCheckableItem(emojiReactionsInListsItem)),
localOnlyItem=new CheckableListItem<>(R.string.sk_settings_support_local_only, R.string.sk_settings_local_only_explanation, CheckableListItem.Style.SWITCH, lp.localOnlySupported, R.drawable.ic_fluent_eye_24_regular, this::onLocalOnlyClick),
glitchModeItem=new CheckableListItem<>(R.string.sk_settings_glitch_instance, R.string.sk_settings_glitch_mode_explanation, CheckableListItem.Style.SWITCH, lp.glitchInstance, R.drawable.ic_fluent_eye_24_filled, ()->toggleCheckableItem(glitchModeItem))
));
contentTypesItem.checkedChangeListener=checked->onContentTypeClick();
defaultContentTypeItem.isEnabled=contentTypesItem.checked;
emojiReactionsItem.checkedChangeListener=checked->onEmojiReactionsClick();
emojiReactionsInListsItem.isEnabled=emojiReactionsItem.checked;
localOnlyItem.checkedChangeListener=checked->onLocalOnlyClick();
glitchModeItem.isEnabled=localOnlyItem.checked;
}
@@ -52,6 +56,8 @@ public class SettingsInstanceFragment extends BaseSettingsFragment<Void> impleme
protected void onHidden(){
super.onHidden();
lp.contentTypesEnabled=contentTypesItem.checked;
lp.emojiReactionsEnabled=emojiReactionsItem.checked;
lp.showEmojiReactionsInLists=emojiReactionsInListsItem.checked;
lp.localOnlySupported=localOnlyItem.checked;
lp.glitchInstance=glitchModeItem.checked;
lp.save();
@@ -101,6 +107,13 @@ public class SettingsInstanceFragment extends BaseSettingsFragment<Void> impleme
.show();
}
private void onEmojiReactionsClick(){
toggleCheckableItem(emojiReactionsItem);
emojiReactionsInListsItem.checked=false;
emojiReactionsInListsItem.isEnabled=emojiReactionsItem.checked;
rebindItem(emojiReactionsInListsItem);
}
private void onLocalOnlyClick(){
toggleCheckableItem(localOnlyItem);
glitchModeItem.checked=localOnlyItem.checked && !isInstanceAkkoma();

View File

@@ -16,6 +16,7 @@ import org.joinmastodon.android.R;
import org.joinmastodon.android.api.session.AccountSession;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.events.SelfUpdateStateChangedEvent;
import org.joinmastodon.android.model.Instance;
import org.joinmastodon.android.model.viewmodel.ListItem;
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
import org.joinmastodon.android.ui.utils.HideableSingleViewRecyclerAdapter;
@@ -29,6 +30,7 @@ import me.grishka.appkit.Nav;
import me.grishka.appkit.utils.MergeRecyclerAdapter;
public class SettingsMainFragment extends BaseSettingsFragment<Void>{
private AccountSession account;
private boolean loggedOut;
private HideableSingleViewRecyclerAdapter bannerAdapter;
private Button updateButton1, updateButton2;
@@ -47,23 +49,27 @@ public class SettingsMainFragment extends BaseSettingsFragment<Void>{
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
account = AccountSessionManager.get(accountID);
setTitle(R.string.settings);
setSubtitle(AccountSessionManager.get(accountID).getFullUsername());
setSubtitle(account.getFullUsername());
onDataLoaded(List.of(
new ListItem<>(R.string.settings_behavior, 0, R.drawable.ic_fluent_settings_24_regular, this::onBehaviorClick),
new ListItem<>(R.string.settings_display, 0, R.drawable.ic_fluent_color_24_regular, this::onDisplayClick),
new ListItem<>(R.string.settings_filters, 0, R.drawable.ic_fluent_filter_24_regular, this::onFiltersClick),
new ListItem<>(R.string.settings_notifications, 0, R.drawable.ic_fluent_alert_24_regular, this::onNotificationsClick),
new ListItem<>(R.string.sk_settings_instance, 0, R.drawable.ic_fluent_server_24_regular, this::onInstanceClick),
new ListItem<>(getString(R.string.about_app, getString(R.string.sk_app_name)), null, R.drawable.ic_fluent_info_24_regular, this::onAboutClick, null, 0, true),
new ListItem<>(R.string.log_out, 0, R.drawable.ic_fluent_sign_out_24_regular, this::onLogOutClick, R.attr.colorM3Error, false)
));
Instance instance = AccountSessionManager.getInstance().getInstanceInfo(account.domain);
if (!instance.isAkkoma())
data.add(2, new ListItem<>(R.string.settings_filters, 0, R.drawable.ic_fluent_filter_24_regular, this::onFiltersClick));
if(BuildConfig.DEBUG || BuildConfig.BUILD_TYPE.equals("appcenterPrivateBeta")){
data.add(0, new ListItem<>("Debug settings", null, R.drawable.ic_fluent_wrench_screwdriver_24_regular, ()->Nav.go(getActivity(), SettingsDebugFragment.class, makeFragmentArgs()), null, 0, true));
}
AccountSessionManager.get(accountID).reloadPreferences(null);
account.reloadPreferences(null);
E.register(this);
}
@@ -80,7 +86,7 @@ public class SettingsMainFragment extends BaseSettingsFragment<Void>{
protected void onHidden(){
super.onHidden();
if(!loggedOut)
AccountSessionManager.get(accountID).savePreferencesIfPending();
account.savePreferencesIfPending();
}
@Override
@@ -147,7 +153,7 @@ public class SettingsMainFragment extends BaseSettingsFragment<Void>{
AccountSession session=AccountSessionManager.getInstance().getAccount(accountID);
new M3AlertDialogBuilder(getActivity())
.setMessage(getString(R.string.confirm_log_out, session.getFullUsername()))
.setPositiveButton(R.string.log_out, (dialog, which)->AccountSessionManager.get(accountID).logOut(getActivity(), ()->{
.setPositiveButton(R.string.log_out, (dialog, which)->account.logOut(getActivity(), ()->{
loggedOut=true;
getActivity().finish();
Intent intent=new Intent(getActivity(), MainActivity.class);

View File

@@ -1,5 +1,7 @@
package org.joinmastodon.android.fragments.settings;
import static org.unifiedpush.android.connector.UnifiedPush.getDistributor;
import android.app.AlertDialog;
import android.app.NotificationManager;
import android.content.Intent;
@@ -25,8 +27,11 @@ import org.joinmastodon.android.model.viewmodel.ListItem;
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
import org.joinmastodon.android.ui.utils.HideableSingleViewRecyclerAdapter;
import org.joinmastodon.android.ui.utils.UiUtils;
import org.unifiedpush.android.connector.RegistrationDialogContent;
import org.unifiedpush.android.connector.UnifiedPush;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
@@ -52,7 +57,8 @@ public class SettingsNotificationsFragment extends BaseSettingsFragment<Void>{
private boolean notificationsAllowed=true;
// MEGALODON
private CheckableListItem<Void> uniformIconItem, deleteItem, onlyLatestItem;
private boolean useUnifiedPush = false;
private CheckableListItem<Void> uniformIconItem, deleteItem, onlyLatestItem, unifiedPushItem;
private CheckableListItem<Void> postsItem, updateItem;
private AccountLocalPreferences lp;
@@ -64,6 +70,7 @@ public class SettingsNotificationsFragment extends BaseSettingsFragment<Void>{
lp=AccountSessionManager.get(accountID).getLocalPreferences();
getPushSubscription();
useUnifiedPush=!getDistributor(getContext()).isEmpty();
onDataLoaded(List.of(
pauseItem=new CheckableListItem<>(getString(R.string.pause_all_notifications), getPauseItemSubtitle(), CheckableListItem.Style.SWITCH, false, R.drawable.ic_fluent_alert_snooze_24_regular, ()->onPauseNotificationsClick(false)),
@@ -79,9 +86,16 @@ public class SettingsNotificationsFragment extends BaseSettingsFragment<Void>{
uniformIconItem=new CheckableListItem<>(R.string.sk_settings_uniform_icon_for_notifications, 0, CheckableListItem.Style.SWITCH, GlobalUserPreferences.uniformNotificationIcon, R.drawable.ic_ntf_logo, ()->toggleCheckableItem(uniformIconItem)),
deleteItem=new CheckableListItem<>(R.string.sk_settings_enable_delete_notifications, 0, CheckableListItem.Style.SWITCH, GlobalUserPreferences.enableDeleteNotifications, R.drawable.ic_fluent_mail_inbox_dismiss_24_regular, ()->toggleCheckableItem(deleteItem)),
onlyLatestItem=new CheckableListItem<>(R.string.sk_settings_single_notification, 0, CheckableListItem.Style.SWITCH, lp.keepOnlyLatestNotification, R.drawable.ic_fluent_convert_range_24_regular, ()->toggleCheckableItem(onlyLatestItem), true)
onlyLatestItem=new CheckableListItem<>(R.string.sk_settings_single_notification, 0, CheckableListItem.Style.SWITCH, lp.keepOnlyLatestNotification, R.drawable.ic_fluent_convert_range_24_regular, ()->toggleCheckableItem(onlyLatestItem), true),
unifiedPushItem=new CheckableListItem<>(R.string.sk_settings_unifiedpush, 0, CheckableListItem.Style.SWITCH, useUnifiedPush, R.drawable.ic_fluent_alert_arrow_up_24_regular, this::onUnifiedPush, true)
));
//only enable when distributors, who can receive notifications, are available
unifiedPushItem.isEnabled=!UnifiedPush.getDistributors(getContext(), new ArrayList<>()).isEmpty();
if (!unifiedPushItem.isEnabled) {
unifiedPushItem.subtitleRes=R.string.sk_settings_unifiedpush_no_distributor_body;
}
typeItems=List.of(mentionsItem, boostsItem, favoritesItem, followersItem, pollsItem, updateItem, postsItem);
pauseItem.checkedChangeListener=checked->onPauseNotificationsClick(true);
updatePolicyItem(null);
@@ -312,4 +326,38 @@ public class SettingsNotificationsFragment extends BaseSettingsFragment<Void>{
bannerAdapter.setVisible(false);
}
}
}
private void onUnifiedPush(){
if(getDistributor(getContext()).isEmpty()){
List<String> distributors = UnifiedPush.getDistributors(getContext(), new ArrayList<>());
showUnifiedPushRegisterDialog(distributors);
return;
}
UnifiedPush.unregisterApp(
getContext(),
accountID
);
//re-register to fcm
AccountSessionManager.getInstance().getAccount(accountID).getPushSubscriptionManager().registerAccountForPush(getPushSubscription());
unifiedPushItem.toggle();
rebindItem(unifiedPushItem);
}
private void showUnifiedPushRegisterDialog(List<String> distributors){
new M3AlertDialogBuilder(getContext()).setTitle(R.string.sk_settings_unifiedpush_choose).setItems(distributors.toArray(String[]::new),
(dialog, which) ->{
String userDistrib = distributors.get(which);
UnifiedPush.saveDistributor(getContext(), userDistrib);
UnifiedPush.registerApp(
getContext(),
accountID,
new ArrayList<>(),
getContext().getPackageName()
);
unifiedPushItem.toggle();
rebindItem(unifiedPushItem);
}).show();
}
}

View File

@@ -8,6 +8,7 @@ import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
@@ -24,6 +25,7 @@ import android.widget.Toast;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.MastodonAPIController;
import org.joinmastodon.android.api.requests.instance.GetInstanceExtendedDescription;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.model.Instance;
import org.joinmastodon.android.model.viewmodel.AccountViewModel;
import org.joinmastodon.android.model.viewmodel.ListItem;
@@ -126,6 +128,8 @@ public class SettingsServerAboutFragment extends LoaderFragment{
hlp.leftMargin=hlp.rightMargin=V.dp(16);
scrollingLayout.addView(heading, hlp);
// if a remote instance is shown, the account is remote and need to be loaded accordingly when shown
instance.contactAccount.isRemote=!AccountSessionManager.get(accountID).domain.equals(instance.normalizedUri);
AccountViewModel model=new AccountViewModel(instance.contactAccount, accountID);
AccountViewHolder holder=new AccountViewHolder(this, scrollingLayout, null);
holder.setStyle(AccountViewHolder.AccessoryType.NONE, false);

View File

@@ -1,9 +1,11 @@
package org.joinmastodon.android.model;
import org.joinmastodon.android.api.ObjectValidationException;
import org.joinmastodon.android.api.RequiredField;
import org.parceler.Parcel;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
@Parcel
@@ -20,6 +22,7 @@ public class Announcement extends BaseModel implements DisplayItemsParent {
public Instant updatedAt;
public boolean read;
public List<Emoji> emojis;
public List<EmojiReaction> reactions;
public List<Mention> mentions;
public List<Hashtag> tags;
@@ -41,10 +44,17 @@ public class Announcement extends BaseModel implements DisplayItemsParent {
'}';
}
public Status toStatus() {
Status s = Status.ofFake(id, content, publishedAt);
s.createdAt = startsAt != null ? startsAt : publishedAt;
if (updatedAt != null) s.editedAt = updatedAt;
@Override
public void postprocess() throws ObjectValidationException{
super.postprocess();
if(reactions==null) reactions=new ArrayList<>();
}
public Status toStatus() {
Status s=Status.ofFake(id, content, publishedAt);
s.createdAt=startsAt != null ? startsAt : publishedAt;
s.reactions=reactions;
if(updatedAt != null) s.editedAt=updatedAt;
return s;
}

View File

@@ -3,6 +3,7 @@ package org.joinmastodon.android.model;
import android.graphics.Bitmap;
import android.graphics.PointF;
import android.graphics.drawable.Drawable;
import android.media.MediaMetadataRetriever;
import com.google.gson.annotations.SerializedName;
@@ -45,26 +46,26 @@ public class Attachment extends BaseModel{
public int getWidth(){
if(meta==null)
return 1920;
return 0;
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 1920;
return 0;
}
public int getHeight(){
if(meta==null)
return 1080;
return 0;
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 1080;
return 0;
}
public double getDuration(){
@@ -77,6 +78,13 @@ public class Attachment extends BaseModel{
return 0;
}
public boolean hasSound() {
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
retriever.setDataSource(url);
String hasAudioStr = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_HAS_AUDIO);
return "yes".equals(hasAudioStr);
}
@Override
public void postprocess() throws ObjectValidationException{
super.postprocess();

View File

@@ -0,0 +1,56 @@
package org.joinmastodon.android.model;
import org.parceler.Parcel;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest;
import me.grishka.appkit.utils.V;
@Parcel
public class EmojiReaction {
public List<Account> accounts;
public List<String> accountIds;
public int count;
public boolean me;
public String name;
public String url;
public String staticUrl;
public transient ImageLoaderRequest request;
public static EmojiReaction of(Emoji info, Account me){
EmojiReaction reaction=new EmojiReaction();
reaction.me=true;
reaction.count=1;
reaction.name=info.shortcode;
reaction.url=info.url;
reaction.staticUrl=info.staticUrl;
reaction.accounts=new ArrayList<>(Collections.singleton(me));
reaction.accountIds=new ArrayList<>(Collections.singleton(me.id));
reaction.request=new UrlImageLoaderRequest(info.url, V.sp(24), V.sp(24));
return reaction;
}
public static EmojiReaction of(String emoji, Account me){
EmojiReaction reaction=new EmojiReaction();
reaction.me=true;
reaction.count=1;
reaction.name=emoji;
reaction.accounts=new ArrayList<>(Collections.singleton(me));
reaction.accountIds=new ArrayList<>(Collections.singleton(me.id));
return reaction;
}
public void add(Account self){
if(accounts==null) accounts=new ArrayList<>();
if(accountIds==null) accountIds=new ArrayList<>();
count++;
me=true;
accounts.add(self);
accountIds.add(self.id);
}
}

View File

@@ -131,7 +131,7 @@ public class Instance extends BaseModel{
ci.domain=uri;
ci.normalizedDomain=IDN.toUnicode(uri);
ci.description=Html.fromHtml(shortDescription).toString().trim();
if(languages!=null && languages.size() > 0){
if(languages!=null&&languages.size()>0){
ci.language=languages.get(0);
ci.languages=languages;
}else{

View File

@@ -1,9 +1,12 @@
package org.joinmastodon.android.model;
import android.content.Context;
import com.google.gson.annotations.SerializedName;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.RequiredField;
import org.joinmastodon.android.ui.utils.UiUtils;
import androidx.annotation.StringRes;
@@ -20,6 +23,40 @@ public class PushNotification extends BaseModel{
@RequiredField
public String body;
public static PushNotification fromNotification(Context context, Notification notification){
PushNotification pushNotification = new PushNotification();
pushNotification.notificationType = switch(notification.type) {
case FOLLOW -> PushNotification.Type.FOLLOW;
case MENTION -> PushNotification.Type.MENTION;
case REBLOG -> PushNotification.Type.REBLOG;
case FAVORITE -> PushNotification.Type.FAVORITE;
case POLL -> PushNotification.Type.POLL;
case STATUS -> PushNotification.Type.STATUS;
case UPDATE -> PushNotification.Type.UPDATE;
case SIGN_UP -> PushNotification.Type.SIGN_UP;
case REPORT -> PushNotification.Type.REPORT;
//Follow request, and reactions are not supported by the API
default -> throw new IllegalStateException("Unexpected value: "+notification.type);
};
String notificationTitle = context.getString(switch(notification.type){
case FOLLOW -> R.string.user_followed_you;
case MENTION -> R.string.sk_notification_mention;
case REBLOG -> R.string.notification_boosted;
case FAVORITE -> R.string.user_favorited;
case POLL -> R.string.poll_ended;
case UPDATE -> R.string.sk_post_edited;
case SIGN_UP -> R.string.sk_signed_up;
case REPORT -> R.string.sk_reported;
default -> throw new IllegalStateException("Unexpected value: "+notification.type);
});
pushNotification.title = UiUtils.generateFormattedString(notificationTitle, notification.account.displayName).toString();
pushNotification.icon = notification.status.account.avatarStatic;
pushNotification.body = notification.status.getStrippedText();
return pushNotification;
}
@Override
public String toString(){
return "PushNotification{"+

View File

@@ -23,6 +23,7 @@ import org.parceler.Parcel;
import java.lang.reflect.Type;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
@Parcel
@@ -74,6 +75,9 @@ public class Status extends BaseModel implements DisplayItemsParent, Searchable{
public Status quote; // can be boolean in calckey
public List<EmojiReaction> reactions;
protected List<EmojiReaction> emojiReactions; // akkoma
public transient boolean filterRevealed;
public transient boolean spoilerRevealed;
public transient boolean sensitiveRevealed;
@@ -96,7 +100,7 @@ public class Status extends BaseModel implements DisplayItemsParent, Searchable{
t.postprocess();
for(Emoji e:emojis)
e.postprocess();
if (mediaAttachments == null) mediaAttachments = List.of();
if (mediaAttachments == null) mediaAttachments=List.of();
for(Attachment a:mediaAttachments)
a.postprocess();
account.postprocess();
@@ -110,10 +114,12 @@ public class Status extends BaseModel implements DisplayItemsParent, Searchable{
for(FilterResult fr:filtered)
fr.postprocess();
if (!TextUtils.isEmpty(spoilerText)) sensitive = true;
if(!TextUtils.isEmpty(spoilerText)) sensitive=true;
spoilerRevealed=TextUtils.isEmpty(spoilerText);
sensitiveRevealed=!sensitive;
if (visibility.equals(StatusPrivacy.LOCAL)) localOnly = true;
if(visibility.equals(StatusPrivacy.LOCAL)) localOnly=true;
if(emojiReactions!=null) reactions=emojiReactions;
if(reactions==null) reactions=new ArrayList<>();
}
@Override
@@ -169,6 +175,7 @@ public class Status extends BaseModel implements DisplayItemsParent, Searchable{
reblogged=ev.reblogged;
bookmarked=ev.bookmarked;
pinned=ev.pinned;
reactions=ev.reactions;
}
public Status getContentStatus(){
@@ -194,17 +201,18 @@ public class Status extends BaseModel implements DisplayItemsParent, Searchable{
}
public static Status ofFake(String id, String text, Instant createdAt) {
Status s = new Status();
s.id = id;
s.mediaAttachments = List.of();
s.createdAt = createdAt;
s.content = s.text = text;
s.spoilerText = "";
s.visibility = StatusPrivacy.PUBLIC;
s.mentions = List.of();
s.tags = List.of();
s.emojis = List.of();
s.filtered = List.of();
Status s=new Status();
s.id=id;
s.mediaAttachments=List.of();
s.createdAt=createdAt;
s.content=s.text=text;
s.spoilerText="";
s.visibility=StatusPrivacy.PUBLIC;
s.reactions=List.of();
s.mentions=List.of();
s.tags =List.of();
s.emojis=List.of();
s.filtered=List.of();
return s;
}
@@ -216,21 +224,21 @@ public class Status extends BaseModel implements DisplayItemsParent, Searchable{
public static class StatusDeserializer implements JsonDeserializer<Status> {
@Override
public Status deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
JsonObject obj = json.getAsJsonObject();
JsonObject obj=json.getAsJsonObject();
Status quote = null;
Status quote=null;
if (obj.has("quote") && obj.get("quote").isJsonObject())
quote = gson.fromJson(obj.get("quote"), Status.class);
quote=gson.fromJson(obj.get("quote"), Status.class);
obj.remove("quote");
Status reblog = null;
Status reblog=null;
if (obj.has("reblog"))
reblog = gson.fromJson(obj.get("reblog"), Status.class);
reblog=gson.fromJson(obj.get("reblog"), Status.class);
obj.remove("reblog");
Status status = gsonWithoutDeserializer.fromJson(json, Status.class);
status.quote = quote;
status.reblog = reblog;
Status status=gsonWithoutDeserializer.fromJson(json, Status.class);
status.quote=quote;
status.reblog=reblog;
return status;
}

File diff suppressed because one or more lines are too long

View File

@@ -3,18 +3,17 @@ package org.joinmastodon.android.ui.displayitems;
import android.content.Context;
import android.view.ViewGroup;
import android.widget.Space;
import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
import org.joinmastodon.android.fragments.BaseStatusListFragment;
import me.grishka.appkit.utils.V;
public class InsetDummyStatusDisplayItem extends StatusDisplayItem {
private final boolean addMediaGridMargin;
public class DummyStatusDisplayItem extends StatusDisplayItem {
public InsetDummyStatusDisplayItem(String parentID, BaseStatusListFragment<?> parentFragment, boolean addMediaGridMargin) {
public DummyStatusDisplayItem(String parentID, BaseStatusListFragment<?> parentFragment) {
super(parentID, parentFragment);
this.addMediaGridMargin = addMediaGridMargin;
}
@Override
@@ -22,20 +21,22 @@ public class InsetDummyStatusDisplayItem extends StatusDisplayItem {
return Type.DUMMY;
}
public static class Holder extends StatusDisplayItem.Holder<InsetDummyStatusDisplayItem> {
public static class Holder extends StatusDisplayItem.Holder<DummyStatusDisplayItem> {
private final RecyclerView.LayoutParams params;
public Holder(Context context) {
super(new Space(context));
}
params=new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0);
@Override
public void onBind(InsetDummyStatusDisplayItem item) {
// BetterItemAnimator appears not to handle InsetStatusItemDecoration's getItemOffsets
// correctly, causing removed inset views to jump while animating. i don't quite
// understand it, but this workaround appears to work.
// see InsetStatusItemDecoration#getItemOffsets
ViewGroup.MarginLayoutParams params = new ViewGroup.MarginLayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0);
params.setMargins(0, item.addMediaGridMargin ? V.dp(4) : 0, 0, V.dp(16));
params.setMargins(0, 0, 0, V.dp(16));
itemView.setLayoutParams(params);
}
@Override
public void onBind(DummyStatusDisplayItem item) {}
}
}

View File

@@ -0,0 +1,385 @@
package org.joinmastodon.android.ui.displayitems;
import android.app.Activity;
import android.content.Context;
import android.graphics.Paint;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.util.Pair;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import org.joinmastodon.android.E;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.MastodonAPIRequest;
import org.joinmastodon.android.api.requests.announcements.AddAnnouncementReaction;
import org.joinmastodon.android.api.requests.announcements.DeleteAnnouncementReaction;
import org.joinmastodon.android.api.requests.statuses.AddStatusReaction;
import org.joinmastodon.android.api.requests.statuses.DeleteStatusReaction;
import org.joinmastodon.android.api.requests.statuses.PleromaAddStatusReaction;
import org.joinmastodon.android.api.requests.statuses.PleromaDeleteStatusReaction;
import org.joinmastodon.android.api.session.AccountSession;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.events.StatusCountersUpdatedEvent;
import org.joinmastodon.android.fragments.BaseStatusListFragment;
import org.joinmastodon.android.fragments.account_list.StatusEmojiReactionsListFragment;
import org.joinmastodon.android.model.Account;
import org.joinmastodon.android.model.Announcement;
import org.joinmastodon.android.model.Emoji;
import org.joinmastodon.android.model.EmojiReaction;
import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.ui.CustomEmojiPopupKeyboard;
import org.joinmastodon.android.ui.utils.TextDrawable;
import org.joinmastodon.android.ui.utils.UiUtils;
import org.joinmastodon.android.ui.views.ProgressBarButton;
import me.grishka.appkit.Nav;
import me.grishka.appkit.api.Callback;
import me.grishka.appkit.api.ErrorResponse;
import me.grishka.appkit.imageloader.ImageLoaderRecyclerAdapter;
import me.grishka.appkit.imageloader.ImageLoaderViewHolder;
import me.grishka.appkit.imageloader.ListImageLoaderWrapper;
import me.grishka.appkit.imageloader.RecyclerViewDelegate;
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest;
import me.grishka.appkit.utils.BindableViewHolder;
import me.grishka.appkit.utils.V;
import me.grishka.appkit.views.UsableRecyclerView;
public class EmojiReactionsStatusDisplayItem extends StatusDisplayItem {
public final Status status;
private final Drawable placeholder;
private final boolean hideAdd, forAnnouncement;
private final String accountID;
private boolean hidden;
public EmojiReactionsStatusDisplayItem(String parentID, BaseStatusListFragment<?> parentFragment, Status status, String accountID, boolean hideAdd, boolean forAnnouncement) {
super(parentID, parentFragment);
this.status=status;
this.hideAdd=hideAdd;
this.forAnnouncement=forAnnouncement;
this.accountID=accountID;
placeholder=parentFragment.getContext().getDrawable(R.drawable.image_placeholder).mutate();
placeholder.setBounds(0, 0, V.sp(24), V.sp(24));
updateHidden();
}
@Override
public int getImageCount(){
return (int) status.reactions.stream().filter(r->r.url != null).count();
}
@Override
public ImageLoaderRequest getImageRequest(int index){
return status.reactions.get(index).request;
}
@Override
public Type getType(){
return Type.EMOJI_REACTIONS;
}
public boolean isHidden(){
return hidden;
}
private void updateHidden(){
hidden=status.reactions.isEmpty() && hideAdd;
}
// borrowed from ProfileFragment
private void setActionProgressVisible(Holder.EmojiReactionViewHolder vh, boolean visible){
if(vh==null) return;
vh.progress.setVisibility(visible ? View.VISIBLE : View.GONE);
if(visible)
vh.progress.setIndeterminateTintList(vh.btn.getTextColors());
vh.btn.setClickable(!visible);
}
private MastodonAPIRequest<?> createRequest(String name, int count, boolean delete, Holder.EmojiReactionViewHolder vh, Runnable cb){
setActionProgressVisible(vh, true);
boolean ak=parentFragment.isInstanceAkkoma();
boolean keepSpinning=delete && count == 1;
if(forAnnouncement){
MastodonAPIRequest<Object> req=delete
? new DeleteAnnouncementReaction(status.id, name)
: new AddAnnouncementReaction(status.id, name);
return req.setCallback(new Callback<>(){
@Override
public void onSuccess(Object result){
if(!keepSpinning) setActionProgressVisible(vh, false);
cb.run();
}
@Override
public void onError(ErrorResponse error){
setActionProgressVisible(vh, false);
error.showToast(parentFragment.getContext());
}
});
}else{
MastodonAPIRequest<Status> req=delete
? (ak ? new PleromaDeleteStatusReaction(status.id, name) : new DeleteStatusReaction(status.id, name))
: (ak ? new PleromaAddStatusReaction(status.id, name) : new AddStatusReaction(status.id, name));
return req.setCallback(new Callback<>(){
@Override
public void onSuccess(Status result){
if(!keepSpinning) setActionProgressVisible(vh, false);
cb.run();
}
@Override
public void onError(ErrorResponse error){
setActionProgressVisible(vh, false);
error.showToast(parentFragment.getContext());
}
});
}
}
public static class Holder extends StatusDisplayItem.Holder<EmojiReactionsStatusDisplayItem> implements ImageLoaderViewHolder, CustomEmojiPopupKeyboard.Listener {
private final UsableRecyclerView list;
private final LinearLayout root, line;
private CustomEmojiPopupKeyboard emojiKeyboard;
private final View space;
private final ImageButton addButton;
private final EmojiReactionsAdapter adapter;
private final ListImageLoaderWrapper imgLoader;
public Holder(Activity activity, ViewGroup parent) {
super(activity, R.layout.display_item_emoji_reactions, parent);
root=(LinearLayout) itemView;
line=findViewById(R.id.line);
list=findViewById(R.id.list);
imgLoader=new ListImageLoaderWrapper(activity, list, new RecyclerViewDelegate(list), null);
list.setAdapter(adapter=new EmojiReactionsAdapter(this, imgLoader));
addButton=findViewById(R.id.add_btn);
addButton.setOnClickListener(this::onReactClick);
space=findViewById(R.id.space);
list.setLayoutManager(new LinearLayoutManager(activity, LinearLayoutManager.HORIZONTAL, false));
}
@Override
public void onBind(EmojiReactionsStatusDisplayItem item) {
if(emojiKeyboard != null) root.removeView(emojiKeyboard.getView());
AccountSession session=item.parentFragment.getSession();
item.status.reactions.forEach(r->
r.request=r.url != null ? new UrlImageLoaderRequest(r.url, V.sp(24), V.sp(24)) : null);
emojiKeyboard=new CustomEmojiPopupKeyboard(
(Activity) item.parentFragment.getContext(),
AccountSessionManager.getInstance().getCustomEmojis(session.domain),
session.domain, true);
emojiKeyboard.setListener(this);
space.setVisibility(View.GONE);
root.addView(emojiKeyboard.getView());
item.updateHidden();
root.setVisibility(item.hidden ? View.GONE : View.VISIBLE);
line.setVisibility(item.hidden ? View.GONE : View.VISIBLE);
line.setPadding(
list.getPaddingLeft(),
item.hidden ? 0 : V.dp(8),
list.getPaddingRight(),
item.forAnnouncement ? V.dp(8) : 0
);
imgLoader.updateImages();
adapter.notifyDataSetChanged();
}
private void hideEmojiKeyboard(){
space.setVisibility(View.GONE);
addButton.setSelected(false);
if(emojiKeyboard.isVisible()) emojiKeyboard.toggleKeyboardPopup(null);
}
@Override
public void onEmojiSelected(Emoji emoji) {
addEmojiReaction(emoji.shortcode, emoji);
hideEmojiKeyboard();
}
@Override
public void onEmojiSelected(String emoji){
addEmojiReaction(emoji, null);
hideEmojiKeyboard();
}
private void addEmojiReaction(String emoji, Emoji info) {
if(item.status.reactions.stream().filter(r->r.name.equals(emoji) && r.me).findAny().isPresent()) return;
Account me=AccountSessionManager.get(item.accountID).self;
EmojiReaction existing=null;
for(int i=0; i<item.status.reactions.size(); i++){
EmojiReaction r=item.status.reactions.get(i);
if(r.name.equals(emoji)){
existing=r;
r.add(me);
adapter.notifyItemChanged(i);
break;
}
}
if(existing==null){
item.status.reactions.add(0, info!=null ? EmojiReaction.of(info, me) : EmojiReaction.of(emoji, me));
adapter.notifyItemRangeInserted(0, 1);
}
E.post(new StatusCountersUpdatedEvent(item.status, adapter.parentHolder));
item.createRequest(emoji, existing==null ? 1 : existing.count, false, null, ()->{}).exec(item.accountID);
}
@Override
public void onBackspace() {}
private void onReactClick(View v){
emojiKeyboard.toggleKeyboardPopup(null);
v.setSelected(emojiKeyboard.isVisible());
space.setVisibility(emojiKeyboard.isVisible() ? View.VISIBLE : View.GONE);
DisplayMetrics displayMetrics = new DisplayMetrics();
int[] locationOnScreen = new int[2];
((Activity) v.getContext()).getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
v.getLocationOnScreen(locationOnScreen);
double fromScreenTop = (double) locationOnScreen[1] / displayMetrics.heightPixels;
if (fromScreenTop > 0.75) {
item.parentFragment.scrollBy(0, (int) (displayMetrics.heightPixels * 0.3));
}
}
@Override
public void setImage(int index, Drawable image){
View child=list.getChildAt(index);
if(child==null) return;
((EmojiReactionViewHolder) list.getChildViewHolder(child)).setImage(index, image);
}
@Override
public void clearImage(int index){
if(item.status.reactions.get(index).url==null) return;
setImage(index, item.placeholder);
}
private class EmojiReactionsAdapter extends UsableRecyclerView.Adapter<EmojiReactionViewHolder> implements ImageLoaderRecyclerAdapter{
ListImageLoaderWrapper imgLoader;
Holder parentHolder;
public EmojiReactionsAdapter(Holder parentHolder, ListImageLoaderWrapper imgLoader){
super(imgLoader);
this.parentHolder=parentHolder;
this.imgLoader=imgLoader;
}
@NonNull
@Override
public EmojiReactionViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
return new EmojiReactionViewHolder(parent.getContext(), list);
}
@Override
public void onBindViewHolder(EmojiReactionViewHolder holder, int position){
holder.bind(Pair.create(item, item.status.reactions.get(position)));
super.onBindViewHolder(holder, position);
}
@Override
public int getItemCount(){
return item.status.reactions.size();
}
@Override
public int getImageCountForItem(int position){
return item.status.reactions.get(position).url == null ? 0 : 1;
}
@Override
public ImageLoaderRequest getImageRequest(int position, int image){
return item.status.reactions.get(position).request;
}
}
private static class EmojiReactionViewHolder extends BindableViewHolder<Pair<EmojiReactionsStatusDisplayItem, EmojiReaction>> implements ImageLoaderViewHolder{
private final ProgressBarButton btn;
private final ProgressBar progress;
public EmojiReactionViewHolder(Context context, RecyclerView list){
super(context, R.layout.item_emoji_reaction, list);
btn=findViewById(R.id.btn);
progress=findViewById(R.id.progress);
}
@Override
public void setImage(int index, Drawable drawable){
drawable.setBounds(0, 0, V.sp(24), V.sp(24));
btn.setCompoundDrawablesRelative(drawable, null, null, null);
if(drawable instanceof Animatable) ((Animatable) drawable).start();
}
@Override
public void clearImage(int index){
setImage(index, item.first.placeholder);
}
@Override
public void onBind(Pair<EmojiReactionsStatusDisplayItem, EmojiReaction> item){
item.first.setActionProgressVisible(this, false);
EmojiReactionsStatusDisplayItem parent=item.first;
EmojiReaction reaction=item.second;
btn.setText(UiUtils.abbreviateNumber(reaction.count));
btn.setContentDescription(reaction.name);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) btn.setTooltipText(reaction.name);
if(reaction.url==null){
Paint p=new Paint();
p.setTextSize(V.sp(18));
TextDrawable drawable=new TextDrawable(p, reaction.name);
btn.setCompoundDrawablesRelative(drawable, null, null, null);
}else{
btn.setCompoundDrawablesRelative(item.first.placeholder, null, null, null);
}
btn.setSelected(reaction.me);
btn.setOnClickListener(e->{
boolean deleting=reaction.me;
parent.createRequest(reaction.name, reaction.count, deleting, this, ()->{
EmojiReactionsAdapter adapter = (EmojiReactionsAdapter) getBindingAdapter();
for(int i=0; i<parent.status.reactions.size(); i++){
EmojiReaction r=parent.status.reactions.get(i);
if(!r.name.equals(reaction.name)) continue;
if(deleting && r.count==1) {
parent.status.reactions.remove(i);
adapter.notifyItemRemoved(i);
break;
}
r.me=!deleting;
if(deleting) r.count--;
else r.count++;
adapter.notifyItemChanged(i);
break;
}
E.post(new StatusCountersUpdatedEvent(parent.status, adapter.parentHolder));
adapter.parentHolder.imgLoader.updateImages();
}).exec(parent.parentFragment.getAccountID());
});
if (parent.parentFragment.isInstanceAkkoma()) {
// glitch-soc doesn't have this, afaik
btn.setOnLongClickListener(e->{
EmojiReaction emojiReaction=parent.status.reactions.get(getAbsoluteAdapterPosition());
Bundle args=new Bundle();
args.putString("account", parent.parentFragment.getAccountID());
args.putString("statusID", parent.status.id);
int atSymbolIndex = emojiReaction.name.indexOf("@");
args.putString("emoji", atSymbolIndex != -1 ? emojiReaction.name.substring(0, atSymbolIndex) : emojiReaction.name);
args.putString("url", emojiReaction.url);
args.putInt("count", emojiReaction.count);
Nav.go(parent.parentFragment.getActivity(), StatusEmojiReactionsListFragment.class, args);
return true;
});
}
}
}
}
}

View File

@@ -77,18 +77,21 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
}
};
private static final float ALPHA_PRESSED=0.55f;
static {
opacityOut = new AlphaAnimation(1, 0.55f);
opacityOut = new AlphaAnimation(1, ALPHA_PRESSED);
opacityOut.setDuration(300);
opacityOut.setInterpolator(CubicBezierInterpolator.DEFAULT);
opacityOut.setFillAfter(true);
opacityIn = new AlphaAnimation(0.55f, 1);
opacityIn = new AlphaAnimation(ALPHA_PRESSED, 1);
opacityIn.setDuration(400);
opacityIn.setInterpolator(CubicBezierInterpolator.DEFAULT);
}
public Holder(Activity activity, ViewGroup parent){
super(activity, R.layout.display_item_footer, parent);
replies=findViewById(R.id.reply);
boosts=findViewById(R.id.boost);
favorites=findViewById(R.id.favorite);

View File

@@ -171,7 +171,7 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
optionsMenu=new PopupMenu(activity, more);
optionsMenu.inflate(R.menu.post);
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.P)
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.P && !UiUtils.isEMUI())
optionsMenu.getMenu().setGroupDividerEnabled(true);
optionsMenu.setOnMenuItemClickListener(menuItem->{
Account account=item.user;
@@ -337,7 +337,8 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
itemView.setPadding(itemView.getPaddingLeft(), itemView.getPaddingTop(), itemView.getPaddingRight(), item.needBottomPadding ? V.dp(16) : 0);
if(TextUtils.isEmpty(item.extraText)){
if (item.status != null) {
UiUtils.setExtraTextInfo(item.parentFragment.getContext(), extraText, pronouns, item.status.visibility==StatusPrivacy.DIRECT, item.status.localOnly || item.status.visibility==StatusPrivacy.LOCAL, item.status.account);
boolean displayPronouns=item.parentFragment instanceof ThreadFragment ? GlobalUserPreferences.displayPronounsInThreads : GlobalUserPreferences.displayPronounsInTimelines;
UiUtils.setExtraTextInfo(item.parentFragment.getContext(), extraText, pronouns, displayPronouns, item.status.visibility==StatusPrivacy.DIRECT, item.status.localOnly || item.status.visibility==StatusPrivacy.LOCAL, item.status.account);
}
}else{
extraText.setVisibility(View.VISIBLE);

View File

@@ -242,6 +242,7 @@ public class MediaGridStatusDisplayItem extends StatusDisplayItem{
if(altTextAnimator!=null)
altTextAnimator.cancel();
// V.setVisibilityAnimated(hideSensitiveButton, View.GONE);
V.cancelVisibilityAnimation(altTextWrapper);
v.setVisibility(View.INVISIBLE);
int index=(Integer)v.getTag();
altTextIndex=index;
@@ -254,7 +255,7 @@ public class MediaGridStatusDisplayItem extends StatusDisplayItem{
noAltText.setVisibility(!hasAltText && showNoAltIndicator ? View.VISIBLE : View.GONE);
altText.setText(att.description);
altTextWrapper.setVisibility(View.VISIBLE);
altTextWrapper.setBackgroundResource(hasAltText ? R.drawable.bg_image_alt_overlay : R.drawable.bg_image_no_alt_overlay);
altTextWrapper.setBackgroundResource(hasAltText ? R.drawable.bg_image_alt_text_overlay : R.drawable.bg_image_no_alt_overlay);
altTextWrapper.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener(){
@Override
public boolean onPreDraw(){
@@ -317,6 +318,7 @@ public class MediaGridStatusDisplayItem extends StatusDisplayItem{
altTextAnimator.cancel();
// V.setVisibilityAnimated(hideSensitiveButton, item.status.sensitive ? View.VISIBLE : View.GONE);
V.cancelVisibilityAnimation(altTextWrapper);
View btn=controllers.get(altTextIndex).btnsWrap;
int i=0;
for(MediaAttachmentViewController c:controllers){
@@ -365,8 +367,8 @@ public class MediaGridStatusDisplayItem extends StatusDisplayItem{
@Override
public void onAnimationEnd(Animator animation){
altTextAnimator=null;
altTextWrapper.setVisibility(View.GONE);
btn.setVisibility(View.VISIBLE);
V.setVisibilityAnimated(altTextWrapper, View.GONE);
V.setVisibilityAnimated(btn, View.VISIBLE);
btn.setAlpha(1);
}
});

View File

@@ -40,7 +40,7 @@ public class ReblogOrReplyLineStatusDisplayItem extends StatusDisplayItem{
private int iconEnd;
private CustomEmojiHelper emojiHelper=new CustomEmojiHelper(), fullTextEmojiHelper;
private View.OnClickListener handleClick;
boolean belowHeader, needBottomPadding;
public boolean needBottomPadding;
ReblogOrReplyLineStatusDisplayItem extra;
CharSequence fullText;
@@ -131,7 +131,6 @@ public class ReblogOrReplyLineStatusDisplayItem extends StatusDisplayItem{
if (visibilityText != 0) text.setContentDescription(item.text + " (" + ctx.getString(visibilityText) + ")");
if(Build.VERSION.SDK_INT<Build.VERSION_CODES.N)
UiUtils.fixCompoundDrawableTintOnAndroid6(text);
text.setTextAppearance(item.belowHeader ? R.style.m3_label_large : R.style.m3_title_small);
text.setCompoundDrawableTintList(text.getTextColors());
}
@@ -141,10 +140,6 @@ public class ReblogOrReplyLineStatusDisplayItem extends StatusDisplayItem{
if (item.extra != null) bindLine(item.extra, extraText);
extraText.setVisibility(item.extra == null ? View.GONE : View.VISIBLE);
separator.setVisibility(item.extra == null ? View.GONE : View.VISIBLE);
ViewGroup.MarginLayoutParams params = new ViewGroup.MarginLayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
params.bottomMargin = item.belowHeader ? V.dp(-6) : V.dp(-12);
params.topMargin = item.belowHeader ? V.dp(-6) : 0;
itemView.setLayoutParams(params);
itemView.setPadding(itemView.getPaddingLeft(), itemView.getPaddingTop(), itemView.getPaddingRight(), item.needBottomPadding ? V.dp(16) : 0);
layoutLine();
}

View File

@@ -92,7 +92,7 @@ public class SpoilerStatusDisplayItem extends StatusDisplayItem{
itemView.getPaddingLeft(),
itemView.getPaddingTop(),
itemView.getPaddingRight(),
item.inset || GlobalUserPreferences.spectatorMode ? itemView.getPaddingTop() : 0
item.inset ? itemView.getPaddingTop() : 0
);
}

View File

@@ -3,6 +3,7 @@ package org.joinmastodon.android.ui.displayitems;
import android.app.Activity;
import android.app.Fragment;
import android.content.Context;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
@@ -17,6 +18,7 @@ import org.joinmastodon.android.fragments.HashtagTimelineFragment;
import org.joinmastodon.android.fragments.HomeTabFragment;
import org.joinmastodon.android.fragments.ListTimelineFragment;
import org.joinmastodon.android.fragments.ProfileFragment;
import org.joinmastodon.android.fragments.ScheduledStatusListFragment;
import org.joinmastodon.android.fragments.ThreadFragment;
import org.joinmastodon.android.model.Account;
import org.joinmastodon.android.model.Attachment;
@@ -25,12 +27,12 @@ import org.joinmastodon.android.model.LegacyFilter;
import org.joinmastodon.android.model.FilterAction;
import org.joinmastodon.android.model.FilterContext;
import org.joinmastodon.android.model.FilterResult;
import org.joinmastodon.android.model.Notification;
import org.joinmastodon.android.model.Poll;
import org.joinmastodon.android.model.ScheduledStatus;
import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.ui.PhotoLayoutHelper;
import org.joinmastodon.android.ui.text.HtmlParser;
import org.joinmastodon.android.ui.utils.UiUtils;
import org.joinmastodon.android.ui.viewholders.AccountViewHolder;
import org.joinmastodon.android.utils.StatusFilterPredicate;
import org.parceler.Parcels;
@@ -48,7 +50,7 @@ import me.grishka.appkit.views.UsableRecyclerView;
public abstract class StatusDisplayItem{
public final String parentID;
public final BaseStatusListFragment parentFragment;
public final BaseStatusListFragment<?> parentFragment;
public boolean inset;
public int index;
public boolean
@@ -63,6 +65,7 @@ public abstract class StatusDisplayItem{
public static final int FLAG_MEDIA_FORCE_HIDDEN=1 << 3;
public static final int FLAG_NO_HEADER=1 << 4;
public static final int FLAG_NO_TRANSLATE=1 << 5;
public static final int FLAG_NO_EMOJI_REACTIONS=1 << 6;
public void setAncestryInfo(
boolean hasDescendantNeighbor,
@@ -76,7 +79,7 @@ public abstract class StatusDisplayItem{
this.isDirectDescendant = isDirectDescendant;
}
public StatusDisplayItem(String parentID, BaseStatusListFragment parentFragment){
public StatusDisplayItem(String parentID, BaseStatusListFragment<?> parentFragment){
this.parentID=parentID;
this.parentFragment=parentFragment;
}
@@ -101,6 +104,7 @@ public abstract class StatusDisplayItem{
case POLL_OPTION -> new PollOptionStatusDisplayItem.Holder(activity, parent);
case POLL_FOOTER -> new PollFooterStatusDisplayItem.Holder(activity, parent);
case CARD -> new LinkCardStatusDisplayItem.Holder(activity, parent);
case EMOJI_REACTIONS -> new EmojiReactionsStatusDisplayItem.Holder(activity, parent);
case FOOTER -> new FooterStatusDisplayItem.Holder(activity, parent);
case ACCOUNT_CARD -> new AccountCardStatusDisplayItem.Holder(activity, parent);
case ACCOUNT -> new AccountStatusDisplayItem.Holder(new AccountViewHolder(parentFragment, parent, null));
@@ -113,11 +117,11 @@ public abstract class StatusDisplayItem{
case SPOILER, FILTER_SPOILER -> new SpoilerStatusDisplayItem.Holder(activity, parent, type);
case SECTION_HEADER -> null; // new SectionHeaderStatusDisplayItem.Holder(activity, parent);
case NOTIFICATION_HEADER -> new NotificationHeaderStatusDisplayItem.Holder(activity, parent);
case DUMMY -> new InsetDummyStatusDisplayItem.Holder(activity);
case DUMMY -> new DummyStatusDisplayItem.Holder(activity);
};
}
public static ArrayList<StatusDisplayItem> buildItems(BaseStatusListFragment<?> fragment, Status status, String accountID, DisplayItemsParent parentObject, Map<String, Account> knownAccounts, boolean inset, boolean addFooter, boolean disableTranslate, FilterContext filterContext) {
public static ArrayList<StatusDisplayItem> buildItems(BaseStatusListFragment<?> fragment, Status status, String accountID, DisplayItemsParent parentObject, Map<String, Account> knownAccounts, boolean inset, boolean showReactions, boolean addFooter, boolean disableTranslate, FilterContext filterContext) {
int flags=0;
if(inset)
flags|=FLAG_INSET;
@@ -125,6 +129,8 @@ public abstract class StatusDisplayItem{
flags|=FLAG_NO_FOOTER;
if (disableTranslate)
flags|=FLAG_NO_TRANSLATE;
if (!showReactions)
flags|=FLAG_NO_EMOJI_REACTIONS;
return buildItems(fragment, status, accountID, parentObject, knownAccounts, filterContext, flags);
}
@@ -203,7 +209,7 @@ public abstract class StatusDisplayItem{
items.add(replyLine);
}
}
if((flags & FLAG_CHECKABLE)!=0)
items.add(header=new CheckableHeaderStatusDisplayItem(parentID, statusForContent.account, statusForContent.createdAt, fragment, accountID, statusForContent, null));
else
@@ -240,17 +246,26 @@ public abstract class StatusDisplayItem{
}
}
boolean hasSpoiler=!TextUtils.isEmpty(statusForContent.spoilerText);
if(!TextUtils.isEmpty(statusForContent.content)){
SpannableStringBuilder parsedText=HtmlParser.parse(statusForContent.content, statusForContent.emojis, statusForContent.mentions, statusForContent.tags, accountID);
HtmlParser.applyFilterHighlights(fragment.getActivity(), parsedText, status.filtered);
TextStatusDisplayItem text=new TextStatusDisplayItem(parentID, HtmlParser.parse(statusForContent.content, statusForContent.emojis, statusForContent.mentions, statusForContent.tags, accountID), fragment, statusForContent, (flags & FLAG_NO_TRANSLATE) != 0);
contentItems.add(text);
} else if (header!=null){
}else if(!hasSpoiler && header!=null){
header.needBottomPadding=true;
}else if(hasSpoiler){
contentItems.add(new DummyStatusDisplayItem(parentID, fragment));
}
List<Attachment> imageAttachments=statusForContent.mediaAttachments.stream().filter(att->att.type.isImage()).collect(Collectors.toList());
if(!imageAttachments.isEmpty()){
int color = UiUtils.getThemeColor(fragment.getContext(), R.attr.colorM3SurfaceVariant);
for (Attachment att : imageAttachments) {
if (att.blurhashPlaceholder == null) {
att.blurhashPlaceholder = new ColorDrawable(color);
}
}
PhotoLayoutHelper.TiledLayoutResult layout=PhotoLayoutHelper.processThumbs(imageAttachments);
MediaGridStatusDisplayItem mediaGrid=new MediaGridStatusDisplayItem(parentID, fragment, layout, imageAttachments, statusForContent);
if((flags & FLAG_MEDIA_FORCE_HIDDEN)!=0)
@@ -276,8 +291,14 @@ public abstract class StatusDisplayItem{
if(contentItems!=items && statusForContent.spoilerRevealed){
items.addAll(contentItems);
}
if((flags & FLAG_NO_EMOJI_REACTIONS)==0
&& AccountSessionManager.get(accountID).getLocalPreferences().emojiReactionsEnabled){
boolean isMainStatus=fragment instanceof ThreadFragment t && t.getMainStatus().id.equals(statusForContent.id);
items.add(new EmojiReactionsStatusDisplayItem(parentID, fragment, statusForContent, accountID, !isMainStatus, false));
}
FooterStatusDisplayItem footer=null;
if((flags & FLAG_NO_FOOTER)==0){
FooterStatusDisplayItem footer=new FooterStatusDisplayItem(parentID, fragment, statusForContent, accountID);
footer=new FooterStatusDisplayItem(parentID, fragment, statusForContent, accountID);
footer.hideCounts=hideCounts;
items.add(footer);
if(status.hasGapAfter && !(fragment instanceof ThreadFragment))
@@ -286,10 +307,12 @@ public abstract class StatusDisplayItem{
int i=1;
boolean inset=(flags & FLAG_INSET)!=0;
// add inset dummy so last content item doesn't clip out of inset bounds
if (inset) {
items.add(new InsetDummyStatusDisplayItem(parentID, fragment,
!contentItems.isEmpty() && contentItems
.get(contentItems.size() - 1) instanceof MediaGridStatusDisplayItem));
if(inset || footer==null){
items.add(new DummyStatusDisplayItem(parentID, fragment));
// in case we ever need the dummy to display a margin for the media grid again:
// (i forgot why we apparently don't need this anymore)
// !contentItems.isEmpty() && contentItems
// .get(contentItems.size() - 1) instanceof MediaGridStatusDisplayItem));
}
for(StatusDisplayItem item:items){
item.inset=inset;
@@ -330,6 +353,7 @@ public abstract class StatusDisplayItem{
POLL_OPTION,
POLL_FOOTER,
CARD,
EMOJI_REACTIONS,
FOOTER,
ACCOUNT_CARD,
ACCOUNT,

View File

@@ -21,6 +21,7 @@ import org.joinmastodon.android.api.requests.statuses.TranslateStatus;
import org.joinmastodon.android.api.session.AccountSession;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.fragments.BaseStatusListFragment;
import org.joinmastodon.android.fragments.ThreadFragment;
import org.joinmastodon.android.model.Instance;
import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.model.StatusPrivacy;
@@ -31,6 +32,8 @@ import org.joinmastodon.android.ui.utils.UiUtils;
import org.joinmastodon.android.ui.views.LinkedTextView;
import org.joinmastodon.android.utils.StatusTextEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.regex.Pattern;
@@ -194,12 +197,23 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
readMore.setText(item.status.textExpanded ? R.string.sk_collapse : R.string.sk_expand);
// remove additional padding when (transparently padded) translate button is visible
int nextPos = getAbsoluteAdapterPosition() + 1;
boolean nextIsFooter = item.parentFragment.getDisplayItems().size() > nextPos &&
item.parentFragment.getDisplayItems().get(nextPos) instanceof FooterStatusDisplayItem;
int bottomPadding = (translateVisible && nextIsFooter) ? 0
: nextIsFooter ? V.dp(6)
: V.dp(12);
int nextPos=getAbsoluteAdapterPosition() + 1;
int bottomPadding=V.dp(12);
List<StatusDisplayItem> displayItems=item.parentFragment.getDisplayItems();
if(displayItems.size() > nextPos){
StatusDisplayItem next=displayItems.get(nextPos);
if(next instanceof EmojiReactionsStatusDisplayItem e && e.isHidden()){
next=displayItems.size() > ++nextPos ? displayItems.get(nextPos) : null;
}
if(next instanceof FooterStatusDisplayItem){
bottomPadding=V.dp(6);
// why does java code always end up looking like this
} else if((!item.inset && next instanceof DummyStatusDisplayItem) ||
next instanceof EmojiReactionsStatusDisplayItem e && !e.isHidden()){
bottomPadding=0;
}
}
itemView.setPadding(itemView.getPaddingLeft(), itemView.getPaddingTop(), itemView.getPaddingRight(), bottomPadding);
if (!GlobalUserPreferences.collapseLongPosts) {
@@ -235,7 +249,7 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
// compensate for spoiler's bottom margin
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) itemView.getLayoutParams();
params.setMargins(params.leftMargin, (item.inset || GlobalUserPreferences.spectatorMode) && hasSpoiler ? V.dp(-16) : 0,
params.setMargins(params.leftMargin, item.inset && hasSpoiler ? V.dp(-16) : 0,
params.rightMargin, params.bottomMargin);
}

View File

@@ -99,11 +99,15 @@ public class BlurhashCrossfadeDrawable extends Drawable{
@Override
public int getIntrinsicWidth(){
if(width==0)
return imageDrawable==null ? 1920 : imageDrawable.getIntrinsicWidth();
return width;
}
@Override
public int getIntrinsicHeight(){
if(height==0)
return imageDrawable==null ? 1080 : imageDrawable.getIntrinsicHeight();
return height;
}

View File

@@ -45,6 +45,7 @@ import android.widget.TextView;
import android.widget.Toast;
import android.widget.Toolbar;
import org.joinmastodon.android.GlobalUserPreferences;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.MastodonAPIController;
import org.joinmastodon.android.model.Attachment;
@@ -418,7 +419,8 @@ public class PhotoViewer implements ZoomPanView.Listener{
WindowManager.LayoutParams wlp=(WindowManager.LayoutParams) windowView.getLayoutParams();
wlp.flags|=WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
wm.updateViewLayout(windowView, wlp);
activity.getSystemService(AudioManager.class).requestAudioFocus(audioFocusListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
int audiofocus = GlobalUserPreferences.overlayMedia ? AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK : AudioManager.AUDIOFOCUS_GAIN;
activity.getSystemService(AudioManager.class).requestAudioFocus(audioFocusListener, AudioManager.STREAM_MUSIC, audiofocus);
}
screenOnRefCount++;
}

View File

@@ -27,7 +27,7 @@ public class InsetStatusItemDecoration extends RecyclerView.ItemDecoration{
public InsetStatusItemDecoration(BaseStatusListFragment<?> listFragment){
this.listFragment=listFragment;
bgColor=UiUtils.getThemeColor(listFragment.getActivity(), R.attr.colorM3SurfaceVariant);
bgColor=UiUtils.getThemeColor(listFragment.getActivity(), R.attr.colorM3Surface);
borderColor=UiUtils.getThemeColor(listFragment.getActivity(), R.attr.colorM3OutlineVariant);
}
@@ -65,13 +65,13 @@ public class InsetStatusItemDecoration extends RecyclerView.ItemDecoration{
paint.setColor(bgColor);
rect.left=V.dp(12);
rect.right=list.getWidth()-V.dp(12);
rect.inset(V.dp(4), V.dp(4));
c.drawRoundRect(rect, V.dp(4), V.dp(4), paint);
rect.inset(V.dp(4), V.dp(0));
c.drawRoundRect(rect, V.dp(12), V.dp(12), paint);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(V.dp(1));
paint.setColor(borderColor);
rect.inset(paint.getStrokeWidth()/2f, paint.getStrokeWidth()/2f);
c.drawRoundRect(rect, V.dp(4), V.dp(4), paint);
c.drawRoundRect(rect, V.dp(12), V.dp(12), paint);
}
@Override
@@ -85,10 +85,10 @@ public class InsetStatusItemDecoration extends RecyclerView.ItemDecoration{
boolean topSiblingInset=pos>0 && displayItems.get(pos-1).inset;
boolean bottomSiblingInset=pos<displayItems.size()-1 && displayItems.get(pos+1).inset;
int pad;
if(holder instanceof MediaGridStatusDisplayItem.Holder || holder instanceof LinkCardStatusDisplayItem.Holder)
// if(holder instanceof MediaGridStatusDisplayItem.Holder || holder instanceof LinkCardStatusDisplayItem.Holder)
pad=V.dp(16);
else
pad=V.dp(12);
// else
// pad=V.dp(12);
boolean insetLeft=true, insetRight=true;
if(insetLeft)
outRect.left=pad;
@@ -97,7 +97,7 @@ public class InsetStatusItemDecoration extends RecyclerView.ItemDecoration{
// had to comment this out because animations with offsets aren't handled properly.
// can be worked around by manually applying top margins to items
// see InsetDummyStatusDisplayItem#onBinds
// see InsetDummyStatusDisplayItem#onBind
// if(!topSiblingInset)
// outRect.top=pad;
// if(!bottomSiblingInset)

View File

@@ -0,0 +1,242 @@
package org.joinmastodon.android.ui.utils;
/*
* Copyright 2016 Ali Muzaffar
* <p/>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p/>
* http://www.apache.org/licenses/LICENSE-2.0
* <p/>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.text.Editable;
import android.text.TextWatcher;
import android.widget.TextView;
import java.lang.ref.WeakReference;
public class TextDrawable extends Drawable implements TextWatcher {
private WeakReference<TextView> ref;
private String mText;
private Paint mPaint;
private Rect mHeightBounds;
private boolean mBindToViewPaint = false;
private float mPrevTextSize = 0;
private boolean mInitFitText = false;
private boolean mFitTextEnabled = false;
/**
* Create a TextDrawable using the given paint object and string
*
* @param paint
* @param s
*/
public TextDrawable(Paint paint, String s) {
mText = s;
mPaint = new Paint(paint);
mHeightBounds = new Rect();
init();
}
/**
* Create a TextDrawable. This uses the given TextView to initialize paint and has initial text
* that will be drawn. Initial text can also be useful for reserving space that may otherwise
* not be available when setting compound drawables.
*
* @param tv The TextView / EditText using to initialize this drawable
* @param initialText Optional initial text to display
* @param bindToViewsText Should this drawable mirror the text in the TextView
* @param bindToViewsPaint Should this drawable mirror changes to Paint in the TextView, like textColor, typeface, alpha etc.
* Note, this will override any changes made using setColorFilter or setAlpha.
*/
public TextDrawable(TextView tv, String initialText, boolean bindToViewsText, boolean bindToViewsPaint) {
this(tv.getPaint(), initialText);
ref = new WeakReference<>(tv);
if (bindToViewsText || bindToViewsPaint) {
if (bindToViewsText) {
tv.addTextChangedListener(this);
}
mBindToViewPaint = bindToViewsPaint;
}
}
/**
* Create a TextDrawable. This uses the given TextView to initialize paint and the text that
* will be drawn.
*
* @param tv The TextView / EditText using to initialize this drawable
* @param bindToViewsText Should this drawable mirror the text in the TextView
* @param bindToViewsPaint Should this drawable mirror changes to Paint in the TextView, like textColor, typeface, alpha etc.
* Note, this will override any changes made using setColorFilter or setAlpha.
*/
public TextDrawable(TextView tv, boolean bindToViewsText, boolean bindToViewsPaint) {
this(tv, tv.getText().toString(), bindToViewsText, bindToViewsPaint);
}
/**
* Use the provided TextView/EditText to initialize the drawable.
* The Drawable will copy the Text and the Paint properties, however it will from that
* point on be independant of the TextView.
*
* @param tv a TextView or EditText or any of their children.
*/
public TextDrawable(TextView tv) {
this(tv, false, false);
}
/**
* Use the provided TextView/EditText to initialize the drawable.
* The Drawable will copy the Paint properties, and use the provided text to initialise itself.
*
* @param tv a TextView or EditText or any of their children.
* @param s The String to draw
*/
public TextDrawable(TextView tv, String s) {
this(tv, s, false, false);
}
@Override
public void draw(Canvas canvas) {
if (mBindToViewPaint && ref.get() != null) {
Paint p = ref.get().getPaint();
canvas.drawText(mText, 0, getBounds().height(), p);
} else {
if (mInitFitText) {
fitTextAndInit();
}
canvas.drawText(mText, 0, getBounds().height(), mPaint);
}
}
@Override
public void setAlpha(int alpha) {
mPaint.setAlpha(alpha);
}
@Override
public void setColorFilter(ColorFilter colorFilter) {
mPaint.setColorFilter(colorFilter);
}
@Override
public int getOpacity() {
int alpha = mPaint.getAlpha();
if (alpha == 0) {
return PixelFormat.TRANSPARENT;
} else if (alpha == 255) {
return PixelFormat.OPAQUE;
} else {
return PixelFormat.TRANSLUCENT;
}
}
private void init() {
Rect bounds = getBounds();
//We want to use some character to determine the max height of the text.
//Otherwise if we draw something like "..." they will appear centered
//Here I'm just going to use the entire alphabet to determine max height.
mPaint.getTextBounds("1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 0, 1, mHeightBounds);
//This doesn't account for leading or training white spaces.
//mPaint.getTextBounds(mText, 0, mText.length(), bounds);
float width = mPaint.measureText(mText);
bounds.top = mHeightBounds.top;
bounds.bottom = mHeightBounds.bottom;
bounds.right = (int) width;
bounds.left = 0;
setBounds(bounds);
}
public void setPaint(Paint paint) {
mPaint = new Paint(paint);
//Since this can change the font used, we need to recalculate bounds.
if (mFitTextEnabled && !mInitFitText) {
fitTextAndInit();
} else {
init();
}
invalidateSelf();
}
public Paint getPaint() {
return mPaint;
}
public void setText(String text) {
mText = text;
//Since this can change the bounds of the text, we need to recalculate.
if (mFitTextEnabled && !mInitFitText) {
fitTextAndInit();
} else {
init();
}
invalidateSelf();
}
public String getText() {
return mText;
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
setText(s.toString());
}
/**
* Make the TextDrawable match the width of the View it's associated with.
* <p/>
* Note: While this option will not work if bindToViewPaint is true.
*
* @param fitText
*/
public void setFillText(boolean fitText) {
mFitTextEnabled = fitText;
if (fitText) {
mPrevTextSize = mPaint.getTextSize();
if (ref.get() != null) {
if (ref.get().getWidth() > 0) {
fitTextAndInit();
} else {
mInitFitText = true;
}
}
} else {
if (mPrevTextSize > 0) {
mPaint.setTextSize(mPrevTextSize);
}
init();
}
}
private void fitTextAndInit() {
float fitWidth = ref.get().getWidth();
float textWidth = mPaint.measureText(mText);
float multi = fitWidth / textWidth;
mPaint.setTextSize(mPaint.getTextSize() * multi);
mInitFitText = false;
init();
}
}

View File

@@ -45,7 +45,9 @@ import android.transition.TransitionManager;
import android.transition.TransitionSet;
import android.util.Log;
import android.util.Pair;
import android.view.Gravity;
import android.view.HapticFeedbackConstants;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.SubMenu;
@@ -73,6 +75,7 @@ import org.joinmastodon.android.api.requests.accounts.SetAccountMuted;
import org.joinmastodon.android.api.requests.accounts.SetDomainBlocked;
import org.joinmastodon.android.api.requests.accounts.AuthorizeFollowRequest;
import org.joinmastodon.android.api.requests.accounts.RejectFollowRequest;
import org.joinmastodon.android.api.requests.instance.GetInstance;
import org.joinmastodon.android.api.requests.lists.DeleteList;
import org.joinmastodon.android.api.requests.notifications.DismissNotification;
import org.joinmastodon.android.api.requests.search.GetSearchResults;
@@ -93,6 +96,8 @@ import org.joinmastodon.android.fragments.ComposeFragment;
import org.joinmastodon.android.fragments.HashtagTimelineFragment;
import org.joinmastodon.android.fragments.ProfileFragment;
import org.joinmastodon.android.fragments.ThreadFragment;
import org.joinmastodon.android.fragments.settings.SettingsServerAboutFragment;
import org.joinmastodon.android.fragments.settings.SettingsServerFragment;
import org.joinmastodon.android.model.Account;
import org.joinmastodon.android.model.AccountField;
import org.joinmastodon.android.model.Emoji;
@@ -103,7 +108,6 @@ import org.joinmastodon.android.model.ScheduledStatus;
import org.joinmastodon.android.model.SearchResults;
import org.joinmastodon.android.model.Searchable;
import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.model.StatusPrivacy;
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
import org.joinmastodon.android.ui.text.CustomEmojiSpan;
import org.joinmastodon.android.ui.text.HtmlParser;
@@ -115,6 +119,7 @@ import java.lang.reflect.Method;
import java.net.IDN;
import java.net.URI;
import java.net.URISyntaxException;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
@@ -132,6 +137,7 @@ import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.BiConsumer;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -528,31 +534,70 @@ public class UiUtils {
.exec(accountID);
});
}
public static void confirmToggleMuteUser(Context context, String accountID, Account account, boolean currentlyMuted, Consumer<Relationship> resultCallback){
View durationView=LayoutInflater.from(context).inflate(R.layout.mute_user_dialog, null);
LinearLayout.LayoutParams params=new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
params.setMargins(0, V.dp(-12), 0, 0);
durationView.setLayoutParams(params);
Button button=durationView.findViewById(R.id.button);
((TextView) durationView.findViewById(R.id.message)).setText(context.getString(R.string.confirm_mute, account.displayName));
public static void confirmToggleMuteUser(Activity activity, String accountID, Account account, boolean currentlyMuted, Consumer<Relationship> resultCallback) {
showConfirmationAlert(activity, activity.getString(currentlyMuted ? R.string.confirm_unmute_title : R.string.confirm_mute_title),
activity.getString(currentlyMuted ? R.string.confirm_unmute : R.string.confirm_mute, account.displayName),
activity.getString(currentlyMuted ? R.string.do_unmute : R.string.do_mute),
currentlyMuted ? R.drawable.ic_fluent_speaker_0_28_regular : R.drawable.ic_fluent_speaker_off_28_regular,
() -> {
new SetAccountMuted(account.id, !currentlyMuted)
.setCallback(new Callback<>() {
AtomicReference<Duration> muteDuration=new AtomicReference<>(Duration.ZERO);
PopupMenu popupMenu=new PopupMenu(context, button, Gravity.CENTER_HORIZONTAL);
popupMenu.inflate(R.menu.mute_duration);
popupMenu.setOnMenuItemClickListener(item->{
int id=item.getItemId();
if(id==R.id.duration_indefinite)
muteDuration.set(Duration.ZERO);
else if(id==R.id.duration_minutes_5){
muteDuration.set(Duration.ofMinutes(5));
}else if(id==R.id.duration_minutes_30){
muteDuration.set(Duration.ofMinutes(30));
}else if(id==R.id.duration_hours_1){
muteDuration.set(Duration.ofHours(1));
}else if(id==R.id.duration_hours_6){
muteDuration.set(Duration.ofHours(6));
}else if(id==R.id.duration_days_1){
muteDuration.set(Duration.ofDays(1));
}else if(id==R.id.duration_days_3){
muteDuration.set(Duration.ofDays(3));
}else if(id==R.id.duration_days_7){
muteDuration.set(Duration.ofDays(7));
}
button.setText(item.getTitle());
return true;
});
button.setOnTouchListener(popupMenu.getDragToOpenListener());
button.setOnClickListener(v->popupMenu.show());
button.setText(popupMenu.getMenu().getItem(0).getTitle());
new M3AlertDialogBuilder(context)
.setTitle(context.getString(currentlyMuted ? R.string.confirm_unmute_title : R.string.confirm_mute_title))
.setMessage(currentlyMuted ? context.getString(R.string.confirm_unmute, account.displayName) : null)
.setView(currentlyMuted ? null : durationView)
.setPositiveButton(context.getString(currentlyMuted ? R.string.do_unmute : R.string.do_mute), (dlg, i)->{
new SetAccountMuted(account.id, !currentlyMuted, muteDuration.get().getSeconds())
.setCallback(new Callback<>(){
@Override
public void onSuccess(Relationship result) {
public void onSuccess(Relationship result){
resultCallback.accept(result);
if (!currentlyMuted) {
if(!currentlyMuted){
E.post(new RemoveAccountPostsEvent(accountID, account.id, false));
}
}
@Override
public void onError(ErrorResponse error) {
error.showToast(activity);
public void onError(ErrorResponse error){
error.showToast(context);
}
})
.wrapProgress(activity, R.string.loading, false)
.wrapProgress(context, R.string.loading, false)
.exec(accountID);
});
})
.setNegativeButton(R.string.cancel, null)
.setIcon(currentlyMuted ? R.drawable.ic_fluent_speaker_0_28_regular : R.drawable.ic_fluent_speaker_off_28_regular)
.show();
}
public static void confirmDeletePost(Activity activity, String accountID, Status status, Consumer<Status> resultCallback) {
@@ -1028,13 +1073,11 @@ public class UiUtils {
return back;
}
public static boolean setExtraTextInfo(Context ctx, TextView extraText, TextView pronouns, boolean mentionedOnly, boolean localOnly, @Nullable Account account) {
public static boolean setExtraTextInfo(Context ctx, TextView extraText, TextView pronouns, boolean displayPronouns, boolean mentionedOnly, boolean localOnly, @Nullable Account account) {
List<String> extraParts = new ArrayList<>();
Optional<String> p=pronouns==null ? Optional.empty() : extractPronouns(ctx, account);
boolean setPronouns=false;
Optional<String> p=pronouns==null || !displayPronouns ? Optional.empty() : extractPronouns(ctx, account);
if(p.isPresent()) {
HtmlParser.setTextWithCustomEmoji(pronouns, p.get(), account.emojis);
setPronouns=true;
pronouns.setVisibility(View.VISIBLE);
}else if(pronouns!=null){
pronouns.setVisibility(View.GONE);
@@ -1241,6 +1284,23 @@ public class UiUtils {
}
})
.exec(accountID));
} else if (uri.getPath() != null && uri.getPath().matches("^/about$")) {
return Optional.of(new GetInstance()
.setCallback(new Callback<>(){
@Override
public void onSuccess(Instance result){
Bundle args = new Bundle();
args.putParcelable("instance", Parcels.wrap(result));
args.putString("account", accountID);
go.accept(SettingsServerFragment.class, args);
}
@Override
public void onError(ErrorResponse error){
go.accept(null, bundleError(error));
}
})
.execNoAuth(uri.getHost()));
} else if (looksLikeFediverseUrl(url)) {
return Optional.of(new GetSearchResults(url, null, true)
.setCallback(new Callback<>() {
@@ -1586,7 +1646,7 @@ public class UiUtils {
private static String extractPronounsFromField(String localizedPronouns, AccountField field) {
if(!field.name.toLowerCase().contains(localizedPronouns) &&
!field.name.toLowerCase().contains("pronouns")) return null;
String text=HtmlParser.strip(field.value);
String text=HtmlParser.text(field.value);
if(field.value.toLowerCase().contains("https://")){
for(String pronounUrl : pronounsUrls){
int index=text.indexOf(pronounUrl);

View File

@@ -74,10 +74,17 @@ public class ComposePollViewController{
pollWrap=view.findViewById(R.id.poll_wrap);
Instance instance=fragment.instance;
if(instance.configuration!=null && instance.configuration.polls!=null && instance.configuration.polls.maxOptions>0)
maxPollOptions=instance.configuration.polls.maxOptions;
if(instance.configuration!=null && instance.configuration.polls!=null && instance.configuration.polls.maxCharactersPerOption>0)
maxPollOptionLength=instance.configuration.polls.maxCharactersPerOption;
if (!instance.isAkkoma()) {
if(instance.configuration!=null && instance.configuration.polls!=null && instance.configuration.polls.maxOptions>0)
maxPollOptions=instance.configuration.polls.maxOptions;
if(instance.configuration!=null && instance.configuration.polls!=null && instance.configuration.polls.maxCharactersPerOption>0)
maxPollOptionLength=instance.configuration.polls.maxCharactersPerOption;
} else {
if (instance.pollLimits!=null && instance.pollLimits.maxOptions>0)
maxPollOptions=instance.pollLimits.maxOptions;
if(instance.pollLimits!=null && instance.pollLimits.maxOptionChars>0)
maxPollOptionLength=instance.pollLimits.maxOptionChars;
}
pollOptionsView=pollWrap.findViewById(R.id.poll_options);
addPollOptionBtn=pollWrap.findViewById(R.id.add_poll_option);

View File

@@ -21,6 +21,7 @@ import android.widget.ProgressBar;
import android.widget.RadioButton;
import android.widget.TextView;
import org.joinmastodon.android.GlobalUserPreferences;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.requests.accounts.SetAccountFollowed;
import org.joinmastodon.android.api.session.AccountSessionManager;
@@ -124,7 +125,8 @@ public class AccountViewHolder extends BindableViewHolder<AccountViewModel> impl
}
// you know what's cooler than followers or verified links? yep. pronouns
Optional<String> pronounsString = UiUtils.extractPronouns(itemView.getContext(), item.account);
Optional<String> pronounsString=GlobalUserPreferences.displayPronounsInUserListings
? UiUtils.extractPronouns(itemView.getContext(), item.account) : Optional.empty();
pronouns.setVisibility(pronounsString.isPresent() ? View.VISIBLE : View.GONE);
pronounsString.ifPresent(p -> HtmlParser.setTextWithCustomEmoji(pronouns, p, item.account.emojis));
@@ -188,7 +190,10 @@ public class AccountViewHolder extends BindableViewHolder<AccountViewModel> impl
}
Bundle args=new Bundle();
args.putString("account", accountID);
args.putParcelable("profileAccount", Parcels.wrap(item.account));
if (item.account.isRemote)
args.putParcelable("remoteAccount", Parcels.wrap(item.account));
else
args.putParcelable("profileAccount", Parcels.wrap(item.account));
Nav.go(fragment.getActivity(), ProfileFragment.class, args);
}

View File

@@ -0,0 +1,44 @@
package org.joinmastodon.android.ui.views;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import me.grishka.appkit.views.UsableRecyclerView;
public class EmojiReactionsRecyclerView extends UsableRecyclerView{
public EmojiReactionsRecyclerView(Context context){
super(context);
}
public EmojiReactionsRecyclerView(Context context, AttributeSet attrs){
super(context, attrs);
}
public EmojiReactionsRecyclerView(Context context, AttributeSet attrs, int defStyle){
super(context, attrs, defStyle);
}
@Override
public boolean onTouchEvent(MotionEvent e){
super.onTouchEvent(e);
// to pass through touch events (i.e. clicking the status) to the parent view
return false;
}
// https://stackoverflow.com/questions/55372837/is-there-a-way-to-make-recyclerview-requiresfadingedge-unaffected-by-paddingtop
@Override
protected boolean isPaddingOffsetRequired() {
return true;
}
@Override
protected int getLeftPaddingOffset(){
return -getPaddingLeft();
}
@Override
protected int getRightPaddingOffset() {
return getPaddingRight();
}
}

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="?colorM3OnSurfaceVariant" android:state_enabled="false" />
<item android:color="?colorM3OnSurfaceVariant" android:state_selected="false" />
<item android:color="?colorM3OnSurface" />
</selector>

View File

@@ -1,10 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:pathData="M63.26,36.4C67.49,37.02 71.06,40.24 71.61,44.32C71.85,46.75 71.73,50.4 71.67,52.17C71.65,52.61 71.64,52.94 71.64,53.1C71.64,53.34 71.61,55.51 71.6,55.74C71.22,61.58 67.58,63.88 63.75,64.62C63.7,64.63 63.66,64.64 63.61,64.65C63.6,64.65 63.58,64.65 63.57,64.65C61.14,65.13 58.54,65.25 56.07,65.32C55.48,65.34 54.89,65.34 54.3,65.34C51.85,65.34 49.4,65.05 47.01,64.47C47,64.47 46.99,64.47 46.97,64.47C46.96,64.48 46.95,64.48 46.94,64.49C46.93,64.5 46.92,64.51 46.92,64.52C46.91,64.53 46.91,64.55 46.91,64.56C46.98,65.33 47.14,66.1 47.41,66.83C47.74,67.68 48.9,69.71 53.19,69.71C55.69,69.71 58.17,69.42 60.6,68.84C60.61,68.84 60.63,68.84 60.64,68.84C60.65,68.85 60.66,68.85 60.67,68.86C60.68,68.87 60.69,68.88 60.7,68.89C60.7,68.9 60.7,68.91 60.7,68.93V71.79C60.7,71.8 60.7,71.82 60.69,71.83C60.69,71.84 60.68,71.85 60.67,71.86C59.91,72.41 58.89,72.73 58,73.01C57.96,73.02 57.91,73.04 57.88,73.05C57.47,73.18 57.06,73.29 56.64,73.39C52.85,74.25 48.9,74.04 45.22,72.79C41.79,71.58 38.28,68.64 37.42,65.1C36.95,63.18 36.63,61.23 36.44,59.27C36.25,57.12 36.18,54.97 36.11,52.82C36.09,52.01 36.06,51.2 36.03,50.38C35.95,48.32 36,46.06 36.44,44.03C37.35,39.89 41.1,37 45.21,36.4C45.3,36.38 45.39,36.37 45.5,36.35C46.31,36.21 48.01,35.91 53.53,35.91H53.58C59.84,35.91 62.55,36.29 63.26,36.4ZM65.39,58.94V48.84C65.39,46.78 64.86,45.14 63.81,43.93C62.71,42.72 61.29,42.09 59.51,42.09C57.47,42.09 55.92,42.88 54.88,44.45L53.88,46.12L52.88,44.45C51.85,42.88 50.3,42.09 48.25,42.09C46.47,42.09 45.05,42.72 43.96,43.93C42.9,45.14 42.37,46.78 42.37,48.84V58.94H46.38V49.14C46.38,47.08 47.25,46.03 48.99,46.03C50.92,46.03 51.89,47.27 51.89,49.73V55.09H55.87V49.73C55.87,47.27 56.84,46.03 58.76,46.03C60.52,46.03 61.38,47.08 61.38,49.14V58.94H65.39Z"
android:fillColor="#4A454E"
android:fillType="evenOdd"/>
</vector>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#B2000000"/>
<solid android:color="#69000000"/>
<corners android:radius="4dp"/>
</shape>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#D0000000"/>
<corners android:radius="4dp"/>
</shape>

View File

@@ -1,21 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<ripple
android:color="@color/m3_on_surface_variant_overlay"
android:radius="42dp" />
</item>
<item>
<selector>
<item android:state_selected="true">
<layer-list>
<item android:gravity="center" android:width="28dp" android:height="28dp">
<shape android:shape="oval">
<stroke android:color="?colorM3OnSecondaryContainer" android:width="2dp"/>
</shape>
</item>
</layer-list>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true">
<layer-list>
<item android:gravity="center" android:width="28dp" android:height="28dp">
<shape android:shape="oval">
<stroke android:color="?colorM3OnSecondaryContainer" android:width="2dp"/>
</shape>
</item>
</selector>
</layer-list>
</item>
</layer-list>
</selector>

View File

@@ -2,4 +2,4 @@
<ripple
xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/m3_on_surface_variant_overlay"
android:radius="42dp" />
android:radius="56dp" />

View File

@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M4,15V13H20V15ZM4,11V9H20V11Z"/>
</vector>

View File

@@ -0,0 +1,12 @@
<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="M12,1.996C16.05,1.996 19.357,5.191 19.496,9.245L19.5,9.496V13.593L20.88,16.749C20.949,16.907 20.985,17.077 20.985,17.25C20.985,17.94 20.425,18.5 19.735,18.5L15,18.501C15,20.158 13.657,21.501 12,21.501C10.402,21.501 9.096,20.252 9.005,18.678L9,18.499L4.275,18.5C4.104,18.5 3.934,18.465 3.777,18.396C3.144,18.121 2.853,17.385 3.128,16.752L4.5,13.594V9.496C4.501,5.341 7.852,1.996 12,1.996ZM13.5,18.499L10.5,18.501C10.5,19.33 11.172,20.001 12,20.001C12.78,20.001 13.421,19.406 13.493,18.646L13.5,18.499ZM12,3.496C8.68,3.496 6.001,6.17 6,9.496V13.906L4.656,17H19.353L18,13.907L18,9.509L17.997,9.284C17.885,6.05 15.242,3.496 12,3.496Z"
android:fillColor="#212121"/>
<path
android:pathData="M8.083,9.969A0.508,0.508 0,0 0,8.807 10.682L11.494,7.955L11.494,14.897a0.508,0.508 0,1 0,1.016 0L12.509,7.958L15.193,10.682A0.508,0.508 45,0 0,15.917 9.969L12.452,6.453a0.635,0.635 0,0 0,-0.904 0z"
android:fillColor="#212121"/>
</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="M12 2c-0.667 0-1.32 0.065-1.95 0.19-0.407 0.08-0.671 0.475-0.591 0.882 0.08 0.406 0.475 0.67 0.881 0.59C10.877 3.556 11.431 3.5 12 3.5s1.123 0.056 1.66 0.162c0.406 0.08 0.8-0.184 0.881-0.59 0.08-0.407-0.184-0.801-0.59-0.882C13.319 2.065 12.667 2 12 2zM7.278 4.931c0.344-0.23 0.436-0.696 0.206-1.04-0.23-0.344-0.697-0.437-1.04-0.206-1.09 0.73-2.03 1.668-2.76 2.758-0.23 0.345-0.137 0.81 0.207 1.04 0.344 0.231 0.81 0.139 1.04-0.205 0.621-0.927 1.42-1.726 2.347-2.347zm10.279-1.246c-0.345-0.23-0.81-0.138-1.04 0.206-0.231 0.344-0.139 0.81 0.205 1.04 0.927 0.621 1.726 1.42 2.347 2.347 0.23 0.344 0.696 0.436 1.04 0.206 0.344-0.23 0.437-0.697 0.206-1.04-0.73-1.09-1.668-2.03-2.758-2.76zm4.253 6.364c-0.08-0.406-0.475-0.67-0.882-0.59-0.406 0.08-0.67 0.475-0.59 0.881 0.106 0.537 0.162 1.091 0.162 1.66s-0.056 1.123-0.162 1.66c-0.08 0.406 0.184 0.8 0.59 0.881 0.407 0.08 0.801-0.184 0.882-0.59C21.935 13.319 22 12.667 22 12s-0.065-1.32-0.19-1.95zM3.662 10.34c0.08-0.406-0.184-0.8-0.59-0.881-0.407-0.08-0.801 0.184-0.882 0.59C2.065 10.681 2 11.333 2 12s0.065 1.32 0.19 1.95c0.08 0.407 0.475 0.671 0.882 0.591 0.406-0.08 0.67-0.475 0.59-0.881C3.556 13.123 3.5 12.569 3.5 12s0.056-1.123 0.162-1.66zm1.27 6.382C4.7 16.378 4.234 16.286 3.89 16.516s-0.437 0.697-0.206 1.04c0.73 1.09 1.668 2.03 2.758 2.76 0.345 0.23 0.81 0.137 1.04-0.207 0.231-0.344 0.139-0.81-0.205-1.04-0.927-0.621-1.726-1.42-2.347-2.347zm15.383 0.835c0.23-0.345 0.138-0.81-0.206-1.04-0.344-0.231-0.81-0.139-1.04 0.205-0.621 0.927-1.42 1.726-2.347 2.347-0.344 0.23-0.436 0.696-0.206 1.04 0.23 0.344 0.697 0.437 1.04 0.206 1.09-0.73 2.03-1.668 2.76-2.758zm-9.975 2.781c-0.406-0.08-0.8 0.184-0.881 0.59-0.08 0.407 0.184 0.801 0.59 0.882C10.681 21.935 11.333 22 12 22s1.32-0.065 1.95-0.19c0.407-0.08 0.671-0.475 0.591-0.882-0.08-0.406-0.475-0.67-0.881-0.59C13.123 20.444 12.569 20.5 12 20.5s-1.123-0.056-1.66-0.162zM9 9.248c0-0.952 1.023-1.554 1.856-1.093l5.757 3.186C16.852 11.473 17 11.724 17 11.997c0 0.272-0.148 0.523-0.387 0.655l-5.757 3.187C10.023 16.299 9 15.697 9 14.745V9.247z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -24,7 +24,7 @@
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:textAppearance="@style/m3_label_large"
android:textColor="?colorGray25"
android:textColor="?colorM3DarkOnSurface"
android:gravity="center"
android:includeFontPadding="false"
android:paddingHorizontal="6dp"

View File

@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:id="@+id/line"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageButton
android:id="@+id/add_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="2dp"
android:minHeight="48dp"
android:minWidth="48dp"
android:background="@drawable/bg_button_m3_tonal_circle"
android:tooltipText="@string/sk_button_react"
android:contentDescription="@string/sk_button_react"
android:src="@drawable/ic_fluent_add_24_filled" />
<org.joinmastodon.android.ui.views.EmojiReactionsRecyclerView
android:id="@+id/list"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:layout_marginStart="-4dp"
android:paddingStart="8dp"
android:clipToPadding="false"
android:requiresFadingEdge="horizontal"
android:fadingEdgeLength="24dp" />
</LinearLayout>
<Space
android:id="@+id/space"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="8dp" />
</LinearLayout>

View File

@@ -7,156 +7,171 @@
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:orientation="horizontal"
<GridLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="11sp">
<!-- avatar width (46sp) / 2 - button width (24sp) / 2 -->
android:rowCount="2"
android:columnCount="1">
<FrameLayout
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/reply_btn"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:paddingVertical="12dp">
<ImageView
android:layout_width="24sp"
android:layout_height="24sp"
android:layout_gravity="center_vertical"
android:layout_marginStart="16dp"
android:duplicateParentState="true"
android:src="@drawable/ic_fluent_chat_multiple_24_selector_text"
android:tint="?android:textColorSecondary"
android:gravity="center_vertical" />
<TextView
android:id="@+id/reply"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:paddingStart="8dp"
android:minWidth="16dp"
android:gravity="center_vertical"
android:textAppearance="@style/m3_label_large"
android:maxLines="1"
android:ellipsize="end"
tools:text="123"
tools:ignore="RtlSymmetry" />
</LinearLayout>
</FrameLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="11sp">
<!-- avatar width (46sp) / 2 - button width (24sp) / 2 -->
<FrameLayout
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/boost_btn"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:foregroundTint="@color/boost_icon"
android:paddingVertical="12dp">
<ImageView
android:layout_width="24sp"
android:layout_height="24sp"
android:layout_gravity="center_vertical"
android:layout_marginStart="16dp"
android:duplicateParentState="true"
android:src="@drawable/ic_boost"
android:tint="@color/boost_icon"
android:gravity="center_vertical" />
<TextView
android:id="@+id/boost"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:paddingStart="8dp"
android:minWidth="16dp"
android:duplicateParentState="true"
android:textColor="@color/boost_icon"
android:gravity="center_vertical"
android:textAppearance="@style/m3_label_large"
android:maxLines="1"
android:ellipsize="end"
tools:text="123"
tools:ignore="RtlSymmetry" />
</LinearLayout>
</FrameLayout>
<FrameLayout
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/favorite_btn"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:paddingVertical="12dp">
<ImageView
android:layout_width="24sp"
android:layout_height="24sp"
android:layout_gravity="center_vertical"
android:layout_marginStart="16dp"
android:duplicateParentState="true"
android:src="@drawable/ic_fluent_star_24_selector"
android:tint="@color/favorite_icon"
android:gravity="center_vertical" />
<TextView
android:id="@+id/favorite"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:paddingStart="8dp"
android:minWidth="16dp"
android:textColor="@color/favorite_icon"
android:gravity="center_vertical"
android:textAppearance="@style/m3_label_large"
android:maxLines="1"
android:ellipsize="end"
tools:text="123"
tools:ignore="RtlSymmetry" />
</LinearLayout>
</FrameLayout>
<FrameLayout
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent">
<FrameLayout
android:id="@+id/bookmark_btn"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/reply_btn"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:paddingVertical="12dp">
<ImageView
android:layout_width="24sp"
android:layout_height="24sp"
android:layout_gravity="center_vertical"
android:layout_marginStart="16dp"
android:duplicateParentState="true"
android:src="@drawable/ic_fluent_chat_multiple_24_selector_text"
android:tint="?android:textColorSecondary"
android:gravity="center_vertical" />
<TextView
android:id="@+id/reply"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:paddingStart="8dp"
android:minWidth="16dp"
android:gravity="center_vertical"
android:textAppearance="@style/m3_label_large"
android:maxLines="1"
android:ellipsize="end"
tools:text="123"
tools:ignore="RtlSymmetry" />
</LinearLayout>
</FrameLayout>
<FrameLayout
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/boost_btn"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:foregroundTint="@color/boost_icon"
android:paddingVertical="12dp">
<ImageView
android:layout_width="24sp"
android:layout_height="24sp"
android:layout_gravity="center_vertical"
android:layout_marginStart="16dp"
android:duplicateParentState="true"
android:src="@drawable/ic_boost"
android:tint="@color/boost_icon"
android:gravity="center_vertical" />
<TextView
android:id="@+id/boost"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:paddingStart="8dp"
android:minWidth="16dp"
android:duplicateParentState="true"
android:textColor="@color/boost_icon"
android:gravity="center_vertical"
android:textAppearance="@style/m3_label_large"
android:maxLines="1"
android:ellipsize="end"
tools:text="123"
tools:ignore="RtlSymmetry" />
</LinearLayout>
</FrameLayout>
<FrameLayout
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/favorite_btn"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:paddingVertical="12dp">
<ImageView
android:layout_width="24sp"
android:layout_height="24sp"
android:layout_gravity="center_vertical"
android:layout_marginStart="16dp"
android:duplicateParentState="true"
android:src="@drawable/ic_fluent_star_24_selector"
android:tint="@color/favorite_icon"
android:gravity="center_vertical" />
<TextView
android:id="@+id/favorite"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:paddingStart="8dp"
android:minWidth="16dp"
android:textColor="@color/favorite_icon"
android:gravity="center_vertical"
android:textAppearance="@style/m3_label_large"
android:maxLines="1"
android:ellipsize="end"
tools:text="123"
tools:ignore="RtlSymmetry" />
</LinearLayout>
</FrameLayout>
<FrameLayout
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent">
<FrameLayout
android:id="@+id/bookmark_btn"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:paddingVertical="12dp">
<ImageView
android:id="@+id/bookmark"
android:layout_width="24sp"
android:layout_height="24sp"
android:layout_gravity="center_vertical"
android:layout_marginHorizontal="16dp"
android:src="@drawable/ic_fluent_bookmark_24_selector"
android:tint="@color/bookmark_icon"
android:gravity="center_vertical"
android:textAppearance="@style/m3_label_large" />
</FrameLayout>
</FrameLayout>
<FrameLayout
android:id="@+id/share_btn"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:paddingVertical="12dp">
<ImageView
android:id="@+id/bookmark"
android:id="@+id/share"
android:layout_width="24sp"
android:layout_height="24sp"
android:layout_gravity="center_vertical"
android:layout_marginHorizontal="16dp"
android:src="@drawable/ic_fluent_bookmark_24_selector"
android:tint="@color/bookmark_icon"
android:gravity="center_vertical"
android:textAppearance="@style/m3_label_large" />
android:src="@drawable/ic_fluent_share_24_regular"
android:tint="?android:textColorSecondary"
android:gravity="center_vertical"/>
</FrameLayout>
</FrameLayout>
<FrameLayout
android:id="@+id/share_btn"
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:paddingVertical="12dp">
<ImageView
android:id="@+id/share"
android:layout_width="24sp"
android:layout_height="24sp"
android:layout_gravity="center_vertical"
android:layout_marginHorizontal="16dp"
android:src="@drawable/ic_fluent_share_24_regular"
android:tint="?android:textColorSecondary"
android:gravity="center_vertical"/>
</FrameLayout>
android:layout_height="wrap_content"
android:id="@+id/footer_emoji_keyboard_container"
android:orientation="vertical">
</LinearLayout>
</GridLayout>
</LinearLayout>
</org.joinmastodon.android.ui.views.MaxWidthFrameLayout>

View File

@@ -33,7 +33,7 @@
android:layout_marginEnd="4dp"
android:importantForAccessibility="no"
android:textAppearance="@style/m3_label_large"
android:textColor="#FFF"
android:textColor="?colorM3DarkOnSurface"
android:gravity="center"
android:includeFontPadding="false"
android:background="@drawable/bg_image_alt_overlay"

View File

@@ -5,14 +5,13 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="16dp"
android:layout_marginBottom="-12dp">
android:layout_marginBottom="-4dp">
<TextView
android:id="@+id/extra_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="16dp"
android:paddingBottom="6dp"
android:textAppearance="@style/m3_title_small"
android:textColor="?colorM3OnSurface"
android:drawableStart="@drawable/ic_fluent_arrow_reply_20sp_filled"
@@ -40,7 +39,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="16dp"
android:paddingBottom="6dp"
android:textAppearance="@style/m3_title_small"
android:textColor="?colorM3OnSurface"
android:drawableStart="@drawable/ic_fluent_arrow_repeat_all_20_filled"

View File

@@ -34,7 +34,7 @@
android:layout_marginEnd="4dp"
android:importantForAccessibility="no"
android:textAppearance="@style/m3_label_large"
android:textColor="#FFF"
android:textColor="?colorM3DarkOnSurface"
android:gravity="center"
android:includeFontPadding="false"
android:background="@drawable/bg_image_alt_overlay"

View File

@@ -56,8 +56,8 @@
<FrameLayout
android:id="@+id/avatar_border"
android:layout_width="104dp"
android:layout_height="104dp"
android:layout_width="108dp"
android:layout_height="108dp"
android:layout_below="@id/cover"
android:layout_alignParentStart="true"
android:layout_marginStart="12dp"
@@ -67,8 +67,8 @@
<ImageView
android:id="@+id/avatar"
android:layout_width="96dp"
android:layout_height="96dp"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_gravity="center"
android:contentDescription="@string/profile_picture"
android:scaleType="centerCrop"

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:orientation="vertical"
android:paddingEnd="8dp"
tools:ignore="RtlSymmetry">
<ProgressBar
android:id="@+id/progress"
style="?android:progressBarStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:elevation="10dp"
android:indeterminate="true"
android:outlineProvider="none"
android:visibility="gone"/>
<org.joinmastodon.android.ui.views.ProgressBarButton
android:id="@+id/btn"
style="@style/Widget.Mastodon.M3.Button.Outlined.Icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableTint="@null"
android:drawableStart="@drawable/image_placeholder"
android:background="@drawable/bg_button_m3_tonal"/>
</FrameLayout>

View File

@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="24dp">
<TextView
android:id="@+id/message"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:gravity="center_vertical"
android:textColor="?android:textColorPrimary"
android:text="@string/confirm_mute"
android:textSize="16sp"/>
<org.joinmastodon.android.ui.views.AutoOrientationLinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layoutDirection="locale">
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:paddingVertical="8dp"
android:gravity="center_vertical"
android:textColor="?android:textColorPrimary"
android:text="@string/sk_mute_label"
android:textSize="16sp"/>
<Button
android:id="@+id/button"
style="@style/Widget.Mastodon.M3.Button.Outlined"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/bg_button_m3_tonal"
android:ellipsize="none"
android:singleLine="true"
android:stateListAnimator="@null"
android:textColor="?android:textColorPrimary"
android:textSize="16sp" />
</org.joinmastodon.android.ui.views.AutoOrientationLinearLayout>
</LinearLayout>

View File

@@ -1,28 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="8dp"
android:paddingTop="8dp"
android:paddingEnd="16dp"
android:clipToPadding="false"
android:background="?colorM3Background">
android:background="?colorM3Background"
tools:ignore="RtlSymmetry">
<ImageView
android:id="@+id/dragger_thingy"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_alignParentStart="true"
android:layout_marginEnd="8dp"
android:scaleType="center"
android:tint="?colorM3OnSurface"
android:contentDescription="@string/reorder"
android:src="@drawable/ic_drag_handle_24px"/>
android:src="@drawable/ic_fluent_re_order_dots_vertical_24_regular"/>
<ImageButton
android:id="@+id/delete"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_alignParentEnd="true"
android:layout_marginStart="8dp"
android:scaleType="center"
@@ -31,32 +32,37 @@
android:contentDescription="@string/delete"
android:src="@drawable/ic_fluent_delete_24_regular"/>
<EditText
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_toEndOf="@id/dragger_thingy"
android:layout_toStartOf="@id/delete"
style="@style/Widget.Mastodon.M3.EditText"
android:inputType="textCapSentences"
android:hint="@string/field_content"
android:saveEnabled="false"
android:singleLine="true"/>
<EditText
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:layout_marginBottom="6dp"
android:layout_toEndOf="@id/dragger_thingy"
android:layout_toStartOf="@id/delete"
android:layout_below="@id/content"
style="@style/Widget.Mastodon.M3.EditText"
android:inputType="textCapSentences"
android:hint="@string/field_label"
android:saveEnabled="false"
android:singleLine="true"/>
<EditText
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toEndOf="@id/dragger_thingy"
android:layout_toStartOf="@id/delete"
android:layout_below="@id/title"
style="@style/Widget.Mastodon.M3.EditText"
android:inputType="textCapSentences"
android:hint="@string/field_content"
android:saveEnabled="false"
android:singleLine="true"/>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_below="@id/content"
android:layout_marginTop="8dp"
android:background="?colorM3SurfaceVariant"/>
</RelativeLayout>

View File

@@ -7,7 +7,7 @@
android:layout_gravity="end|bottom"
android:layout_margin="12dp"
android:importantForAccessibility="noHideDescendants"
android:background="@drawable/bg_image_alt_overlay">
android:background="@drawable/bg_image_alt_text_overlay">
<ImageView
android:id="@+id/no_alt_button"
@@ -22,7 +22,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/m3_label_large"
android:textColor="?colorGray25"
android:textColor="?colorM3DarkOnSurface"
android:gravity="center"
android:includeFontPadding="false"
android:paddingHorizontal="6dp"
@@ -36,7 +36,7 @@
android:layout_height="40dp"
android:layout_gravity="end|top"
android:src="@drawable/ic_baseline_close_24"
android:tint="#FFF"
android:tint="?colorM3DarkOnSurface"
android:background="?android:actionBarItemBackground"/>
<org.joinmastodon.android.ui.views.NestableScrollView
@@ -58,7 +58,7 @@
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:textAppearance="@style/m3_body_medium"
android:textColor="#FFF"
android:textColor="?colorM3DarkOnSurface"
tools:text="Alt text goes here"/>
<TextView
@@ -69,7 +69,7 @@
android:layout_marginEnd="8dp"
android:layout_marginStart="14dp"
android:textAppearance="@style/m3_label_large"
android:textColor="?colorGray25"
android:textColor="?colorM3DarkOnSurface"
android:text="@string/sk_no_alt_text"/>
</LinearLayout>

View File

@@ -12,110 +12,187 @@
<org.joinmastodon.android.ui.views.TabBar
android:id="@+id/tabbar"
android:layout_width="match_parent"
android:layout_height="56dp">
android:layout_height="wrap_content"
android:paddingTop="12dp"
android:paddingBottom="16dp"
android:paddingHorizontal="8dp">
<FrameLayout
<LinearLayout
android:id="@+id/tab_home"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_marginEnd="8dp"
android:background="@drawable/bg_tabbar_tab"
android:contentDescription="@string/home_timeline">
android:orientation="vertical">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center"
android:scaleType="center"
android:importantForAccessibility="no"
android:tint="@color/tab_bar_icon"
android:src="@drawable/ic_fluent_home_24_selector"/>
<FrameLayout
android:id="@+id/tab_home_pill"
android:layout_width="match_parent"
android:layout_height="32dp"
android:background="@drawable/bg_tabbar_tab"
android:contentDescription="@string/home_timeline">
</FrameLayout>
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center"
android:scaleType="center"
android:importantForAccessibility="no"
android:tint="@color/tab_bar_icon"
android:src="@drawable/ic_fluent_home_24_selector"/>
<FrameLayout
</FrameLayout>
<TextView
android:id="@+id/tab_home_label"
style="@style/m3_label_medium"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="4dp"
android:textColor="@color/m3_on_surface_selector"
android:text="@string/sk_tab_home" />
</LinearLayout>
<LinearLayout
android:id="@+id/tab_search"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_marginEnd="8dp"
android:background="@drawable/bg_tabbar_tab"
android:contentDescription="@string/search_hint">
android:orientation="vertical">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center"
android:scaleType="center"
android:importantForAccessibility="no"
android:tint="@color/tab_bar_icon"
android:src="@drawable/ic_fluent_search_24_selector"/>
<FrameLayout
android:id="@+id/tab_search_pill"
android:layout_width="match_parent"
android:layout_height="32dp"
android:background="@drawable/bg_tabbar_tab"
android:contentDescription="@string/search_hint">
</FrameLayout>
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center"
android:scaleType="center"
android:importantForAccessibility="no"
android:tint="@color/tab_bar_icon"
android:src="@drawable/ic_fluent_search_24_selector"/>
<RelativeLayout
</FrameLayout>
<TextView
android:id="@+id/tab_search_label"
style="@style/m3_label_medium"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/m3_on_surface_selector"
android:layout_gravity="center_horizontal"
android:layout_marginTop="4dp"
android:text="@string/sk_tab_search" />
</LinearLayout>
<LinearLayout
android:id="@+id/tab_notifications"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_marginEnd="8dp"
android:background="@drawable/bg_tabbar_tab"
android:contentDescription="@string/notifications">
android:orientation="vertical">
<RelativeLayout
android:id="@+id/tab_notifications_pill"
android:layout_width="match_parent"
android:layout_height="32dp"
android:background="@drawable/bg_tabbar_tab"
android:contentDescription="@string/notifications">
<ImageView
android:id="@+id/notifications_icon"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_centerInParent="true"
android:scaleType="center"
android:importantForAccessibility="no"
android:tint="@color/tab_bar_icon"
android:src="@drawable/ic_fluent_alert_24_selector"/>
<TextView
android:id="@+id/notifications_badge"
android:layout_width="wrap_content"
android:layout_height="16dp"
android:layout_gravity="center"
android:layout_alignTop="@id/notifications_icon"
android:layout_toEndOf="@id/notifications_icon"
android:layout_marginTop="-4dp"
android:layout_marginStart="-12dp"
android:background="@drawable/bg_tabbar_badge"
android:textColor="?colorM3OnPrimary"
android:gravity="center"
android:includeFontPadding="false"
android:textAppearance="@style/m3_label_small"
android:minWidth="16dp"
tools:text="222"/>
</RelativeLayout>
<ImageView
android:id="@+id/notifications_icon"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_centerInParent="true"
android:scaleType="center"
android:importantForAccessibility="no"
android:tint="@color/tab_bar_icon"
android:src="@drawable/ic_fluent_alert_24_selector"/>
<TextView
android:id="@+id/notifications_badge"
android:id="@+id/tab_notifications_label"
style="@style/m3_label_medium"
android:layout_width="wrap_content"
android:layout_height="16dp"
android:layout_gravity="center"
android:layout_alignTop="@id/notifications_icon"
android:layout_toEndOf="@id/notifications_icon"
android:layout_marginTop="-4dp"
android:layout_marginStart="-12dp"
android:background="@drawable/bg_tabbar_badge"
android:textColor="?colorM3OnPrimary"
android:gravity="center"
android:includeFontPadding="false"
android:textAppearance="@style/m3_label_small"
android:minWidth="16dp"
tools:text="222"/>
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="4dp"
android:textColor="@color/m3_on_surface_selector"
android:text="@string/sk_tab_notifications" />
</RelativeLayout>
</LinearLayout>
<FrameLayout
<LinearLayout
android:id="@+id/tab_profile"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@drawable/bg_tabbar_tab"
android:contentDescription="@string/my_profile">
<ImageView
android:id="@+id/tab_profile_ava"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center"
android:scaleType="centerCrop"
android:src="@null"/>
<ImageView
android:layout_width="8dp"
android:layout_height="24dp"
android:layout_gravity="center"
android:layout_marginStart="22dp"
android:importantForAccessibility="no"
android:scaleType="center"
android:tint="@color/tab_bar_icon"
android:src="@drawable/ic_fluent_chevron_up_down_16_regular"/>
</FrameLayout>
android:layout_marginEnd="8dp"
android:orientation="vertical">
<FrameLayout
android:id="@+id/tab_profile_pill"
android:layout_width="match_parent"
android:layout_height="32dp"
android:background="@drawable/bg_tabbar_tab"
android:contentDescription="@string/my_profile">
<ImageView
android:id="@+id/tab_profile_ava"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center"
android:scaleType="centerCrop"
android:src="@null"/>
<ImageView
android:layout_width="8dp"
android:layout_height="24dp"
android:layout_gravity="center"
android:layout_marginStart="22dp"
android:importantForAccessibility="no"
android:scaleType="center"
android:tint="@color/tab_bar_icon"
android:src="@drawable/ic_fluent_chevron_up_down_16_regular"/>
</FrameLayout>
<TextView
android:id="@+id/tab_profile_label"
style="@style/m3_label_medium"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="4dp"
android:textColor="@color/m3_on_surface_selector"
android:text="@string/sk_tab_profile" />
</LinearLayout>
</org.joinmastodon.android.ui.views.TabBar>
</FrameLayout>

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/duration_indefinite" android:title="@string/sk_duration_indefinite" />
<item android:id="@+id/duration_minutes_5" android:title="@string/sk_duration_minutes_5"/>
<item android:id="@+id/duration_minutes_30" android:title="@string/sk_duration_minutes_30"/>
<item android:id="@+id/duration_hours_1" android:title="@string/sk_duration_hours_1"/>
<item android:id="@+id/duration_hours_6" android:title="@string/sk_duration_hours_6"/>
<item android:id="@+id/duration_days_1" android:title="@string/sk_duration_days_1"/>
<item android:id="@+id/duration_days_3" android:title="@string/sk_duration_days_3"/>
<item android:id="@+id/duration_days_7" android:title="@string/sk_duration_days_7"/>
</menu>

View File

@@ -4,11 +4,18 @@
<string name="next">التالي</string>
<string name="loading_instance">جارٍ جلب معلومات الخادم…</string>
<string name="error">خطأ</string>
<string name="not_a_mastodon_instance">لا يبدو أنّ %s كخادم ماستدون.</string>
<string name="ok">حسنًا</string>
<string name="preparing_auth">جَارٍ الإعدَادُ لِلمُصادَقَة…</string>
<string name="finishing_auth">يُنهي المصادقة…</string>
<string name="user_boosted">%s إعادة نشر</string>
<string name="in_reply_to">ردًا على %s</string>
<string name="notifications">الإشعارات</string>
<string name="user_followed_you">%s بَدَأ بِمُتابَعَتِك</string>
<string name="user_sent_follow_request">%s أرسَلَ طَلَبًا لِمُتابَعَتِك</string>
<string name="user_favorited">%s أعجَبه منشورك</string>
<string name="notification_boosted">قام %s بإعادة نشر منشورك</string>
<string name="poll_ended">الاطلاع على نتائج استطلاع الرأي الذي صوّت فيه</string>
<string name="share_toot_title">شارك</string>
<string name="settings">الإعدادات</string>
<string name="publish">انشر</string>
@@ -37,7 +44,8 @@
<string name="profile_about">حَول</string>
<string name="button_follow">تابِع</string>
<string name="button_following">مُتابَع</string>
<string name="edit_profile">حرّر الملف الشخصي</string>
<string name="edit_profile">تعديل الملف الشخصي</string>
<string name="share_user">شارك الصفحة الشخصية</string>
<string name="mute_user">كَتمُ %s</string>
<string name="unmute_user">إلغاء الكَتم عن @%s</string>
<string name="block_user">حَظرُ %s</string>
@@ -117,8 +125,16 @@
<item quantity="many">تبقى %d يومًا</item>
<item quantity="other">تبقى %d يوم</item>
</plurals>
<string name="poll_closed">انتهى</string>
<string name="confirm_mute_title">اكتم الحساب</string>
<plurals name="x_votes">
<item quantity="zero">%,d صوت</item>
<item quantity="one">%,d صوت واحد</item>
<item quantity="two">صوتين</item>
<item quantity="few">%,d أصوات</item>
<item quantity="many">%,d صوتا</item>
<item quantity="other">%,d صوتا</item>
</plurals>
<string name="poll_closed">مغلق</string>
<string name="confirm_mute_title">كتم الحساب</string>
<string name="confirm_mute">أكّد كتم %s</string>
<string name="do_mute">اكتم</string>
<string name="confirm_unmute_title">ارفع الكتم عن الحساب</string>
@@ -135,17 +151,20 @@
<string name="button_blocked">محجوب</string>
<string name="action_vote">صَوّت</string>
<string name="delete">احذف</string>
<string name="confirm_delete_title">احذف المنشور</string>
<string name="confirm_delete">أمتأكد من حذف هذا المنشور؟</string>
<string name="deleting">يحذف…</string>
<string name="notification_channel_audio_player">تشغيل الصوت</string>
<string name="play">شغّل</string>
<string name="pause">ألبث</string>
<string name="log_out">الخروج</string>
<string name="add_account">أضف حساباً</string>
<string name="search_hint">ابحث</string>
<string name="hashtags">وُسُوم</string>
<string name="news">الأخبار</string>
<string name="for_you">لأجلك</string>
<string name="mentions">الذِكر</string>
<string name="all_notifications">كلها</string>
<string name="mentions">الإشارات</string>
<plurals name="x_people_talking">
<item quantity="zero">لا أحد يتحدث</item>
<item quantity="one">شخص واحد يتحدث</item>
@@ -155,11 +174,16 @@
<item quantity="other">%d شخص يتحدثون</item>
</plurals>
<string name="report_title">بلّغ عن %s</string>
<string name="report_choose_reason">ما المُشكِلَةُ فِي هَذَا المَنشُور؟</string>
<string name="report_choose_reason_account">ما المُشكِلَة مَعَ %s؟</string>
<string name="report_choose_reason_subtitle">اختر أفضل تطابق</string>
<string name="report_reason_personal">لا يعجبني</string>
<string name="report_reason_personal_subtitle">ألا ترغب برؤيته</string>
<string name="report_reason_spam">إنه غير مرغوب فيه</string>
<string name="report_reason_spam_subtitle">روابط خبيثة أو تفاعل كاذب أو ردود متكررة</string>
<string name="report_reason_violation">ينتهك قواعد الخادم</string>
<string name="report_reason_violation_subtitle">تعلم أنه ينتهك قواعد محددة</string>
<string name="report_reason_other">إنَّهُ شَيءٌ آخَر</string>
<string name="report_reason_other_subtitle">لا تندرج هذه المشكلة ضمن فئات أخرى</string>
<string name="report_choose_rule">ما هي القواعد المنتهكة؟</string>
<string name="report_choose_rule_subtitle">اختر كل ما ينطبق</string>
@@ -168,8 +192,14 @@
<string name="report_comment_title">هل لديك شيء آخر لتخبرنا به؟</string>
<string name="report_comment_hint">تعليقات إضافية</string>
<string name="sending_report">يرسل البلاغ…</string>
<string name="report_sent_title">شُكرًا لَكَ على الإبلاغ، سَوفَ نتحرى عن الأمر.</string>
<string name="report_sent_subtitle">في أثناء مراجعتنا للبلاغ، يمكنك اتخاذ إجراء ضد %s:</string>
<string name="unfollow_user">ألغ متابعة %s</string>
<string name="unfollow">ألغ المتابعة</string>
<string name="mute_user_explain">لن ترى مشاركاتهم. لكن لا يزال بإمكانهم متابعتك ورؤية مشاركاتك ولن يعرفوا أنه تم كتم صوتها.</string>
<string name="block_user_explain">لن ترى مشاركاتهم. ولن يتمكنوا من رؤية مشاركاتك أو متابعتك. سيكونون قادرين على معرفة أنهم محظورون.</string>
<string name="report_personal_title">لا تريد أن ترى هذا؟</string>
<string name="report_personal_subtitle">فيما يلي خياراتك للتحكم بما يُعرَض عليك في ماستدون:</string>
<string name="back">العودة</string>
<string name="search_communities">اسم الخادم أو عنوان URL</string>
<string name="instance_rules_title">قواعد الخادم</string>
@@ -196,23 +226,39 @@
<string name="category_tech">تقني</string>
<string name="confirm_email_title">تحقق من صندوق الوارد الخاص بك</string>
<!-- %s is the email address -->
<string name="confirm_email_subtitle">اضغط على الرابط الذي أرسلناه إليك للتحقق من %s. سننتظر هنا.</string>
<string name="confirm_email_didnt_get">ألم تحصل على رابط؟</string>
<string name="resend">أعد الإرسال</string>
<string name="open_email_app">افتح تطبيق البريد الإلكتروني</string>
<string name="resent_email">أُرسلت رسالة التأكيد</string>
<string name="compose_hint">عَبِّر عَمَّ يَجُولُ فِي ذِهنِك</string>
<string name="content_warning">تحذير من المحتوى</string>
<string name="save">احفظ</string>
<string name="add_alt_text">أضف نصًا بديلًا</string>
<string name="visibility_public">علني</string>
<string name="visibility_followers_only">للمُتابِعينَ فقط</string>
<string name="visibility_private">للمشار إليهم فقط</string>
<string name="recent_searches">الحديثة</string>
<string name="skip">تخطى</string>
<string name="notification_type_follow">متابعُون جُدُد</string>
<string name="notification_type_favorite">المفضلة</string>
<string name="notification_type_mention">الذِكر</string>
<string name="notification_type_reblog">المشاركات</string>
<string name="notification_type_mention">الإشارات</string>
<string name="notification_type_poll">استطلاع رأي</string>
<string name="choose_account">اختر حسابًا</string>
<string name="err_not_logged_in">يرجى تسجيل الدخول إلى حساب ماستدون أولًا</string>
<plurals name="cant_add_more_than_x_attachments">
<item quantity="zero">لا يمكن إرفاق أكثر من ملف واحد</item>
<item quantity="one">لا يمكن إرفاق أكثر من ملف واحد</item>
<item quantity="two">لا يمكنك إرفاق أكثر من %d ملفين</item>
<item quantity="few">لا يمكن إرفاق أكثر من %d ملفات</item>
<item quantity="many">لا يمكن إرفاق أكثر من %d ملفات</item>
<item quantity="other">لا يمكن إرفاق أكثر من %d ملف</item>
</plurals>
<string name="media_attachment_unsupported_type">نوع الملف %s غير مدعوم</string>
<string name="media_attachment_too_big">الملف %1$s يتجاوز حدّ %2$s مب</string>
<string name="settings_theme">المظهر</string>
<string name="theme_auto">استخدام مظهر الجهاز</string>
<string name="theme_light">فاتح</string>
<string name="theme_dark">داكن</string>
<string name="settings_behavior">السلوك</string>
@@ -225,9 +271,13 @@
<string name="settings_clear_cache">امسح التخزين المؤقت للوسائط</string>
<string name="settings_app_version">تطبيق ماستدون لأندرويد نسخة %1$s (%2$d)</string>
<string name="media_cache_cleared">مُسح التخزين المؤقت للوسائط</string>
<string name="confirm_log_out">تسجيل الخروج من %s؟</string>
<string name="sensitive_content_explain">وصف المؤلف هذه الوسائط بأنها حساسة.</string>
<string name="avatar_description">انتقل إلى الصفحة الشخصية لـ %s</string>
<string name="more_options">مزيد من الخيارات</string>
<string name="new_post">منشور جديد</string>
<string name="button_reply">ردّ</string>
<string name="button_reblog">شارك</string>
<string name="button_favorite">فضّل</string>
<string name="button_share">شارك</string>
<string name="media_no_description">وسائط بدون وصف</string>
@@ -239,7 +289,11 @@
<string name="media_viewer">عارض الوسائط</string>
<string name="follow_user">تابع %s</string>
<string name="unfollowed_user">ألغ متابعة %s</string>
<string name="followed_user">أنت تتابع الآن %s</string>
<string name="following_user_requested">طَلَبَ %s مُتابَعتك</string>
<string name="open_in_browser">افتح في المتصفح</string>
<string name="hide_boosts_from_user">اخف مشاركات %s</string>
<string name="show_boosts_from_user">أظهر مشاركات %s</string>
<string name="signup_reason">لماذا تريد الانضمام؟</string>
<string name="signup_reason_note">هذا سوف يساعدنا في مراجعة تطبيقك.</string>
<string name="clear">امسح</string>
@@ -253,7 +307,13 @@
<string name="error_saving_file">خطأ أثناء حفظ الملف</string>
<string name="file_saved">حُفظ الملف</string>
<string name="downloading">ينزّل…</string>
<string name="no_app_to_handle_action">لا يوجد تطبيق لمعالجة هذا الإجراء</string>
<string name="local_timeline">المحلي</string>
<string name="trending_posts_info_banner">هذه هي المشاركات التي تكتسب شعبية عبر ماستدون.</string>
<string name="trending_links_info_banner">هذه هي القصص الإخبارية التي يُتحدّث عنها على ماستدون.</string>
<!-- %s is the server domain -->
<string name="local_timeline_info_banner">هذه هي جميع المشاركات من جميع المستخدمين في الخادم الخاص بك (%s).</string>
<string name="recommended_accounts_info_banner">قد تعجبك هذه الحسابات استنادا إلى حسابات أخرى تتابعها.</string>
<string name="see_new_posts">استعرض المنشورات الجديدة</string>
<string name="load_missing_posts">حمّل المَنشورات المَفقودَة</string>
<string name="follow_back">رُدّ المتابعة</string>
@@ -285,6 +345,14 @@
<item quantity="many">%,d تفضيلًا</item>
<item quantity="other">%,d تفضيل</item>
</plurals>
<plurals name="x_reblogs">
<item quantity="zero">%,d إعادة نشر</item>
<item quantity="one">إعادة نشر واحدة</item>
<item quantity="two">أعيد نشره مرّتان</item>
<item quantity="few">أعيد نشره %,d مرة</item>
<item quantity="many">أعيد نشره %,d مرات</item>
<item quantity="other">أعيد نشره %,d مرات</item>
</plurals>
<string name="timestamp_via_app">%1$s عبر %2$s</string>
<string name="time_now">الآن</string>
<string name="edit_history">تاريخ التعديل</string>
@@ -347,6 +415,7 @@
<string name="login_title">مرحبا بك مجددًا</string>
<string name="login_subtitle">قم بتسجيل الدخول باستخدام الخادم حيث قمتَ بإنشاء حسابك فيه.</string>
<string name="server_url">رابط الخادم</string>
<string name="signup_random_server_explain">سوف نختار خادماً بناءً على لغتك إذا قمت بالمتابعة دون إجراء إختيار.</string>
<string name="server_filter_any_language">أي لغة</string>
<string name="server_filter_instant_signup">تسجيل فوري</string>
<string name="server_filter_manual_review">مراجعة يدوية</string>
@@ -359,6 +428,7 @@
<string name="server_filter_region_oceania">أوقيانوسيا</string>
<string name="not_accepting_new_members">لا يقبل استقبال أعضاء جدد</string>
<string name="category_special_interests">المصالح الخاصة</string>
<string name="signup_passwords_dont_match">كلمات المرور غير متطابقة</string>
<string name="pick_server_for_me">اختر لي</string>
<string name="profile_add_row">إضافة صف</string>
<string name="profile_setup">إعداد الملف الشخصي</string>
@@ -367,21 +437,265 @@
<string name="popular_on_mastodon">مشهور على ماستدون</string>
<string name="follow_all">اتبع الكل</string>
<string name="server_rules_disagree">لا أوافق</string>
<string name="privacy_policy_explanation">بالمختصر: نحن لا نجمع أو نعالج أي شيء.</string>
<!-- %s is server domain -->
<string name="server_policy_disagree">لا أوافق %s</string>
<string name="profile_bio">نبذة عنك</string>
<!-- Shown in a progress dialog when you tap "follow all" -->
<string name="sending_follows">متابعة المستخدمين…</string>
<!-- %1$s is server domain, %2$s is email domain. You can reorder these placeholders to fit your language better. -->
<string name="signup_email_domain_blocked">%1$s لا يسمح بالاشتراكات من %2$s. جرب خادما واحدا أو &lt;a&gt;اختر خادما مختلفا&lt;/a&gt;.</string>
<string name="spoiler_show">إظهاره على أي حال</string>
<string name="spoiler_hide">إعادة الإخفاء</string>
<string name="poll_multiple_choice">اختر واحدا أو أكثر</string>
<string name="save_changes">حفظ التغييرات</string>
<string name="profile_featured">المميزة</string>
<string name="profile_timeline">الخيط</string>
<string name="view_all">عرض الكل</string>
<string name="profile_endorsed_accounts">الحسابات</string>
<string name="verified_link">رابط متحقق منه</string>
<string name="show">إظهار</string>
<string name="hide">إخفاء</string>
<string name="join_default_server">الانضمام إلى %s</string>
<string name="pick_server">اختر خادما آخر</string>
<string name="signup_or_login">أو</string>
<string name="learn_more">تعلم المزيد</string>
<string name="welcome_to_mastodon">أهلًا بك على ماستدون</string>
<string name="welcome_paragraph1">ماستدون شبكة اجتماعية لامركزية، بمعنى أنه ليس هناك شركة واحدة تتحكم فيها. وهي تتألف من العديد من الخوادم التي تدار بشكل مستقل، وجميعها متصلة معا.</string>
<string name="what_are_servers">ما هي الخوادم؟</string>
<string name="welcome_paragraph2"><![CDATA[تتم استضافة كل حساب ماستدون على خادم - ولكل منها قيمه وقواعده ومسؤوليه الخاصين. بغض النظر عن الشخص الذي تختاره ، يمكنك متابعة الأشخاص والتفاعل معهم على أي خادم.]]></string>
<string name="opening_link">رابط الافتتاح…</string>
<string name="link_not_supported">هذا الرابط غير مدعوم في التطبيق</string>
<string name="log_out_all_accounts">تسجيل الخروج من جميع الحسابات</string>
<string name="confirm_log_out_all_accounts">أتريد تسجيل الخروج من جميع الحسابات؟</string>
<string name="retry">حاول مجددًا</string>
<string name="post_failed">أخفق في الإرسال</string>
<!-- %s is formatted file size ("467 KB image") -->
<string name="attachment_description_image">صورة %s</string>
<string name="attachment_description_video">فيديو %s</string>
<string name="attachment_description_audio">مقطع صوتي %s</string>
<string name="attachment_description_unknown">ملف %s</string>
<string name="attachment_type_image">صورة</string>
<string name="attachment_type_video">فيديو</string>
<string name="attachment_type_audio">مقطع صوتي</string>
<string name="attachment_type_gif">GIF</string>
<string name="attachment_type_unknown">ملف</string>
<string name="attachment_x_percent_uploaded">%d%% تم الرفع</string>
<string name="add_poll_option">إضافة خيار للاستطلاع</string>
<string name="poll_length">مدة الاستطلاع</string>
<string name="poll_style">النوع</string>
<string name="compose_poll_single_choice">اختر واحدا</string>
<string name="compose_poll_multiple_choice">خيارات متعددة</string>
<string name="delete_poll_option">حذف خيار من الاستطلاع</string>
<string name="poll_style_title">نمط الاستطلاع</string>
<string name="alt_text">نص بديل</string>
<string name="help">المساعدة</string>
<string name="what_is_alt_text">ما هو النص البديل؟</string>
<string name="alt_text_help">يوفر النص البديل أوصافا للصور للأشخاص الذين يعانون من إعاقات بصرية أو اتصالات ذات نطاق ترددي منخفض أو أولئك الذين يبحثون عن سياق إضافي.\n\nيمكنك تحسين إمكانية الوصول والفهم للجميع من خلال كتابة نص بديل واضح وموجز وموضوعي.\n\n التقاط العناصر المهمة\n<ul><li>تلخيص النص في الصور</li>\n<li>استخدام بنية الجملة العادية</li>\n<li>تجنب المعلومات الزائدة</li>\n<li>التركيز على الاتجاهات والنتائج الرئيسية في العناصر المرئية المعقدة (مثل الرسوم البيانية أو الخرائط)</li><li></li></ul></string>
<string name="edit_post">تعديل المنشور</string>
<string name="no_verified_link">لم يتم التحقق من الرابط</string>
<string name="compose_autocomplete_emoji_empty">تصفح الرموز التعبيرية</string>
<string name="compose_autocomplete_users_empty">العثور على الأشخاص الذين تبحث عنهم</string>
<string name="no_search_results">تعذر العثور على أي نتائج لمصطلحات البحث هذه</string>
<string name="language">اللغة</string>
<string name="language_default">الافتراضية</string>
<string name="language_system">النظام</string>
<string name="language_detecting">اكتشاف اللغة</string>
<string name="language_cant_detect">تعذر اكتشاف اللغة</string>
<string name="language_detected">الكشف عن</string>
<string name="media_hidden">وسائط مخفية</string>
<string name="post_hidden">منشور مخفي</string>
<string name="report_title_post">الإبلاغ عن المنشور</string>
<string name="forward_report_explanation">الحساب من خادم آخر. هل تودّ إرسال نسخة مجهولة المصدر من هذا التقرير هناك أيضا؟</string>
<!-- %s is the server domain -->
<string name="forward_report_to_server">تحويله إلى %s</string>
<!-- Shown on the "stamp" on the screen that appears after you report a post/user. Please keep the translation short, preferably a single word -->
<string name="reported">تم الإبلاغ عنه</string>
<string name="report_unfollow_explanation">لعدم رؤية مشاركاتهم في خلاصة ملخصك بعد الآن، ألغِ متابعتهم.</string>
<string name="muted_user">كتم %s</string>
<string name="report_sent_already_blocked">لقد حظرت هذا المستخدم من قبل، لذلك لا يوجد شيء آخر عليك القيام به خلال مراجعة بلاغك.</string>
<string name="report_personal_already_blocked">لقد قمت بالفعل بحظر هذا المستخدم، لذلك لا يوجد شيء آخر عليك القيام به.\n\nشكرا للمساعدة في الحفاظ على ماستدون مكانا آمنا للجميع!</string>
<string name="blocked_user">حظر %s</string>
<string name="mark_all_notifications_read">اعتبار الكل كمقروء</string>
<string name="settings_display">الشاشة</string>
<string name="settings_filters">عوامل التصفية</string>
<string name="settings_server_explanation">نظرة عامة وقواعد ومشرفين</string>
<!-- %s is the app name (Mastodon, key app_name). I made it a placeholder so everything Just Works™ with forks -->
<string name="about_app">عن %s</string>
<string name="default_post_language">اللغة الافتراضية للمنشور</string>
<string name="settings_alt_text_reminders">إضافة تذكير بالنصوص البديلة</string>
<string name="settings_confirm_unfollow">السؤال قبل إلغاء متابعة شخص ما</string>
<string name="settings_confirm_boost">اسأل قبل إعادة النشر</string>
<string name="settings_confirm_delete_post">السؤال قبل حذف المشاركات</string>
<string name="pause_all_notifications">إيقاف الكل</string>
<string name="pause_notifications_off">إيقاف</string>
<string name="notifications_policy_anyone">أيا كان</string>
<string name="notifications_policy_followed">الأشخاص الذين تتابعهم</string>
<string name="notifications_policy_follower">الأشخاص الذين تتابعهم</string>
<string name="notifications_policy_no_one">لا أحد</string>
<string name="settings_notifications_policy">تلقي الإشعارات من</string>
<string name="notification_type_mentions_and_replies">الإشارات والردود</string>
<string name="pause_all_notifications_title">إيقاف جميع الإشعارات مؤقتًا</string>
<plurals name="x_weeks">
<item quantity="zero">%d أسبوع</item>
<item quantity="one">أسبوع واحد</item>
<item quantity="two">أسبوعان</item>
<item quantity="few">%d أسابيع</item>
<item quantity="many">%d أسبوعًا</item>
<item quantity="other">%d أسابيع</item>
</plurals>
<!-- %1$s is the date (may be relative, e.g. "today" or "yesterday"), %2$s is the time. You can reorder these placeholders if that works better for your language -->
<string name="date_at_time">%1$s في %2$s</string>
<string name="today">اليوم</string>
<string name="yesterday">أمس</string>
<string name="tomorrow">غدًا</string>
<!-- %s is the timestamp ("tomorrow at 12:34") -->
<string name="pause_notifications_ends">ينتهي %s</string>
<!-- %s is the timestamp ("tomorrow at 12:34") -->
<string name="pause_notifications_banner">سيتم استئناف الإشعارات %s.</string>
<string name="resume_notifications_now">استأنف الآن</string>
<string name="open_system_notification_settings">الانتقال إلى إعدادات الإشعارات</string>
<string name="about_server">عن</string>
<string name="server_rules">القواعد</string>
<string name="server_administrator">المدير</string>
<string name="send_email_to_server_admin">للاتصال بالمدير</string>
<string name="notifications_disabled_in_system">شغل الإشعارات من إعدادات جهازك لرؤية التحديثات من أي مكان.</string>
<string name="settings_even_more">المزيد من الإعدادات</string>
<string name="settings_show_cws">إظهار تحذيرات المحتوى</string>
<string name="settings_hide_sensitive_media">فلرتة الوسائط التي تم وضع علامة عليها على أنها حساسة</string>
<string name="settings_show_interaction_counts">عدد التفاعل مع المنشورات</string>
<string name="settings_show_emoji_in_names">رموز تعبيرية مخصصة في أسماء العرض</string>
<plurals name="in_x_seconds">
<item quantity="zero">في %d ثانية</item>
<item quantity="one">في ثانية واحدة</item>
<item quantity="two">في ثانيتين</item>
<item quantity="few">في %d ثوانٍ</item>
<item quantity="many">في %d ثانية</item>
<item quantity="other">في %d ثوان</item>
</plurals>
<plurals name="in_x_minutes">
<item quantity="zero">في %d دقيقة</item>
<item quantity="one">في دقيقة واحدة</item>
<item quantity="two">في دقيقتين</item>
<item quantity="few">في %d دقائق</item>
<item quantity="many">في %d دقيقة</item>
<item quantity="other">في %d دقائق</item>
</plurals>
<plurals name="in_x_hours">
<item quantity="zero">في %d ساعة</item>
<item quantity="one">خلال ساعة واحدة</item>
<item quantity="two">خلال ساعتان</item>
<item quantity="few">خلال %d ساعات</item>
<item quantity="many">خلال %d ساعة</item>
<item quantity="other">خلال %d ساعات</item>
</plurals>
<plurals name="x_hours_ago">
<item quantity="zero">منذ %d ساعات</item>
<item quantity="one">منذ ساعة واحدة</item>
<item quantity="two">منذ ساعتان</item>
<item quantity="few">منذ %d ساعات</item>
<item quantity="many">منذ %d ساعة</item>
<item quantity="other">منذ %d ساعات</item>
</plurals>
<string name="alt_text_reminder_title">تفتقد الوسائط إلى نص بديل</string>
<plurals name="alt_text_reminder_x_images">
<item quantity="zero">%s من صورك يفتقر إلى نص بديل. أتردد النشر على أي حال؟</item>
<item quantity="one">%s من صورك يفتقر إلى نص بديل. أتردد النشر على أي حال؟</item>
<item quantity="two">%s من صورك يفتقر إلى نص بديل. أتردد النشر على أي حال؟</item>
<item quantity="few">%s من صورك يفتقر إلى نص بديل. أتردد النشر على أي حال؟</item>
<item quantity="many">%s من صورك يفتقر إلى نص بديل. أتردد النشر على أي حال؟</item>
<item quantity="other">%s من صورك يفتقر إلى نص بديل. أتردد النشر على أي حال؟</item>
</plurals>
<plurals name="alt_text_reminder_x_attachments">
<item quantity="zero">%s من مرفقات الوسائط الخاصة بك يفتقر لنص بديل. نشر على أي حال؟</item>
<item quantity="one">%s من مرفقات الوسائط الخاصة بك يفتقر لنص بديل. نشر على أي حال؟</item>
<item quantity="two">%s من مرفقات الوسائط الخاصة بك يفتقر لنص بديل. نشر على أي حال؟</item>
<item quantity="few">%s من مرفقات الوسائط الخاصة بك يفتقر لنص بديل. نشر على أي حال؟</item>
<item quantity="many">%s من مرفقات الوسائط الخاصة بك يفتقر لنص بديل. نشر على أي حال؟</item>
<item quantity="other">%s من مرفقات الوسائط الخاصة بك يفتقر لنص بديل. نشر على أي حال؟</item>
</plurals>
<string name="count_one">واحد</string>
<string name="count_two">اثنان</string>
<string name="count_three">ثلاثة</string>
<string name="count_four">أربعة</string>
<string name="alt_text_reminder_post_anyway">مَنشور</string>
<!-- %s is the username -->
<string name="unfollow_confirmation">أتريد إلغاء متابعة %s؟</string>
<string name="filter_active">نشِط</string>
<string name="filter_inactive">خامل</string>
<string name="settings_add_filter">إضافة عامل تصفية</string>
<string name="settings_edit_filter">تعديل عامل التصفية</string>
<string name="settings_filter_duration">المدة</string>
<string name="settings_filter_muted_words">الكلمات المحظورة</string>
<string name="settings_filter_context">كتم الصوت من</string>
<string name="settings_filter_show_cw">عرض مع تحذير المحتوى</string>
<string name="settings_filter_show_cw_explanation">الاستمرار في عرض المشاركات التي تطابق هذا الفلتر، ولكن خلف تحذير حول المحتوى</string>
<string name="settings_delete_filter">حذف عامل التصفية</string>
<string name="filter_duration_forever">إلى الأبد</string>
<!-- %s is the timestamp ("tomorrow at 12:34") -->
<string name="settings_filter_ends">ينتهي %s</string>
<plurals name="settings_x_muted_words">
<item quantity="zero">%d كلمة أو عبارة مكتومة</item>
<item quantity="one">%d كلمة أو عبارة مكتومة</item>
<item quantity="two">%d كلمتان أو عبارتان مكتومتان</item>
<item quantity="few">%d كلمة أو عبارة مكتومة</item>
<item quantity="many">%d كلمة أو عبارة مكتومة</item>
<item quantity="other">%d كلمة أو عبارة مكتومة</item>
</plurals>
<string name="selection_2_options">%1$s و %2$s</string>
<string name="selection_3_options">%1$s و %2$s و %3$s</string>
<string name="selection_4_or_more">%1$s, %2$s, و %3$d والمزيد</string>
<string name="filter_context_home_lists">الخيط الزمني الرئيسي والقوائم</string>
<string name="filter_context_notifications">الإشعارات</string>
<string name="filter_context_public_timelines">الخيوط الزمنية العامة</string>
<string name="filter_context_threads_replies">سلاسل المحادثات والردود</string>
<string name="filter_context_profiles">الصفحات التعريفية</string>
<string name="settings_filter_title">العنوان</string>
<string name="settings_delete_filter_title">حذف عامل التصفية \"%s\"؟</string>
<string name="settings_delete_filter_confirmation">سيتم حذف هذا الفلتر من حسابك على جميع الأجهزة.</string>
<string name="add_muted_word">إضافة كلمة مكتومة</string>
<string name="edit_muted_word">تحرير كلمة مكتومة</string>
<string name="add">إضافة</string>
<string name="filter_word_or_phrase">كلمة أو عبارة</string>
<string name="filter_add_word_help">الكلمات غير حساسة لحالة الأحرف وتتطابق مع الكلمات الكاملة فقط.\n\nإذا قمت بتصفية الكلمة الرئيسية \"Apple\" ، فستخفي المشاركات التي تحتوي على \"apple\" أو \"aPpLe\" ولكن ليس \"pineapple.\"</string>
<string name="settings_delete_filter_word">حذف الكلمة \"%s\"؟</string>
<string name="enter_selection_mode">اختر</string>
<string name="select_all">اختيار الكل</string>
<string name="settings_filter_duration_title">مدة التصفية</string>
<string name="filter_duration_custom">مخصص</string>
<plurals name="settings_delete_x_filter_words">
<item quantity="zero">حذف %d كلمات؟</item>
<item quantity="one">حذف كلمة واحدة؟</item>
<item quantity="two">حذف كلمتان؟</item>
<item quantity="few">حذف %d كلمات؟</item>
<item quantity="many">حذف %d كلمة؟</item>
<item quantity="other">حذف %d كلمات؟</item>
</plurals>
<plurals name="x_items_selected">
<item quantity="zero">تم تحديد %d</item>
<item quantity="one">تم تحديد %d</item>
<item quantity="two">%d تم تحديدها</item>
<item quantity="few">%d تم تحديدها</item>
<item quantity="many">%d تم تحديدها</item>
<item quantity="other">%d تم تحديدها</item>
</plurals>
<string name="required_form_field_blank">لا يمكن أن يكون فارغاً</string>
<string name="filter_word_already_in_list">موجود بالفعل في القائمة</string>
<string name="app_update_ready">تحديث التطبيق جاهز</string>
<string name="app_update_version">الإصدار %s</string>
<string name="downloading_update">جارٍ التنزيل (%d%%)</string>
<!-- Shown like a content warning, %s is the name of the filter -->
<string name="post_matches_filter_x">تطابق عامل التصفية \"%s\"</string>
<string name="search_mastodon">البحث في ماستدون</string>
<string name="clear_all">امسح الكل</string>
<string name="search_open_url">فتح الرابط التشعبي في ماستدون</string>
<string name="posts_matching_hashtag">منشورات تحتوي على “%s”</string>
<string name="search_go_to_account">الانتقال إلى %s</string>
<string name="posts_matching_string">منشورات تحتوي على “%s”</string>
<string name="accounts_matching_string">أشخاص لديهم \"%s\"</string>
<!-- Shown in the post header. Please keep it short -->
<string name="time_seconds_ago_short">مُنذُ %dث</string>
<string name="time_minutes_ago_short">مُنذُ %dش</string>
<string name="time_hours_ago_short">مُنذُ %dس</string>
<string name="time_days_ago_short">مُنذُ %dي</string>
</resources>

View File

@@ -7,8 +7,11 @@
<string name="preparing_auth">প্রমাণীকরণের জন্য প্রস্তুত হচ্ছে...</string>
<string name="in_reply_to">%s কে উত্তর দিন</string>
<string name="notifications">নোটিফিকেশন</string>
<string name="user_followed_you">%s আপনাকে ফলো করেছেন</string>
<string name="user_sent_follow_request">%s আপনাকে ফলো করার অনুরোধ পাঠিয়েছেন</string>
<string name="share_toot_title">শেয়ার করুন</string>
<string name="settings">সেটিংস</string>
<string name="discard">বাতিল করুন</string>
<string name="cancel">বাতিল করুন</string>
<plurals name="followers">
<item quantity="one">জন ফলোয়ার</item>
@@ -92,9 +95,18 @@
<item quantity="one">%d জন ব্যক্তি বলছেন</item>
<item quantity="other">%d jon ব্যক্তিরা বলছেন</item>
</plurals>
<string name="report_title">%s -এর নামে অভিযোগ করুন</string>
<string name="report_choose_reason">এই পোস্টে ভুল কি?</string>
<string name="report_reason_personal">আমার এটি ভালো লাগছে না</string>
<string name="report_reason_spam">এটি স্প্যাম</string>
<string name="report_reason_violation">এটি সার্ভারের নিয়ম ভাঙছে</string>
<string name="report_reason_other">এটি অন্য কিছু</string>
<string name="report_choose_rule">কোন নিয়মটা ভেঙেছে?</string>
<string name="sending_report">রিপোর্ট পাঠানো হচ্ছে…</string>
<string name="report_sent_title">রিপোর্ট করার জন্য আপনাকে ধন্যবাদ, আমরা এটি শীঘ্রই দেখব.</string>
<string name="report_sent_subtitle">আমরা যতক্ষণে আপনার রিপোর্ট পুনর্বিবেচনা করছি, আপনি %s এর বিরুদ্ধে ব্যবস্থা নিতে পারেন:</string>
<string name="unfollow_user">%s -কে আনফলো করুন</string>
<string name="unfollow">আনফলো করুন</string>
<string name="report_personal_title">আপনি এটি আর দেখতে চান না?</string>
<string name="back">ফিরে যান</string>
<string name="search_communities">সার্ভারের নাম বা লিঙ্ক</string>

View File

@@ -105,6 +105,12 @@
<item quantity="many">Zbývá %d dní</item>
<item quantity="other">Zbývá %d dní</item>
</plurals>
<plurals name="x_votes">
<item quantity="one">%,d hlas</item>
<item quantity="few">%,d hlasující</item>
<item quantity="many">%,d hlasujících</item>
<item quantity="other">%,d hlasů</item>
</plurals>
<string name="poll_closed">Uzavřeno</string>
<string name="confirm_mute_title">Skrýt účet</string>
<string name="confirm_mute">Potvrdit skrytí %s</string>
@@ -217,6 +223,12 @@
<string name="notification_type_poll">Ankety</string>
<string name="choose_account">Vybrat účet</string>
<string name="err_not_logged_in">Nejprve se přihlaste do Mastodonu</string>
<plurals name="cant_add_more_than_x_attachments">
<item quantity="one">Nelze přidat více než %d multimediální přílohu</item>
<item quantity="few">Nelze přidat více než %d multimediální přílohy</item>
<item quantity="many">Nelze přidat více než %d multimediálních příloh</item>
<item quantity="other">Nelze přidat více než %d multimediálních příloh</item>
</plurals>
<string name="media_attachment_unsupported_type">Soubor %s nepatří mezi podporované typy</string>
<string name="media_attachment_too_big">Soubor %1$s překračuje limit velikosti %2$s MB</string>
<string name="settings_theme">Vzhled</string>
@@ -301,6 +313,12 @@
<item quantity="many">%,d oblíbení</item>
<item quantity="other">%,d oblíbení</item>
</plurals>
<plurals name="x_reblogs">
<item quantity="one">%,d boost</item>
<item quantity="few">%,d boosty</item>
<item quantity="many">%,d boostů</item>
<item quantity="other">%,d boostů</item>
</plurals>
<string name="timestamp_via_app">%1$s přes %2$s</string>
<string name="time_now">teď</string>
<string name="edit_history">Historie úprav</string>
@@ -480,6 +498,12 @@
<string name="settings_notifications_policy">Dostávat oznámení od</string>
<string name="notification_type_mentions_and_replies">Zmínění a odpovědi</string>
<string name="pause_all_notifications_title">Pozastavit všechna oznámení</string>
<plurals name="x_weeks">
<item quantity="one">%d týden</item>
<item quantity="few">%d týdny</item>
<item quantity="many">%d týdnů</item>
<item quantity="other">%d týdnů</item>
</plurals>
<!-- %1$s is the date (may be relative, e.g. "today" or "yesterday"), %2$s is the time. You can reorder these placeholders if that works better for your language -->
<string name="date_at_time">%1$s v %2$s</string>
<string name="today">dnes</string>
@@ -501,7 +525,43 @@
<string name="settings_hide_sensitive_media">Zakrýt média označená jako citlivá</string>
<string name="settings_show_interaction_counts">Počet interakcí příspěvku</string>
<string name="settings_show_emoji_in_names">Vlastní emoji v zobrazených jménech</string>
<plurals name="in_x_seconds">
<item quantity="one">za %d vteřinu</item>
<item quantity="few">za %d vteřiny</item>
<item quantity="many">za %d vteřin</item>
<item quantity="other">za %d vteřin</item>
</plurals>
<plurals name="in_x_minutes">
<item quantity="one">za %d minutu</item>
<item quantity="few">za %d minuty</item>
<item quantity="many">za %d minut</item>
<item quantity="other">za %d minut</item>
</plurals>
<plurals name="in_x_hours">
<item quantity="one">za %d hodinu</item>
<item quantity="few">za %d hodiny</item>
<item quantity="many">za %d hodin</item>
<item quantity="other">za %d hodin</item>
</plurals>
<plurals name="x_hours_ago">
<item quantity="one">před %d hodinou</item>
<item quantity="few">před %d hodinami</item>
<item quantity="many">před %d hodinami</item>
<item quantity="other">před %d hodinami</item>
</plurals>
<string name="alt_text_reminder_title">Média nemají alternativní text</string>
<plurals name="alt_text_reminder_x_images">
<item quantity="one">%s z vašich obrázků nemá alternativní text. Přesto odeslat?</item>
<item quantity="few">%s z vašich obrázků nemají alternativní text. Přesto odeslat?</item>
<item quantity="many">%s z vašich obrázků nemá alternativní text. Přesto odeslat?</item>
<item quantity="other">%s z vašich obrázků nemá alternativní text. Přesto odeslat?</item>
</plurals>
<plurals name="alt_text_reminder_x_attachments">
<item quantity="one">%s z vašich multimediálních příloh nemá alternativní text. Přesto odeslat?</item>
<item quantity="few">%s z vašich multimediálních příloh nemají alternativní text. Přesto odeslat?</item>
<item quantity="many">%s z vašich multimediálních příloh nemá alternativní text. Přesto odeslat?</item>
<item quantity="other">%s z vašich multimediálních příloh nemá alternativní text. Přesto odeslat?</item>
</plurals>
<string name="count_one">Jeden</string>
<string name="count_two">Dva</string>
<string name="count_three">Tři</string>
@@ -522,6 +582,12 @@
<string name="filter_duration_forever">Napořád</string>
<!-- %s is the timestamp ("tomorrow at 12:34") -->
<string name="settings_filter_ends">Končí %s</string>
<plurals name="settings_x_muted_words">
<item quantity="one">%d ztišené slovo nebo fráze</item>
<item quantity="few">%d ztišená slova nebo fráze</item>
<item quantity="many">%d ztišených slov nebo frází</item>
<item quantity="other">%d ztišených slov nebo fráze</item>
</plurals>
<string name="selection_2_options">%1$s a %2$s</string>
<string name="selection_3_options">%1$s, %2$s a %3$s</string>
<string name="selection_4_or_more">%1$s, %2$s a %3$d další</string>
@@ -543,6 +609,18 @@
<string name="select_all">Vybrat vše</string>
<string name="settings_filter_duration_title">Doba použití filtru</string>
<string name="filter_duration_custom">Vlastní</string>
<plurals name="settings_delete_x_filter_words">
<item quantity="one">Smazat %d slovo?</item>
<item quantity="few">Smazat %d slova?</item>
<item quantity="many">Smazat %d slov?</item>
<item quantity="other">Smazat %d slov?</item>
</plurals>
<plurals name="x_items_selected">
<item quantity="one">%d vybráno</item>
<item quantity="few">%d vybrány</item>
<item quantity="many">%d vybraných</item>
<item quantity="other">%d vybraných</item>
</plurals>
<string name="required_form_field_blank">Nemůže být prázdné</string>
<string name="filter_word_already_in_list">Již v seznamu</string>
<string name="app_update_ready">Je připravena aktualizace aplikace</string>

View File

@@ -4,18 +4,24 @@
<string name="next">Næste</string>
<string name="loading_instance">Henter serverinfo…</string>
<string name="error">Fejl</string>
<string name="ok">Ok</string>
<string name="not_a_mastodon_instance">%s lader ikke til at være en Mastodon-server.</string>
<string name="ok">OK</string>
<string name="preparing_auth">Forbereder godkendelse…</string>
<string name="finishing_auth">Afslutter godkendelse…</string>
<string name="user_boosted">%s fremhævede</string>
<string name="in_reply_to">Som svar til %s</string>
<string name="notifications">Meddelelser</string>
<string name="notifications">Notifikationer</string>
<string name="user_followed_you">%s begyndte at følge dig</string>
<string name="user_sent_follow_request">%s har sendt dig en følgeanmodning</string>
<string name="user_favorited">%s favoritmarkerede dit indlæg</string>
<string name="notification_boosted">%s boostede dit indlæg</string>
<string name="poll_ended">Se resultaterne af en afstemning, hvori du deltog</string>
<string name="share_toot_title">Del</string>
<string name="settings">Indstillinger</string>
<string name="publish">Offentliggør</string>
<string name="publish">Publicér</string>
<string name="discard_draft">Kassér kladde?</string>
<string name="discard">Kassér</string>
<string name="cancel">Fortryd</string>
<string name="cancel">Afbryd</string>
<plurals name="followers">
<item quantity="one">følger</item>
<item quantity="other">følgere</item>
@@ -31,13 +37,14 @@
<string name="button_follow">Følg</string>
<string name="button_following">Følger</string>
<string name="edit_profile">Redigér profil</string>
<string name="mute_user">Skjul %s (mute)</string>
<string name="unmute_user">Vis %s igen (unmute)</string>
<string name="block_user">Bloker %s</string>
<string name="unblock_user">Fjern blokering af %s</string>
<string name="report_user">Indberet %s</string>
<string name="block_domain">Bloker %s</string>
<string name="unblock_domain">Fjern blokering af %s</string>
<string name="share_user">Del profil</string>
<string name="mute_user">Gør tavs %s</string>
<string name="unmute_user">Vis %s igen</string>
<string name="block_user">Blokér %s</string>
<string name="unblock_user">Afblokér %s</string>
<string name="report_user">Anmeld %s</string>
<string name="block_domain">Blokér %s</string>
<string name="unblock_domain">Afblokér %s</string>
<plurals name="x_posts">
<item quantity="one">%,d indlæg</item>
<item quantity="other">%,d indlæg</item>
@@ -45,7 +52,7 @@
<string name="profile_joined">Tilmeldt</string>
<string name="done">Udført</string>
<string name="loading">Indlæser…</string>
<string name="field_label">Mærkat</string>
<string name="field_label">Etiket</string>
<string name="field_content">Indhold</string>
<string name="saving">Gemmer…</string>
<string name="post_from_user">Indlæg fra %s</string>
@@ -78,66 +85,84 @@
<item quantity="one">%d dag tilbage</item>
<item quantity="other">%d dage tilbage</item>
</plurals>
<plurals name="x_votes">
<item quantity="one">%,d stemme</item>
<item quantity="other">%,d stemmer</item>
</plurals>
<string name="poll_closed">Lukket</string>
<string name="confirm_mute_title">Skjul konto</string>
<string name="confirm_mute">Bekræft at du vil skjule %s</string>
<string name="do_mute">Skjul (mute)</string>
<string name="confirm_unmute_title">Vis bruger igen (unmute)</string>
<string name="confirm_unmute">Bekræft at du vil se %s igen</string>
<string name="do_unmute">Vis igen (unmute)</string>
<string name="confirm_block_title">Bloker bruger</string>
<string name="confirm_block_domain_title">Bloker domæne</string>
<string name="confirm_block">Bekræft at du vil blokere %s</string>
<string name="do_block">Bloker</string>
<string name="confirm_unblock_title">Fjern blokering af bruger</string>
<string name="confirm_unblock_domain_title">Fjern blokering af domæne</string>
<string name="confirm_unblock">Bekræft at du vil fjerne blokering af %s</string>
<string name="do_unblock">Fjern blokering</string>
<string name="confirm_mute_title">Tavsgør konto</string>
<string name="confirm_mute">Bekræft tavsgørelse af %s</string>
<string name="do_mute">Tavsgør</string>
<string name="confirm_unmute_title">Vis konto igen</string>
<string name="confirm_unmute">Bekræft, at %s ikke længere er tavsgjort</string>
<string name="do_unmute">Ophæv tavsgørelse</string>
<string name="confirm_block_title">Blokér konto</string>
<string name="confirm_block_domain_title">Blokér domæne</string>
<string name="confirm_block">Bekræft blokering af %s</string>
<string name="do_block">Blokér</string>
<string name="confirm_unblock_title">Afblokér konto</string>
<string name="confirm_unblock_domain_title">Afblokér domæne</string>
<string name="confirm_unblock">Bekræft afblokeringen af %s</string>
<string name="do_unblock">Afblokér</string>
<string name="button_blocked">Blokeret</string>
<string name="action_vote">Stem</string>
<string name="delete">Slet</string>
<string name="confirm_delete">Er du sikker på, at du vil slette dette indlæg?</string>
<string name="confirm_delete_title">Slet indlæg</string>
<string name="confirm_delete">Sikker på, at dette indlæg skal slettes?</string>
<string name="deleting">Sletter…</string>
<string name="notification_channel_audio_player">Afspilning af lyd</string>
<string name="notification_channel_audio_player">Lydafspilning</string>
<string name="play">Afspil</string>
<string name="pause">Sæt på pause</string>
<string name="pause">Pausér</string>
<string name="log_out">Log ud</string>
<string name="add_account">Tilføj konto</string>
<string name="search_hint">Søg</string>
<string name="hashtags">Hashtags</string>
<string name="news">Nyheder</string>
<string name="for_you">Til dig</string>
<string name="all_notifications">Alt</string>
<string name="mentions">Omtaler</string>
<plurals name="x_people_talking">
<item quantity="one">%d person deltager</item>
<item quantity="other">%d personer deltager</item>
<item quantity="one">%d person taler</item>
<item quantity="other">%d personer taler</item>
</plurals>
<string name="report_title">Indberet %s</string>
<string name="report_choose_reason_subtitle">Vælg en passende grund</string>
<string name="report_title">Anmeld %s</string>
<string name="report_choose_reason">Hvad er der galt med dette indlæg?</string>
<string name="report_choose_reason_account">Hvad er der galt med %s?</string>
<string name="report_choose_reason_subtitle">Vælg bedste match</string>
<string name="report_reason_personal">Bryder mig ikke om det</string>
<string name="report_reason_personal_subtitle">Det er ikke noget, man ønsker at se</string>
<string name="report_reason_spam_subtitle">Ondsindede links, falske interaktioner, eller gentagne svar</string>
<string name="report_reason_violation">Det overtræder serverreglerne</string>
<string name="report_reason_violation_subtitle">Du kender til specifikke regler som det er i strid med</string>
<string name="report_reason_other_subtitle">Problemet passer ikke ind i andre kategorier</string>
<string name="report_reason_spam">Det er spam</string>
<string name="report_reason_spam_subtitle">Ondsindede links, falske interaktioner eller gentagne svar</string>
<string name="report_reason_violation">Det overtræder serverregler</string>
<string name="report_reason_violation_subtitle">Du er bekendt med, at det overtræder bestemte regler</string>
<string name="report_reason_other">Drejer sig om noget andet</string>
<string name="report_reason_other_subtitle">Ingen kategori modsvarer problematikken</string>
<string name="report_choose_rule">Hvilke regler overtrædes?</string>
<string name="report_choose_rule_subtitle">Vælg alle relevante</string>
<string name="report_choose_posts">Er der indlæg, som kan bekræfte denne anmeldelse?</string>
<string name="report_choose_posts">Er der indlæg, som understøtter denne anmeldelse?</string>
<string name="report_choose_posts_subtitle">Vælg alle relevante</string>
<string name="report_comment_title">Er der andet, vi bør vide?</string>
<string name="report_comment_hint">Yderligere kommentarer</string>
<string name="sending_report">Sender rapport…</string>
<string name="sending_report">Indsender rapport…</string>
<string name="report_sent_title">Tak for anmeldelsen. Vi vil se nærmere på dette.</string>
<string name="report_sent_subtitle">Mens vi gennemgår anmeldelsen, kan du tage skridt mod %s:</string>
<string name="unfollow_user">Følg ikke længere %s</string>
<string name="unfollow">Følg ikke længere</string>
<string name="back">Tilbage</string>
<string name="search_communities">Server-navn eller URL</string>
<string name="mute_user_explain">Du vil ikke se indlæg fra vedkommende, der dog stadig kan se dine indlæg og følge dig, men ikke være bekendt med tavsgørelsen.</string>
<string name="block_user_explain">Du vil ikke se indlæg fra vedkommende, der hverken kan se dine indlæg eller følge dig, men vil være bekendt med blokeringen.</string>
<string name="report_personal_title">Ønsker du ikke at se dette?</string>
<string name="report_personal_subtitle">Her er mulighederne for at styre, hvad du ser på Mastodon:</string>
<string name="back">Retur</string>
<string name="search_communities">Servernavn eller -URL</string>
<string name="instance_rules_title">Serverregler</string>
<string name="instance_rules_subtitle">Ved at fortsætte accepterer du at følge følgende regler, der er vedtaget og håndhævet af %ss moderatorer.</string>
<string name="instance_rules_subtitle">Ved at fortsætte accepterer du at overholde flg. regler, som angivet og håndhævet af %s moderatorerne.</string>
<string name="signup_title">Opret konto</string>
<string name="display_name">Navn</string>
<string name="username">Brugernavn</string>
<string name="email">Email</string>
<string name="email">E-mail</string>
<string name="password">Adgangskode</string>
<string name="confirm_password">Bekræft adgangskode</string>
<string name="password_note">Inkluder store bogstaver, specialtegn og tal for at gøre din adgangskode stærkere.</string>
<string name="password_note">Benyt majuskler, specialtegn og tal for at øge adgangskodens styrke.</string>
<string name="category_academia">Forskning og højere uddannelser</string>
<string name="category_activism">Aktivisme</string>
<string name="category_all">Alt</string>
@@ -153,41 +178,55 @@
<string name="category_tech">Teknologi</string>
<string name="confirm_email_title">Tjek din indbakke</string>
<!-- %s is the email address -->
<string name="confirm_email_subtitle">Tryk på det modtage link for at bekræfte %s. Vi venter her så længe.</string>
<string name="confirm_email_didnt_get">Modtog intet link?</string>
<string name="resend">Send igen</string>
<string name="open_email_app">Åben email-app</string>
<string name="resent_email">Bekræftelses-email sendt</string>
<string name="open_email_app">Åbn e-mail app</string>
<string name="resent_email">Bekræftelsesmail sendt</string>
<string name="compose_hint">Angiv eller indsæt, hvad du tænker på</string>
<string name="content_warning">Indholdsadvarsel</string>
<string name="save">Gem</string>
<string name="add_alt_text">Tilføj alternativ tekst</string>
<string name="visibility_public">Offentlig</string>
<string name="visibility_followers_only">Kun følgere</string>
<string name="skip">Spring over</string>
<string name="notification_type_follow">Nye følgere</string>
<string name="visibility_followers_only">Kun Følgere</string>
<string name="visibility_private">Kun nævnte personer</string>
<string name="recent_searches">Nylige</string>
<string name="skip">Overspring</string>
<string name="notification_type_follow">Nye Følgere</string>
<string name="notification_type_favorite">Favoritmarkeringer</string>
<string name="notification_type_reblog">Fremhævninger</string>
<string name="notification_type_mention">Omtaler</string>
<string name="notification_type_poll">Afstemninger</string>
<string name="choose_account">Vælg konto</string>
<string name="err_not_logged_in">Log venligst ind på Mastodon først</string>
<string name="media_attachment_unsupported_type">Filen %s er af en type der ikke understøttes</string>
<string name="media_attachment_too_big">Størrelsen på filen %1$s overskrider grænsen på %2$s MB</string>
<string name="err_not_logged_in">Log ind på Mastodon først</string>
<plurals name="cant_add_more_than_x_attachments">
<item quantity="one">Maks. %d medievedhæftning kan tilføjes</item>
<item quantity="other">Maks. %d medievedhæftninger kan tilføjes</item>
</plurals>
<string name="media_attachment_unsupported_type">Filen %s er af en uunderstøttet type</string>
<string name="media_attachment_too_big">Filen %1$s overskrider begrænsningen på %2$s MB</string>
<string name="settings_theme">Udseende</string>
<string name="theme_auto">Brug enhedens indstillinger for udseende</string>
<string name="theme_light">Lys</string>
<string name="theme_dark">Mørk</string>
<string name="settings_behavior">Opførsel</string>
<string name="settings_gif">Spil animerede avatarer og emoji</string>
<string name="settings_behavior">Adfærd</string>
<string name="settings_gif">Afspil animerede avatarer og emojier</string>
<string name="settings_custom_tabs">Benyt in-app browser</string>
<string name="settings_notifications">Meddelelser</string>
<string name="settings_notifications">Notifikationer</string>
<string name="settings_contribute">Bidrag til Mastodon</string>
<string name="settings_tos">Vilkår og Betingelser</string>
<string name="settings_privacy_policy">Privatlivspolitik</string>
<string name="settings_tos">Tjenestevilkår</string>
<string name="settings_privacy_policy">Fortrolighedspolitik</string>
<string name="settings_clear_cache">Ryd mediecache</string>
<string name="settings_app_version">Mastodon til Android v%1$s (%2$d)</string>
<string name="media_cache_cleared">Mediecache ryddet</string>
<string name="confirm_log_out">Log ud af %s?</string>
<string name="sensitive_content_explain">Forfatteren markerede dette medie som sensitivt.</string>
<string name="avatar_description">Gå til %ss profil</string>
<string name="more_options">Flere muligheder</string>
<string name="new_post">Nyt indlæg</string>
<string name="button_reply">Svar</string>
<string name="button_reblog">Fremhæv</string>
<string name="button_favorite">Favorit</string>
<string name="button_favorite">Gør til favorit</string>
<string name="button_share">Del</string>
<string name="media_no_description">Medier uden beskrivelse</string>
<string name="add_media">Tilføj medier</string>
@@ -198,6 +237,7 @@
<string name="media_viewer">Mediefremviser</string>
<string name="follow_user">Følg %s</string>
<string name="unfollowed_user">Følg ikke længere %s</string>
<string name="followed_user">Du følger nu %s</string>
<string name="following_user_requested">Anmodede om at følge %s</string>
<string name="open_in_browser">Åbn i browser</string>
<string name="hide_boosts_from_user">Skjul fremhævninger fra %s</string>
@@ -207,7 +247,7 @@
<string name="clear">Ryd</string>
<string name="profile_header">Overskriftsbillede</string>
<string name="profile_picture">Profilbillede</string>
<string name="reorder">Omarranger</string>
<string name="reorder">Omarrangér</string>
<string name="download">Download</string>
<string name="permission_required">Tilladelse kræves</string>
<string name="storage_permission_to_download">Appen skal have adgang til din lagerplads for at gemme denne fil.</string>
@@ -215,7 +255,13 @@
<string name="error_saving_file">Fejl under lagring af fil</string>
<string name="file_saved">Fil gemt</string>
<string name="downloading">Downloader…</string>
<string name="no_app_to_handle_action">Ingen app til at håndtere denne handling</string>
<string name="local_timeline">Lokalt</string>
<string name="trending_posts_info_banner">Her er de indlæg, som vinder indpas på tværs af Mastodon.</string>
<string name="trending_links_info_banner">Her er de nyheder, der bliver talt om på Mastodon.</string>
<!-- %s is the server domain -->
<string name="local_timeline_info_banner">Disse er alle indlæg fra alle brugere på din server (%s).</string>
<string name="recommended_accounts_info_banner">Baseret på andre, du følger, vil du måske synes om disse konti.</string>
<string name="see_new_posts">Se nye indlæg</string>
<string name="load_missing_posts">Indlæs manglende indlæg</string>
<string name="follow_back">Følg Tilbage</string>
@@ -236,14 +282,14 @@
<item quantity="other">%,d favoritmarkeringer</item>
</plurals>
<plurals name="x_reblogs">
<item quantity="one">%,d indlæg</item>
<item quantity="other">%,d indlæg</item>
<item quantity="one">%,d fremhævning</item>
<item quantity="other">%,d fremhævninger</item>
</plurals>
<string name="timestamp_via_app">%1$s via %2$s</string>
<string name="time_now">nu</string>
<string name="edit_history">Rediger historik</string>
<string name="last_edit_at_x">Senest ændret: %s</string>
<string name="time_just_now">lige nu</string>
<string name="time_just_now">netop nu</string>
<plurals name="x_seconds_ago">
<item quantity="one">%d sekund siden</item>
<item quantity="other">%d sekunder siden</item>
@@ -252,11 +298,11 @@
<item quantity="one">%d minut siden</item>
<item quantity="other">%d minutter siden</item>
</plurals>
<string name="edited_timestamp">redigerede %s</string>
<string name="edited_timestamp">redigeret %s</string>
<string name="edit_original_post">Oprindeligt indlæg</string>
<string name="edit_text_edited">Tekst redigeret</string>
<string name="edit_spoiler_added">Indholdsadvarsel tilføjet</string>
<string name="edit_spoiler_edited">Indholdsadvarsel tilføjet</string>
<string name="edit_spoiler_edited">Indholdsadvarsel redigeret</string>
<string name="edit_spoiler_removed">Indholdsadvarsel fjernet</string>
<string name="edit_poll_added">Afstemning tilføjet</string>
<string name="edit_poll_edited">Afstemning redigeret</string>
@@ -264,11 +310,11 @@
<string name="edit_media_added">Medier tilføjet</string>
<string name="edit_media_removed">Medier fjernet</string>
<string name="edit_media_reordered">Medie omarrangeret</string>
<string name="edit_marked_sensitive">Marker som følsom</string>
<string name="edit_marked_not_sensitive">Markeret ikke følsom</string>
<string name="edit_marked_sensitive">Markeret sensitivt</string>
<string name="edit_marked_not_sensitive">Markeret ikke-sensitivt</string>
<string name="edit_multiple_changed">Indlæg redigeret</string>
<string name="edit">Rediger</string>
<string name="discard_changes">Fortryd ændringer?</string>
<string name="edit">Redigér</string>
<string name="discard_changes">Kassér ændringer?</string>
<string name="upload_failed">Upload mislykkedes</string>
<string name="file_size_bytes">%d bytes</string>
<string name="file_size_kb">%.2f KB</string>
@@ -279,20 +325,21 @@
<!-- %s is version like 1.2.3 -->
<!-- %s is file size -->
<string name="download_update">Download (%s)</string>
<string name="install_update">Installer</string>
<string name="privacy_policy_title">Dit privatliv</string>
<string name="privacy_policy_subtitle">Selvom Mastodon-appen ikke indsamler data, kan den server du tilmelder dig gennem, have en anden politik.\n\nHvis du er uenig i politikken for %s, kan du gå tilbage og vælge en anden server.</string>
<string name="install_update">Installér</string>
<string name="privacy_policy_title">Din fortrolighed</string>
<string name="privacy_policy_subtitle">Selvom Mastodon-appen ikke indsamler data, kan serveren, via hvilken man tilmelder sig, have en anden politik.\n\nEr man uenig i politikken for %s, kan man gå tilbage og vælge en anden server.</string>
<string name="i_agree">Jeg accepterer</string>
<string name="empty_list">Denne liste er tom</string>
<string name="instance_signup_closed">Denne server er ikke åben for nye tilmeldinger.</string>
<string name="instance_signup_closed">Denne server accepterer ikke nye tilmeldinger.</string>
<string name="text_copied">Kopieret til udklipsholderen</string>
<string name="add_bookmark">Bogmærk</string>
<string name="remove_bookmark">Fjern bogmærke</string>
<string name="bookmarks">Bogmærker</string>
<string name="your_favorites">Dine Favoritter</string>
<string name="login_title">Velkommen tilbage</string>
<string name="login_subtitle">Log ind med serveren hvor du oprettede din bruger.</string>
<string name="login_subtitle">Log ind med serveren, på hvilken din konto blev oprettet.</string>
<string name="server_url">Server-URL</string>
<string name="signup_random_server_explain">En server baseret på dit sprog vælges, hvis du fortsætter uden selv at vælge.</string>
<string name="server_filter_any_language">Hvilket som helst sprog</string>
<string name="server_filter_instant_signup">Øjeblikkelig tilmelding</string>
<string name="server_filter_manual_review">Manuel evaluering</string>
@@ -305,6 +352,7 @@
<string name="server_filter_region_oceania">Oceania</string>
<string name="not_accepting_new_members">Ikke åben for nye medlemmer</string>
<string name="category_special_interests">Særlige Interesser</string>
<string name="signup_passwords_dont_match">Adgangskoder matcher ikke</string>
<string name="pick_server_for_me">Vælg for mig</string>
<string name="profile_add_row">Tilføj række</string>
<string name="profile_setup">Profilopsætning</string>
@@ -313,12 +361,14 @@
<string name="popular_on_mastodon">Populært på Mastodon</string>
<string name="follow_all">Følg alle</string>
<string name="server_rules_disagree">Ikke enig</string>
<string name="privacy_policy_explanation">TL;DR: Vi indsamler eller behandler ikke noget.</string>
<!-- %s is server domain -->
<string name="server_policy_disagree">Uenig med %s</string>
<string name="profile_bio">Biografi</string>
<!-- Shown in a progress dialog when you tap "follow all" -->
<string name="sending_follows">Følger brugere …</string>
<!-- %1$s is server domain, %2$s is email domain. You can reorder these placeholders to fit your language better. -->
<string name="signup_email_domain_blocked">%1$s tillader ikke tilmeldinger fra %2$s. Prøv en anden eller &lt;a&gt;vælg en anden server&lt;/a&gt;.</string>
<string name="spoiler_show">Vis alligevel</string>
<string name="spoiler_hide">Genskjul</string>
<string name="poll_multiple_choice">Vælg en eller flere</string>
@@ -331,21 +381,127 @@
<string name="show">Vis</string>
<string name="hide">Skjul</string>
<string name="join_default_server">Tilmeld dig %s</string>
<string name="pick_server">Vælg en anden server</string>
<string name="signup_or_login">eller</string>
<string name="learn_more">Få mere at vide</string>
<string name="welcome_to_mastodon">Velkommen til Mastodon</string>
<string name="welcome_paragraph1">Mastodon er et decentraliseret socialt netværk, hvilket betyder at ingen enkelt virksomhed styrer det. Det består af mange uafhængige servere, alle forbundet sammen.</string>
<string name="what_are_servers">Hvad er servere?</string>
<string name="welcome_paragraph2"><![CDATA[Alle Mastodon-konti har plads på en server. Hver server har sine egne værdier, regler og administratorer. Ligegyldigt hvilken server du vælger, kan du følge og interagere med folk på alle andre servere.]]></string>
<string name="opening_link">Åbner link…</string>
<string name="link_not_supported">Linket er ikke understøttet i appen</string>
<string name="log_out_all_accounts">Log ud af alle konti</string>
<string name="confirm_log_out_all_accounts">Log ud af alle konti?</string>
<string name="retry">Forsøg igen</string>
<string name="post_failed">Mislykkedes at indsende indlæg</string>
<!-- %s is formatted file size ("467 KB image") -->
<string name="attachment_description_image">%s billede</string>
<string name="attachment_description_video">%s video</string>
<string name="attachment_description_audio">%s lyd</string>
<string name="attachment_description_unknown">%s fil</string>
<string name="attachment_type_image">Billede</string>
<string name="attachment_type_video">Video</string>
<string name="attachment_type_audio">Lyd</string>
<string name="attachment_type_gif">GIF</string>
<string name="attachment_type_unknown">Fil</string>
<string name="attachment_x_percent_uploaded">%d%% uploadet</string>
<string name="add_poll_option">Tilføj afstemningsmulighed</string>
<string name="poll_length">Afstemningslængde</string>
<string name="poll_style">Stil</string>
<string name="compose_poll_single_choice">Vælg én</string>
<string name="compose_poll_multiple_choice">Multivalg</string>
<string name="delete_poll_option">Slet afstemningsvalg</string>
<string name="poll_style_title">Afstemningsstil</string>
<string name="alt_text">Alt text</string>
<string name="help">Hjælp</string>
<string name="what_is_alt_text">Hvad er alt tekst?</string>
<string name="alt_text_help">Alt tekst vil sige billedbeskrivelser til personer med synshandikap, forbindelser med lav båndbredde eller dem, som søger ekstra kontekst.\n\nMan kan forbedre tilgængelighed og forståelse for alle ved at skrive klar, kortfattet og objektiv alt-tekst.\n\n<ul><li>Fang vigtige elementer</li>\n<li>Opsummér tekst i billeder</li>\n<li>Brug regulær sætningsstruktur</li>\n<li>Undgå overflødig information</li>\n<li>Fokus på tendenser og vigtige fund i kompleks grafik (såsom diagrammer/kort)</li></ul></string>
<string name="edit_post">Redigere opslag</string>
<string name="no_verified_link">Intet bekræftet link</string>
<string name="compose_autocomplete_emoji_empty">Gennemse emoji</string>
<string name="compose_autocomplete_users_empty">Find den, man leder efter</string>
<string name="no_search_results">Ingen resultater for disse søgeord</string>
<string name="language">Sprog</string>
<string name="language_default">Standard</string>
<string name="language_system">System</string>
<string name="language_detecting">Detektere sprog</string>
<string name="language_cant_detect">Kan ikke detektere sprog</string>
<string name="language_detected">Detekteret</string>
<string name="media_hidden">Medie skjult</string>
<string name="post_hidden">Indlæg skjult</string>
<string name="report_title_post">Anmeld indlæg</string>
<string name="forward_report_explanation">Kontoen er fra en anden server. Send også en anonymiseret kopi af anmeldelsen dertil?</string>
<!-- %s is the server domain -->
<string name="forward_report_to_server">Videresend til %s</string>
<!-- Shown on the "stamp" on the screen that appears after you report a post/user. Please keep the translation short, preferably a single word -->
<string name="reported">Anmeldt</string>
<string name="report_unfollow_explanation">For ikke længere at se vedkommendes indlæg i hjemmefeedet, ophør med at følge.</string>
<string name="muted_user">Tavsgjorde %s</string>
<!-- %s is the app name (Mastodon, key app_name). I made it a placeholder so everything Just Works™ with forks -->
<!-- %1$s is the date (may be relative, e.g. "today" or "yesterday"), %2$s is the time. You can reorder these placeholders if that works better for your language -->
<!-- %s is the timestamp ("tomorrow at 12:34") -->
<!-- %s is the timestamp ("tomorrow at 12:34") -->
<string name="count_three">Tre</string>
<string name="count_four">Fire</string>
<string name="alt_text_reminder_post_anyway">Indlæg</string>
<!-- %s is the username -->
<string name="unfollow_confirmation">Følg ikke længere %s?</string>
<string name="filter_active">Aktiv</string>
<string name="filter_inactive">Inaktiv</string>
<string name="settings_add_filter">Tilføj filter</string>
<string name="settings_edit_filter">Redigér filter</string>
<string name="settings_filter_duration">Varighed</string>
<string name="settings_filter_muted_words">Tavsgjorte (skjulte) ord</string>
<string name="settings_filter_context">Tavsgør fra</string>
<string name="settings_filter_show_cw">Vis med indholdsadvarsel</string>
<string name="settings_filter_show_cw_explanation">Viser stadig indlæg matchende dette filter, men bag en indholdsadvarsel</string>
<string name="settings_delete_filter">Slet filter</string>
<string name="filter_duration_forever">For evigt</string>
<!-- %s is the timestamp ("tomorrow at 12:34") -->
<string name="settings_filter_ends">Slutter %s</string>
<plurals name="settings_x_muted_words">
<item quantity="one">%d tavsgjort (skjult) ord/sætning</item>
<item quantity="other">%d tavsgjorte (skjulte) ord/sætninger</item>
</plurals>
<string name="selection_2_options">%1$s og %2$s</string>
<string name="selection_3_options">%1$s, %2$s og %3$s</string>
<string name="selection_4_or_more">%1$s, %2$s og %3$d flere</string>
<string name="filter_context_home_lists">Hjem og lister</string>
<string name="filter_context_notifications">Notifikationer</string>
<string name="filter_context_public_timelines">Offentlige tidslinjer</string>
<string name="filter_context_threads_replies">Tråde og svar</string>
<string name="filter_context_profiles">Profiler</string>
<string name="settings_filter_title">Titel</string>
<string name="settings_delete_filter_title">Slet filter “%s”?</string>
<string name="settings_delete_filter_confirmation">Dette filter slettes fra din konto på alle enheder.</string>
<string name="add_muted_word">Tilføj tavsgjort (skjult) ord</string>
<string name="edit_muted_word">Redigér tavsgjort (skjult) ord</string>
<string name="add">Tilføj</string>
<string name="filter_word_or_phrase">Ord/sætning</string>
<string name="filter_add_word_help">Ord er versalfølsomme og matcher kun fulde ord.\n\nFiltreres nøgleordet “Apple,” vil det skjule indlæg indeholdende “apple” eller “aPpLe”, men ikke “pineapple”.</string>
<string name="settings_delete_filter_word">Slet ordet “%s”?</string>
<string name="enter_selection_mode">Vælg</string>
<string name="select_all">Vælg alle</string>
<string name="settings_filter_duration_title">Filtervarighed</string>
<string name="filter_duration_custom">Tilpasset</string>
<plurals name="settings_delete_x_filter_words">
<item quantity="one">Slet %d ord?</item>
<item quantity="other">Slet %d ord?</item>
</plurals>
<plurals name="x_items_selected">
<item quantity="one">%d valgt</item>
<item quantity="other">%d valgt</item>
</plurals>
<string name="required_form_field_blank">Obligatorisk felt</string>
<string name="filter_word_already_in_list">Allerede på listen</string>
<string name="app_update_ready">App-opdatering klar</string>
<string name="app_update_version">Version %s</string>
<string name="downloading_update">Downloader (%d%%)</string>
<!-- Shown like a content warning, %s is the name of the filter -->
<string name="post_matches_filter_x">Matcher filter “%s”</string>
<string name="search_mastodon">Søg i Mastodon</string>
<string name="clear_all">Ryd alle</string>
<string name="search_open_url">Åbn URL i Mastodon</string>
<string name="posts_matching_hashtag">Indlæg med “%s”</string>
<!-- Shown in the post header. Please keep it short -->
</resources>

View File

@@ -7,8 +7,8 @@
<string name="not_a_mastodon_instance">%s scheint kein Mastodon-Server zu sein.</string>
<string name="ok">OK</string>
<string name="preparing_auth">Authentifizierung wird vorbereitet </string>
<string name="finishing_auth">Authentifizierung wird abgeschlossen …</string>
<string name="user_boosted">%s hat geteilt</string>
<string name="finishing_auth">Authentisierung wird abgeschlossen …</string>
<string name="user_boosted">%s teilte</string>
<string name="in_reply_to">Als Antwort auf %s</string>
<string name="notifications">Benachrichtigungen</string>
<string name="user_followed_you">%s folgt dir jetzt</string>
@@ -143,7 +143,7 @@
<string name="report_choose_posts_subtitle">Alles Zutreffende auswählen</string>
<string name="report_comment_title">Gibt es weitere Anmerkungen, von denen wir wissen sollten?</string>
<string name="report_comment_hint">Ergänzende Hinweise</string>
<string name="sending_report">Meldung wird verschickt …</string>
<string name="sending_report">Meldung wird gesendet …</string>
<string name="report_sent_title">Danke für deine Meldung, wir werden uns damit beschäftigen.</string>
<string name="report_sent_subtitle">Während wir den Vorfall überprüfen, kannst du gegen %s weitere Maßnahmen ergreifen:</string>
<string name="unfollow_user">%s entfolgen</string>
@@ -240,7 +240,7 @@
<string name="followed_user">Du folgst nun %s</string>
<string name="following_user_requested">Deine Follower-Anfrage an %s wurde gesendet</string>
<string name="open_in_browser">Im Browser öffnen</string>
<string name="hide_boosts_from_user">Geteilte Beiträge von %s verstecken</string>
<string name="hide_boosts_from_user">Geteilte Beiträge von %s ausblenden</string>
<string name="show_boosts_from_user">Geteilte Beiträge von %s anzeigen</string>
<string name="signup_reason">Warum möchtest du beitreten?</string>
<string name="signup_reason_note">Das erleichtert uns die Prüfung deiner Anmeldung.</string>
@@ -250,7 +250,7 @@
<string name="reorder">Neu sortieren</string>
<string name="download">Herunterladen</string>
<string name="permission_required">Berechtigung erforderlich</string>
<string name="storage_permission_to_download">Die App benötigt Zugriff auf den Speicher deines Gerätes, um diese Datei zu speichern.</string>
<string name="storage_permission_to_download">Die App benötigt Zugriff auf den Speicher deines Geräts, um diese Datei zu speichern.</string>
<string name="open_settings">Einstellungen öffnen</string>
<string name="error_saving_file">Fehler beim Speichern der Datei</string>
<string name="file_saved">Datei gespeichert</string>
@@ -278,8 +278,8 @@
<item quantity="other">%,d folge ich</item>
</plurals>
<plurals name="x_favorites">
<item quantity="one">%,d × favorisiert</item>
<item quantity="other">%,d × favorisiert</item>
<item quantity="one">einmal favorisiert</item>
<item quantity="other">%,d-mal favorisiert</item>
</plurals>
<plurals name="x_reblogs">
<item quantity="one">%,d geteilter Beitrag</item>
@@ -332,7 +332,7 @@
<string name="empty_list">Diese Liste ist leer</string>
<string name="instance_signup_closed">Dieser Server akzeptiert keine neuen Registrierungen.</string>
<string name="text_copied">In die Zwischenablage kopiert</string>
<string name="add_bookmark">Lesezeichen hinzufügen</string>
<string name="add_bookmark">Lesezeichen setzen</string>
<string name="remove_bookmark">Lesezeichen entfernen</string>
<string name="bookmarks">Lesezeichen</string>
<string name="your_favorites">Deine Favoriten</string>

View File

@@ -329,10 +329,23 @@
<string name="sk_hashtag_timeline_local_only_switch">Nur lokale Beiträge anzeigen\?</string>
<string name="sk_add_timeline_tag_error_empty">Hashtag darf nicht leer sein</string>
<string name="sk_advanced_options_hide">Erweiterte Optionen ausblenden</string>
<string name="sk_search_fediverse">Fediverse durchsuchen</string>
<string name="sk_search_fediverse">Fediverse durchsuchen</string>
<string name="sk_switch_timeline">Timeline wechseln</string>
<string name="sk_settings_instance">Instanz</string>
<string name="sk_disable_pill_shaped_active_indicator">Pillen-förmigen Indikator für aktiven Tab deaktivieren</string>
<string name="sk_settings_true_black">Stockfinster-Modus</string>
<string name="sk_gif_badge">GIF</string>
<string name="sk_settings_unifiedpush">UnifiedPush verwenden</string>
<string name="sk_settings_unifiedpush_no_distributor">Kein Verteiler gefunden</string>
<string name="sk_settings_unifiedpush_no_distributor_body">Damit UnifiedPush-Benachrichtigungen funktionieren, muss ein Verteiler installiert sein. Mehr Informationen auf https://unifiedpush.org</string>
<string name="sk_settings_display_pronouns_in_timelines">Pronomen in der Timeline anzeigen</string>
<string name="sk_settings_continues_playback">Tonwiedergabe überlagern</string>
<string name="sk_settings_continues_playback_summary">Musik im Hintergrund nicht pausieren, wenn in der App Medien abgespielt werden</string>
<string name="sk_settings_unifiedpush_choose">Verteiler auswählen</string>
<string name="sk_settings_display_pronouns_in_threads">Pronomen in Threads anzeigen</string>
<string name="sk_settings_display_pronouns_in_user_listings">Pronomen in User-Auflistungen anzeigen</string>
<string name="sk_tab_home">Start</string>
<string name="sk_tab_search">Suche</string>
<string name="sk_tab_notifications">Benachrichtigungen</string>
<string name="sk_tab_profile">Profil</string>
</resources>

View File

@@ -4,12 +4,18 @@
<string name="next">Siguiente</string>
<string name="loading_instance">Recuperando información del servidor…</string>
<string name="error">Error</string>
<string name="not_a_mastodon_instance">%s no parece ser un servidor de Mastodon.</string>
<string name="ok">Aceptar</string>
<string name="preparing_auth">Preparando para autenticación…</string>
<string name="finishing_auth">Finalizando autenticación…</string>
<string name="user_boosted">%s retooteó</string>
<string name="in_reply_to">En respuesta a %s</string>
<string name="notifications">Notificaciones</string>
<string name="user_followed_you">%s te ha seguido</string>
<string name="user_sent_follow_request">%s te envió una solicitud de seguimiento</string>
<string name="user_favorited">%s ha marcado tu publicación como favorita</string>
<string name="notification_boosted">%s ha impulsado tu publicación</string>
<string name="poll_ended">Ver los resultados de una encuesta en la que votaste</string>
<string name="share_toot_title">Compartir</string>
<string name="settings">Ajustes</string>
<string name="publish">Publicar</string>
@@ -31,6 +37,7 @@
<string name="button_follow">Seguir</string>
<string name="button_following">Siguiendo</string>
<string name="edit_profile">Editar perfil</string>
<string name="share_user">Compartir perfil</string>
<string name="mute_user">Silenciar a %s</string>
<string name="unmute_user">Dejar de silenciar a %s</string>
<string name="block_user">Bloquear a %s</string>
@@ -78,6 +85,10 @@
<item quantity="one">%d día restante</item>
<item quantity="other">%d días restantes</item>
</plurals>
<plurals name="x_votes">
<item quantity="one">%,d voto</item>
<item quantity="other">%,d votos</item>
</plurals>
<string name="poll_closed">Cerrado</string>
<string name="confirm_mute_title">Silenciar cuenta</string>
<string name="confirm_mute">Confirmar para silenciar a %s</string>
@@ -96,27 +107,35 @@
<string name="button_blocked">Bloqueado</string>
<string name="action_vote">Votar</string>
<string name="delete">Eliminar</string>
<string name="confirm_delete_title">Eliminar publicación</string>
<string name="confirm_delete">¿Confirma que quiere eliminar esta publicación?</string>
<string name="deleting">Eliminando…</string>
<string name="notification_channel_audio_player">Reproducción de audio</string>
<string name="play">Reproducir</string>
<string name="pause">Pausar</string>
<string name="log_out">Cerrar sesión</string>
<string name="add_account">Añadir cuenta</string>
<string name="search_hint">Buscar</string>
<string name="hashtags">Etiquetas</string>
<string name="news">Noticias</string>
<string name="for_you">Para ti</string>
<string name="all_notifications">Todo</string>
<string name="mentions">Menciones</string>
<plurals name="x_people_talking">
<item quantity="one">%d persona está hablando</item>
<item quantity="other">%d personas están hablando</item>
</plurals>
<string name="report_title">Denunciar a %s</string>
<string name="report_choose_reason">¿Qué hay de malo en esta publicación?</string>
<string name="report_choose_reason_account">¿Qué hay de malo con %s?</string>
<string name="report_choose_reason_subtitle">Seleccione la mejor opción</string>
<string name="report_reason_personal">No me gusta</string>
<string name="report_reason_personal_subtitle">No es algo que quieras ver</string>
<string name="report_reason_spam">Es spam</string>
<string name="report_reason_spam_subtitle">Enlaces maliciosos, interacciones falsas o respuestas repetitivas</string>
<string name="report_reason_violation">Viola las reglas del servidor</string>
<string name="report_reason_violation_subtitle">Eres consciente de que infringe normas específicas</string>
<string name="report_reason_other">Es otra cosa</string>
<string name="report_reason_other_subtitle">El problema no encaja en otras categorías</string>
<string name="report_choose_rule">¿Qué normas se están violando?</string>
<string name="report_choose_rule_subtitle">Selecciona todos los que correspondan</string>
@@ -125,8 +144,14 @@
<string name="report_comment_title">¿Hay algo más que deberíamos saber?</string>
<string name="report_comment_hint">Comentarios adicionales</string>
<string name="sending_report">Enviando denuncia…</string>
<string name="report_sent_title">Gracias por reportar, lo revisaremos.</string>
<string name="report_sent_subtitle">Mientras revisamos esto, puedes tomar medidas contra %s:</string>
<string name="unfollow_user">Dejar de seguir a %s</string>
<string name="unfollow">Dejar de seguir</string>
<string name="mute_user_explain">No verás sus mensajes. Todavía pueden seguirte y ver tus mensajes y no sabrán que están silenciados.</string>
<string name="block_user_explain">No verás sus mensajes. No podrán ver tus publicaciones ni seguirte. Podrán decir que están bloqueados.</string>
<string name="report_personal_title">¿No quiere ver esto?</string>
<string name="report_personal_subtitle">Aquí están tus opciones para controlar lo que ves en Mastodon:</string>
<string name="back">Atrás</string>
<string name="search_communities">Nombre del servidor o URL</string>
<string name="instance_rules_title">Reglas del servidor</string>
@@ -153,14 +178,19 @@
<string name="category_tech">Tecnología</string>
<string name="confirm_email_title">Revisa tu bandeja de entrada</string>
<!-- %s is the email address -->
<string name="confirm_email_subtitle">Pulsa el enlace que te hemos enviado para verificar %s. Esperaremos aquí mismo.</string>
<string name="confirm_email_didnt_get">¿No recibiste un enlace?</string>
<string name="resend">Reenviar</string>
<string name="open_email_app">Abrir aplicación de email</string>
<string name="resent_email">Correo de confirmación enviado</string>
<string name="compose_hint">Escribe o pega lo que tengas en mente</string>
<string name="content_warning">Advertencia de contenido</string>
<string name="save">Guardar</string>
<string name="add_alt_text">Añadir texto alternativo</string>
<string name="visibility_public">Público</string>
<string name="visibility_followers_only">Sólo seguidores</string>
<string name="visibility_private">Solo personas mencionadas</string>
<string name="recent_searches">Recientes</string>
<string name="skip">Saltar</string>
<string name="notification_type_follow">Nuevos seguidores</string>
<string name="notification_type_favorite">Favoritos</string>
@@ -169,8 +199,14 @@
<string name="notification_type_poll">Encuestas</string>
<string name="choose_account">Elegir cuenta</string>
<string name="err_not_logged_in">Por favor inicie sesión primero en Mastodon</string>
<plurals name="cant_add_more_than_x_attachments">
<item quantity="one">No puede añadir más de %d archivo adjunto</item>
<item quantity="other">No puede añadir más de %d archivos adjuntos</item>
</plurals>
<string name="media_attachment_unsupported_type">El archivo %s es de un tipo no compatible</string>
<string name="media_attachment_too_big">El archivo %1$s supera el límite de tamaño de %2$s MB</string>
<string name="settings_theme">Apariencia</string>
<string name="theme_auto">Usar apariencia del dispositivo</string>
<string name="theme_light">Claro</string>
<string name="theme_dark">Oscuro</string>
<string name="settings_behavior">Comportamiento</string>
@@ -183,6 +219,9 @@
<string name="settings_clear_cache">Limpiar caché multimedia</string>
<string name="settings_app_version">Mastodon para Android -%1$s (%2$d)</string>
<string name="media_cache_cleared">Caché multimedia borrada</string>
<string name="confirm_log_out">¿Cerrar sesión de %s?</string>
<string name="sensitive_content_explain">El autor ha marcado este medio como sensible.</string>
<string name="avatar_description">Ir al perfil de %s</string>
<string name="more_options">Más opciones</string>
<string name="new_post">Nuevo post</string>
<string name="button_reply">Responder</string>
@@ -198,6 +237,7 @@
<string name="media_viewer">Visor de medios</string>
<string name="follow_user">Seguir a %s</string>
<string name="unfollowed_user">No sigues a %s</string>
<string name="followed_user">Ahora estás siguiendo a %s</string>
<string name="following_user_requested">%s solicitó seguirte</string>
<string name="open_in_browser">Abrir en el navegador</string>
<string name="hide_boosts_from_user">Ocultar retoots de %s</string>
@@ -215,7 +255,13 @@
<string name="error_saving_file">Error al guardar el archivo</string>
<string name="file_saved">Archivo guardado</string>
<string name="downloading">Descargando…</string>
<string name="no_app_to_handle_action">No hay ninguna aplicación para manejar esta acción</string>
<string name="local_timeline">Local</string>
<string name="trending_posts_info_banner">Estas son las publicaciones que están ganando tracción a través de Mastodon.</string>
<string name="trending_links_info_banner">Estas son las noticias de las que se habla en Mastodon.</string>
<!-- %s is the server domain -->
<string name="local_timeline_info_banner">Estos son todos los mensajes de todos los usuarios de tu servidor (%s).</string>
<string name="recommended_accounts_info_banner">Es posible que te gusten estas cuentas basadas en otras que sigues.</string>
<string name="see_new_posts">Ver nuevas publicaciones</string>
<string name="load_missing_posts">Cargar publicaciones faltantes</string>
<string name="follow_back">Seguir de vuelta</string>
@@ -293,6 +339,7 @@
<string name="login_title">Qué bueno verle de nuevo</string>
<string name="login_subtitle">Inicie sesión con el servidor donde creó su cuenta.</string>
<string name="server_url">URL del servidor</string>
<string name="signup_random_server_explain">Seleccionaremos un servidor basado en tu idioma si continúas sin hacer una selección.</string>
<string name="server_filter_any_language">Cualquier idioma</string>
<string name="server_filter_instant_signup">Registro instantáneo</string>
<string name="server_filter_manual_review">Revisión manual</string>
@@ -305,6 +352,7 @@
<string name="server_filter_region_oceania">Oceanía</string>
<string name="not_accepting_new_members">No se aceptan nuevos miembros</string>
<string name="category_special_interests">Intereses especiales</string>
<string name="signup_passwords_dont_match">Las contraseñas no coinciden</string>
<string name="pick_server_for_me">Elegir por mí</string>
<string name="profile_add_row">Añadir fila</string>
<string name="profile_setup">Configuración del perfil</string>
@@ -313,12 +361,14 @@
<string name="popular_on_mastodon">Popular en Mastodon</string>
<string name="follow_all">Seguir a todos</string>
<string name="server_rules_disagree">En desacuerdo</string>
<string name="privacy_policy_explanation">Resumen: No recogemos ni procesamos nada.</string>
<!-- %s is server domain -->
<string name="server_policy_disagree">En desacuerdo con %s</string>
<string name="profile_bio">Biografía</string>
<!-- Shown in a progress dialog when you tap "follow all" -->
<string name="sending_follows">Siguiendo usuarios…</string>
<!-- %1$s is server domain, %2$s is email domain. You can reorder these placeholders to fit your language better. -->
<string name="signup_email_domain_blocked">%1$s no permite registros de %2$s. Prueba uno diferente o &lt;a&gt;elige un servidor diferente&lt;/a&gt;.</string>
<string name="spoiler_show">Mostrar de todos modos</string>
<string name="spoiler_hide">Volver a ocultar</string>
<string name="poll_multiple_choice">Escoge una o más</string>
@@ -338,15 +388,190 @@
<string name="welcome_paragraph1">Mastodon es una red social descentralizada, lo que significa que no la controla una sola compañía. Está formada por muchos servidores independientes, todos juntos conectados.</string>
<string name="what_are_servers">¿Qué son los servidores?</string>
<string name="welcome_paragraph2"><![CDATA[Cada cuenta de Mastodon está alojada en un servidor — cada uno con sus propios valores, reglas y administradores. No importa cual elijas, puede seguir e interactuar con personas en cualquier servidor.]]></string>
<string name="opening_link">Abriendo enlace…</string>
<string name="link_not_supported">Este enlace no es compatible con la aplicación</string>
<string name="log_out_all_accounts">Cerrar sesión en todas las cuentas</string>
<string name="confirm_log_out_all_accounts">¿Cerrar sesión de todas las cuentas?</string>
<string name="retry">Reintentar</string>
<string name="post_failed">Error al enviar la publicación</string>
<!-- %s is formatted file size ("467 KB image") -->
<string name="attachment_description_image">Imagen %s</string>
<string name="attachment_description_video">Vídeo %s</string>
<string name="attachment_description_audio">Audio %s</string>
<string name="attachment_description_unknown">Archivo %s</string>
<string name="attachment_type_image">Imagen</string>
<string name="attachment_type_video">Vídeo</string>
<string name="attachment_type_audio">Audio</string>
<string name="attachment_type_gif">GIF</string>
<string name="attachment_type_unknown">Archivo</string>
<string name="attachment_x_percent_uploaded">%d%% subido</string>
<string name="add_poll_option">Añadir opción de encuesta</string>
<string name="poll_length">Longitud de la encuesta</string>
<string name="poll_style">Estilo</string>
<string name="compose_poll_single_choice">Elige uno</string>
<string name="compose_poll_multiple_choice">Múltiples opciones</string>
<string name="delete_poll_option">Borrar opción de encuesta</string>
<string name="poll_style_title">Estilo de encuesta</string>
<string name="alt_text">Texto alternativo</string>
<string name="help">Ayuda</string>
<string name="what_is_alt_text">¿Qué es el texto alternativo?</string>
<string name="edit_post">Editar publicación</string>
<string name="no_verified_link">Enlace no verificado</string>
<string name="compose_autocomplete_emoji_empty">Explorar emojis</string>
<string name="compose_autocomplete_users_empty">Encuentra quién estás buscando</string>
<string name="no_search_results">No se pudo encontrar nada para estos términos de búsqueda</string>
<string name="language">Idioma</string>
<string name="language_default">Predeterminado</string>
<string name="language_system">Sistema</string>
<string name="language_detecting">Detectando idioma</string>
<string name="language_cant_detect">No se puede detectar el idioma</string>
<string name="language_detected">Detectado</string>
<string name="media_hidden">Medios ocultos</string>
<string name="post_hidden">Publicación ocultada</string>
<string name="report_title_post">Reportar publicación</string>
<string name="forward_report_explanation">La cuenta es de otro servidor. ¿Enviar también una copia anónima de este informe?</string>
<!-- %s is the server domain -->
<string name="forward_report_to_server">Reenviar a %s</string>
<!-- Shown on the "stamp" on the screen that appears after you report a post/user. Please keep the translation short, preferably a single word -->
<string name="reported">Reportado</string>
<string name="muted_user">Silenciado %s</string>
<string name="report_sent_already_blocked">Ya has bloqueado a este usuario, así que no tienes nada más que hacer mientras revisamos tu informe.</string>
<string name="report_personal_already_blocked">Ya has bloqueado a este usuario, así que no tienes nada más que hacer.\n\n¡Gracias por ayudar a mantener Mastodon un lugar seguro para todos!</string>
<string name="blocked_user">Bloqueado %s</string>
<string name="mark_all_notifications_read">Marcar todo como leído</string>
<string name="settings_display">Visualización</string>
<string name="settings_filters">Filtros</string>
<string name="settings_server_explanation">Resumen, reglas, moderadores</string>
<!-- %s is the app name (Mastodon, key app_name). I made it a placeholder so everything Just Works™ with forks -->
<string name="about_app">Acerca de %s</string>
<string name="default_post_language">Idioma predeterminado de la publicación</string>
<string name="settings_alt_text_reminders">Añadir recordatorios de texto alt</string>
<string name="settings_confirm_unfollow">Preguntar antes de dejar de seguir a alguien</string>
<string name="settings_confirm_boost">Preguntar antes de impulsar</string>
<string name="settings_confirm_delete_post">Preguntar antes de eliminar mensajes</string>
<string name="pause_all_notifications">Pausar todo</string>
<string name="pause_notifications_off">Apagado</string>
<string name="notifications_policy_anyone">Cualquiera</string>
<string name="notifications_policy_followed">Personas que te siguen</string>
<string name="notifications_policy_follower">Personas que sigues</string>
<string name="notifications_policy_no_one">Nadie</string>
<string name="settings_notifications_policy">Recibir notificaciones de</string>
<string name="notification_type_mentions_and_replies">Menciones y respuestas</string>
<string name="pause_all_notifications_title">Pausar todas las notificaciones</string>
<plurals name="x_weeks">
<item quantity="one">%d semana</item>
<item quantity="other">%d semanas</item>
</plurals>
<!-- %1$s is the date (may be relative, e.g. "today" or "yesterday"), %2$s is the time. You can reorder these placeholders if that works better for your language -->
<string name="date_at_time">%1$s en %2$s</string>
<string name="today">hoy</string>
<string name="yesterday">ayer</string>
<string name="tomorrow">mañana</string>
<!-- %s is the timestamp ("tomorrow at 12:34") -->
<string name="pause_notifications_ends">Termina %s</string>
<!-- %s is the timestamp ("tomorrow at 12:34") -->
<string name="pause_notifications_banner">Las notificaciones se reanudarán %s.</string>
<string name="resume_notifications_now">Reanudar ahora</string>
<string name="open_system_notification_settings">Ir a la configuración de notificaciones</string>
<string name="about_server">Acerca de</string>
<string name="server_rules">Reglas</string>
<string name="server_administrator">Administrador</string>
<string name="notifications_disabled_in_system">Active las notificaciones desde la configuración de su dispositivo para ver actualizaciones desde cualquier lugar.</string>
<string name="settings_even_more">Aún más ajustes</string>
<string name="settings_show_cws">Mostrar advertencias de contenido</string>
<plurals name="in_x_seconds">
<item quantity="one">en %d segundo</item>
<item quantity="other">en %d segundos</item>
</plurals>
<plurals name="in_x_minutes">
<item quantity="one">en %d minuto</item>
<item quantity="other">en %d minutos</item>
</plurals>
<plurals name="in_x_hours">
<item quantity="one">en %d hora</item>
<item quantity="other">en %d horas</item>
</plurals>
<plurals name="x_hours_ago">
<item quantity="one">hace %d hora</item>
<item quantity="other">hace %d horas</item>
</plurals>
<string name="alt_text_reminder_title">Falta el texto alt de los medios</string>
<plurals name="alt_text_reminder_x_images">
<item quantity="one">%s de tus imágenes no tiene texto alternativo. ¿Publicar de todos modos?</item>
<item quantity="other">%s de tus imágenes no tienen texto alternativo. ¿Publicar de todos modos?</item>
</plurals>
<plurals name="alt_text_reminder_x_attachments">
<item quantity="one">%s de tus archivos adjuntos multimedia carece de texto alternativo. ¿Publicar de todos modos?</item>
<item quantity="other">%s de tus archivos adjuntos no tienen texto alternativo. ¿Publicar de todos modos?</item>
</plurals>
<string name="count_one">Uno</string>
<string name="count_two">Dos</string>
<string name="count_three">Tres</string>
<string name="count_four">Cuatro</string>
<string name="alt_text_reminder_post_anyway">Publicar</string>
<!-- %s is the username -->
<string name="unfollow_confirmation">¿Dejar de seguir a %s?</string>
<string name="filter_active">Activo</string>
<string name="filter_inactive">Inactivo</string>
<string name="settings_add_filter">Añadir filtro</string>
<string name="settings_edit_filter">Editar filtro</string>
<string name="settings_filter_duration">Duración</string>
<string name="settings_filter_muted_words">Palabras silenciadas</string>
<string name="settings_filter_context">Silenciar de</string>
<string name="settings_filter_show_cw">Mostrar con advertencia de contenido</string>
<string name="settings_filter_show_cw_explanation">Sigue mostrando mensajes que coinciden con este filtro, pero detrás de una advertencia de contenido</string>
<string name="settings_delete_filter">Eliminar filtro</string>
<string name="filter_duration_forever">Siempre</string>
<!-- %s is the timestamp ("tomorrow at 12:34") -->
<string name="settings_filter_ends">Termina %s</string>
<plurals name="settings_x_muted_words">
<item quantity="one">%d palabra o frase silenciada</item>
<item quantity="other">%d palabras o frases silenciadas</item>
</plurals>
<string name="selection_2_options">%1$s y %2$s</string>
<string name="selection_3_options">%1$s, %2$s, y %3$s</string>
<string name="selection_4_or_more">%1$s, %2$s, y %3$d más</string>
<string name="filter_context_home_lists">Inicio y listas</string>
<string name="filter_context_notifications">Notificaciones</string>
<string name="filter_context_threads_replies">Hilos y respuestas</string>
<string name="filter_context_profiles">Profiles</string>
<string name="settings_filter_title">Título</string>
<string name="settings_delete_filter_title">¿Eliminar filtro “%s”?</string>
<string name="settings_delete_filter_confirmation">Este filtro se eliminará de su cuenta en todos sus dispositivos.</string>
<string name="add_muted_word">Añadir palabra silenciada</string>
<string name="edit_muted_word">Editar palabra silenciada</string>
<string name="add">Añadir</string>
<string name="filter_word_or_phrase">Palabra o frase</string>
<string name="settings_delete_filter_word">¿Eliminar palabra “%s”?</string>
<string name="enter_selection_mode">Seleccionar</string>
<string name="select_all">Seleccionar todo</string>
<string name="settings_filter_duration_title">Duración del filtro</string>
<string name="filter_duration_custom">Personalizado</string>
<plurals name="settings_delete_x_filter_words">
<item quantity="one">¿Eliminar palabra %d?</item>
<item quantity="other">¿Eliminar palabras %d?</item>
</plurals>
<plurals name="x_items_selected">
<item quantity="one">%d seleccionado</item>
<item quantity="other">%d seleccionados</item>
</plurals>
<string name="required_form_field_blank">No puede estar en blanco</string>
<string name="filter_word_already_in_list">Ya está en la lista</string>
<string name="app_update_ready">Actualización de aplicación lista</string>
<string name="app_update_version">Versión %s</string>
<string name="downloading_update">Descargando (%d%%)</string>
<!-- Shown like a content warning, %s is the name of the filter -->
<string name="post_matches_filter_x">Coincide con el filtro “%s”</string>
<string name="search_mastodon">Buscar en Mastodon</string>
<string name="clear_all">Limpiar todo</string>
<string name="search_open_url">Abrir URL en Mastodon</string>
<string name="posts_matching_hashtag">Publicaciones con “%s”</string>
<string name="search_go_to_account">Ir a %s</string>
<string name="posts_matching_string">Publicaciones con “%s”</string>
<string name="accounts_matching_string">Personas con “%s”</string>
<!-- Shown in the post header. Please keep it short -->
<string name="time_seconds_ago_short">hace %ds</string>
<string name="time_minutes_ago_short">hace %dm</string>
<string name="time_hours_ago_short">hace %dh</string>
<string name="time_days_ago_short">hace %dd</string>
</resources>

View File

@@ -256,7 +256,7 @@
<string name="sk_settings_collapse_long_posts">Minimizar publicaciones largas</string>
<string name="sk_unfinished_attachments">¿Corregir adjuntos\?</string>
<string name="sk_unfinished_attachments_message">Algunos adjuntos no han terminado de subirse.</string>
<string name="sk_settings_prefix_reply_cw_with_re">Añadir \"re:\" a Advertencias de Contenido para</string>
<string name="sk_settings_prefix_reply_cw_with_re">Prefijo CW con \"re:\" al responder</string>
<string name="sk_spectator_mode">Modo espectador</string>
<string name="sk_settings_hide_interaction">Ocultar los botones de interacción</string>
<string name="sk_follow_as">Seguir desde otra cuenta</string>
@@ -273,8 +273,8 @@
<string name="sk_show_thread">Mostrar hilo</string>
<string name="sk_compact_reblog_reply_line">Línea compacta compartida/respondida</string>
<string name="sk_settings_confirm_before_reblog">Confirmación antes de impulsar</string>
<string name="sk_reacted_with">reaccionó con %s</string>
<string name="sk_reacted">reaccionó</string>
<string name="sk_reacted_with">%1$s reaccionó con %2$s</string>
<string name="sk_reacted">%s reaccionó</string>
<string name="sk_content_type">Tipo del contenido</string>
<string name="sk_content_type_unspecified">Sin especificar</string>
<string name="sk_content_type_plain">Texto sin formato</string>
@@ -292,18 +292,18 @@
<string name="sk_external_share_or_open_title">Compartir o abrir con una cuenta</string>
<string name="sk_open_in_app">Abrir en la app</string>
<string name="sk_external_share_title">Compartir con una cuenta</string>
<string name="sk_settings_auto_reveal_equal_spoilers">Mostrar Avisos de Contenido para respuestas de</string>
<string name="sk_settings_auto_reveal_nobody">nadie</string>
<string name="sk_settings_auto_reveal_author">autor</string>
<string name="sk_settings_auto_reveal_anyone">todos</string>
<string name="sk_settings_auto_reveal_equal_spoilers">Revelar automáticamente los CW iguales en las respuestas</string>
<string name="sk_settings_auto_reveal_nobody">Nunca</string>
<string name="sk_settings_auto_reveal_author">Respuestas del mismo autor</string>
<string name="sk_settings_auto_reveal_anyone">Respuestas de todos</string>
<string name="sk_open_in_app_failed">No se pudo abrir en la aplicación</string>
<string name="sk_no_remote_info_hint">no hay información remota disponible</string>
<string name="sk_error_loading_profile">No se pudo cargar el perfil a través de %s</string>
<string name="sk_settings_allow_remote_loading">Cargar la información desde las instancias remotas</string>
<string name="sk_settings_allow_remote_loading_explanation">Intenta obtener listas más precisas de seguidores, Me gusta y promociones cargando la información desde la instancia de origen.</string>
<string name="sk_settings_prefix_replies_always">Todas</string>
<string name="sk_settings_prefix_replies_never">Ninguna</string>
<string name="sk_settings_prefix_replies_to_others">A otros</string>
<string name="sk_settings_prefix_replies_always">Responder a cualquiera</string>
<string name="sk_settings_prefix_replies_never">Nunca</string>
<string name="sk_settings_prefix_replies_to_others">Solo en respuesta a otros</string>
<string name="sk_settings_forward_report_default">\"Reenviar denuncia\" activado por defecto</string>
<string name="sk_list_exclusive_switch">Hacer una lista exclusiva</string>
<string name="sk_icon_feed">Cronología</string>
@@ -328,4 +328,49 @@
<string name="sk_edit_timeline_tags_explanation">Por favor, ten en cuenta que es el servidor el gestiona estas operaciones. Puede que no sea factible combinarlas.</string>
<string name="sk_icon_beaker">Ciencia</string>
<string name="sk_icon_bed">Cama</string>
<string name="sk_gif_badge">GIF</string>
<string name="sk_spoiler_show">Mostrar el contenido</string>
<string name="sk_pronouns_label">Pronombres</string>
<string name="sk_switch_timeline">Cambiar la línea de tiempo</string>
<string name="sk_settings_instance">Instancia</string>
<string name="sk_disable_pill_shaped_active_indicator">Desactivar el indicador de pestaña activa en forma de pastilla</string>
<string name="sk_settings_true_black">Modo negro intenso</string>
<string name="sk_search_fediverse">Buscar en el Fediverso</string>
<string name="sk_settings_unifiedpush_choose">Selecciona un distribuidor</string>
<string name="sk_settings_unifiedpush_no_distributor">No se ha encontrado ningún distribuidor</string>
<string name="sk_settings_display_pronouns_in_user_listings">Mostrar pronombres en los listados de usuarios</string>
<string name="sk_tab_home">Inicio</string>
<string name="sk_tab_search">Buscar</string>
<string name="sk_tab_notifications">Notificaciones</string>
<string name="sk_tab_profile">Perfil</string>
<string name="sk_mute_label">Duración</string>
<string name="sk_duration_indefinite">Indefinido</string>
<string name="sk_duration_minutes_5">5 minutos</string>
<string name="sk_duration_minutes_30">30 minutos</string>
<string name="sk_duration_hours_1">1 hora</string>
<string name="sk_duration_hours_6">6 horas</string>
<string name="sk_duration_days_1">1 día</string>
<string name="sk_duration_days_3">3 días</string>
<string name="sk_duration_days_7">7 días</string>
<string name="sk_notification_mention">Has sido mencionado por %s</string>
<string name="sk_settings_continues_playback">Superposición del audio</string>
<string name="sk_settings_continues_playback_summary">Permitir que los medios que ya se están reproduciendo sigan reproduciéndose, superponiéndose a la nueva reproducción</string>
<string name="sk_settings_unifiedpush">Usar UnifiedPush</string>
<string name="sk_settings_unifiedpush_no_distributor_body">Es necesario instalar un distribuidor para que funcionen las notificaciones de UnifiedPush. Para obtener más información, visita https://unifiedpush.org/</string>
<string name="sk_settings_display_pronouns_in_timelines">Mostrar pronombres en líneas de tiempo</string>
<string name="sk_settings_display_pronouns_in_threads">Mostrar pronombres en los hilos</string>
<string name="sk_settings_show_labels_in_navigation_bar">Mostrar las etiquetas de las pestañas en la barra de navegación</string>
<string name="sk_settings_emoji_reactions_in_lists">Mostrar las reacciones con los emoticonos en las líneas de tiempo</string>
<plurals name="sk_users_reacted_with">
<item quantity="one">Un usuario reaccionó con %2$s</item>
<item quantity="many">%1$,d usuarios reaccionaron con %2$s</item>
<item quantity="other">%1$,d usuarios reaccionaron con %2$s</item>
</plurals>
<string name="sk_enter_emoji_toast">Necesitas escribir un emoticono</string>
<string name="sk_enter_emoji_hint">Escribe para reaccionar con un emoticono</string>
<string name="sk_settings_emoji_reactions">Activar las reacciones con los emoticonos</string>
<string name="sk_settings_emoji_reactions_explanation">Muestra las reacciones con los emoticonos a los mensajes y te permite interactuar con ellos. Algunas versiones modificadas de Mastodon lo soportan, pero Mastodon no.</string>
<string name="sk_settings_emoji_reactions_in_lists_explanation">Las reacciones con los emoticonos deben mostrarse en las líneas de tiempo. Si esta opción está desactivada, las reacciones con los emoticonos solo se mostrarán al ver un hilo.</string>
<string name="sk_button_react">Reacciona con un emoticono</string>
<string name="sk_again_for_system_keyboard">Pulsa de nuevo para el Teclado del sistema</string>
</resources>

View File

@@ -4,9 +4,11 @@
<string name="next">Hurrengoa</string>
<string name="loading_instance">Zerbitzariaren informazioa berreskuratzen…</string>
<string name="error">Errorea</string>
<string name="not_a_mastodon_instance">%s(e)k ez dirudi Mastodon instantzia bat denik.</string>
<string name="ok">Ados</string>
<string name="preparing_auth">Autentifikaziorako prestatzen…</string>
<string name="finishing_auth">Autentikazioa bukatzen…</string>
<string name="user_boosted">%s(e)k bultzatu du</string>
<string name="in_reply_to">%s-(r)i erantzunez</string>
<string name="notifications">Jakinarazpenak</string>
<string name="share_toot_title">Partekatu</string>
@@ -95,27 +97,35 @@
<string name="button_blocked">Blokeatuta</string>
<string name="action_vote">Bozkatu</string>
<string name="delete">Ezabatu</string>
<string name="confirm_delete_title">Ezabatu bidalketa</string>
<string name="confirm_delete">Ziur bidalketa hau ezabatu nahi duzula?</string>
<string name="deleting">Ezabatzen…</string>
<string name="notification_channel_audio_player">Audioa erreproduzitzen</string>
<string name="play">Jo</string>
<string name="pause">Pausatu</string>
<string name="log_out">Itxi saioa</string>
<string name="add_account">Gehitu kontua</string>
<string name="search_hint">Bilatu</string>
<string name="hashtags">Traolak</string>
<string name="news">Berriak</string>
<string name="for_you">Zuretzat</string>
<string name="all_notifications">Dena</string>
<string name="mentions">Aipamenak</string>
<plurals name="x_people_talking">
<item quantity="one">Pertsona %d hizketan</item>
<item quantity="other">%d pertsona hizketan</item>
</plurals>
<string name="report_title">Salatu %s</string>
<string name="report_choose_reason">Zer du txarra argitalpen honek?</string>
<string name="report_choose_reason_account">Zer du txarra %s?</string>
<string name="report_choose_reason_subtitle">Aukeratu egokiena</string>
<string name="report_reason_personal">Ez dut gustukoa</string>
<string name="report_reason_personal_subtitle">Ikusi nahi ez dudan zerbait da</string>
<string name="report_reason_spam">Spama da</string>
<string name="report_reason_spam_subtitle">Esteka maltzurrak, gezurrezko elkarrekintzak edo erantzun errepikakorrak</string>
<string name="report_reason_violation">Zerbitzariaren arauak hausten ditu</string>
<string name="report_reason_violation_subtitle">Arau zehatzak urratzen dituela badakizu</string>
<string name="report_reason_other">Beste zerbait da</string>
<string name="report_reason_other_subtitle">Arazoa ezin da beste kategorietan sailkatu</string>
<string name="report_choose_rule">Ze arau hautsi ditu?</string>
<string name="report_choose_rule_subtitle">Hautatu dagozkion guztiak</string>
@@ -124,8 +134,11 @@
<string name="report_comment_title">Beste zerbait jakin beharko genuke?</string>
<string name="report_comment_hint">Iruzkin gehigarriak</string>
<string name="sending_report">Txostena bidaltzen…</string>
<string name="report_sent_title">Mila esker salaketagatik, berrikusiko dugu.</string>
<string name="unfollow_user">%s jarraitzeari utzi</string>
<string name="unfollow">Utzi jarraitzeari</string>
<string name="report_personal_title">Ez duzu hau ikusi nahi?</string>
<string name="report_personal_subtitle">Hemen dituzu Mastodonen ikusiko duzuna kontrolatzeko aukerak:</string>
<string name="back">Atzera</string>
<string name="search_communities">Zerbitzari izena edo URLa</string>
<string name="instance_rules_title">Zerbitzariaren arauak</string>
@@ -151,23 +164,34 @@
<string name="category_tech">Teknologia</string>
<string name="confirm_email_title">Egiaztatu zure sarrerako ontzia</string>
<!-- %s is the email address -->
<string name="confirm_email_didnt_get">Ez duzu estekarik jaso?</string>
<string name="resend">Berbidali</string>
<string name="open_email_app">Ireki eposta aplikazioa</string>
<string name="resent_email">Berretzi eposta bidaltzea</string>
<string name="compose_hint">Idatzi edo itsatsi buruan duzuna</string>
<string name="content_warning">Edukiaren abisua</string>
<string name="save">Gorde</string>
<string name="add_alt_text">Gehitu ordezko testua</string>
<string name="visibility_public">Publikoa</string>
<string name="visibility_followers_only">Jarraitzaileak soilik</string>
<string name="visibility_private">Aipatzen dudan jendea soilik</string>
<string name="recent_searches">Azkenaldikoak</string>
<string name="skip">Saltatu</string>
<string name="notification_type_follow">Jarraitzaile berriak</string>
<string name="notification_type_favorite">Gogokoak</string>
<string name="notification_type_reblog">Bultzadak</string>
<string name="notification_type_mention">Aipamenak</string>
<string name="notification_type_poll">Inkestak</string>
<string name="choose_account">Aukeratu kontua</string>
<string name="err_not_logged_in">Mesedez, hasi saioa lehenengo Mastodonen</string>
<plurals name="cant_add_more_than_x_attachments">
<item quantity="one">Ezin duzu multimedia fitxategi %d baino gehiago gehitu</item>
<item quantity="other">Ezin dituzu %d baino multimedia fitxategi gehiago gehitu</item>
</plurals>
<string name="media_attachment_unsupported_type">%s fitxategi mota ez da bateragarria</string>
<string name="media_attachment_too_big">%1$s fitxategiak %2$s MB-eko tamainaren muga gainditzen du</string>
<string name="settings_theme">Itxura</string>
<string name="theme_auto">Erabili sistemaren diseinua</string>
<string name="theme_light">Argia</string>
<string name="theme_dark">Iluna</string>
<string name="settings_behavior">Jokabidea</string>
@@ -180,9 +204,13 @@
<string name="settings_clear_cache">Garbitu multimediaren cachea</string>
<string name="settings_app_version">Mastodon Android-entzat v%1$s (%2$d)</string>
<string name="media_cache_cleared">Multimediaren cachea garbitua</string>
<string name="confirm_log_out">Itxi saioa %s?</string>
<string name="sensitive_content_explain">Egileak multimedia hau hunkigarritzat markatu du.</string>
<string name="avatar_description">Joan %s-(r)en profilera</string>
<string name="more_options">Aukera gehiago</string>
<string name="new_post">Bidalketa berria</string>
<string name="button_reply">Erantzun</string>
<string name="button_reblog">Bultzada</string>
<string name="button_favorite">Gogokoa</string>
<string name="button_share">Partekatu</string>
<string name="media_no_description">Deskribapenik gabeko multimedia</string>
@@ -194,6 +222,7 @@
<string name="media_viewer">Multimedia ikuskatzailea</string>
<string name="follow_user">Jarraitu %s</string>
<string name="unfollowed_user">Utzi %s jarraitzeari</string>
<string name="followed_user">%s jarraitzen ari zara</string>
<string name="open_in_browser">Ireki nabigatzailean</string>
<string name="signup_reason">Zergatik elkartu nahi duzu?</string>
<string name="signup_reason_note">Honek zure eskaera berrikustean lagunduko digu.</string>
@@ -208,6 +237,9 @@
<string name="error_saving_file">Errorea fitxategia gordetzerakoan</string>
<string name="file_saved">Fitxategia gorde da</string>
<string name="downloading">Jeisten…</string>
<string name="no_app_to_handle_action">Ez dago ekintza hau kudeatu dezkeen aplikaziorik</string>
<string name="local_timeline">Lokala</string>
<string name="trending_posts_info_banner">Hauek dira zure Mastodon txokoan beraien lekua hartzen ari diren argitalpenak.</string>
<!-- %s is the server domain -->
<string name="see_new_posts">Ikusi bidalketa berriak</string>
<string name="load_missing_posts">Falta diren bidalketak kargatu</string>
@@ -290,22 +322,118 @@
<string name="server_filter_region_asia">Asia</string>
<string name="server_filter_region_oceania">Ozeania</string>
<string name="not_accepting_new_members">Ez da kide berririk onartzen</string>
<string name="signup_passwords_dont_match">Pasahitzak ez datoz bat</string>
<string name="pick_server_for_me">Aukeratu niretzat</string>
<string name="profile_add_row">Gehitu errenkada</string>
<string name="popular_on_mastodon">Mastodonen pil-pilean</string>
<string name="follow_all">Jarraitu denak</string>
<string name="server_rules_disagree">Ez ados</string>
<!-- %s is server domain -->
<string name="profile_bio">Biografia</string>
<!-- Shown in a progress dialog when you tap "follow all" -->
<!-- %1$s is server domain, %2$s is email domain. You can reorder these placeholders to fit your language better. -->
<string name="spoiler_show">Erakutsi hala ere</string>
<string name="poll_multiple_choice">Aukeratu bat edo gehiago</string>
<string name="save_changes">Gorde aldaketak</string>
<string name="profile_featured">Nabarmenak</string>
<string name="profile_timeline">Denbora-lerroa</string>
<string name="view_all">Ikusi guztia</string>
<string name="profile_endorsed_accounts">Kontuak</string>
<string name="show">Erakutsi</string>
<string name="hide">Ezkutatu</string>
<string name="pick_server">Aukeratu beste zerbitzari bat</string>
<string name="signup_or_login">edo</string>
<string name="learn_more">Ikasi gehiago</string>
<string name="welcome_to_mastodon">Ongi etorri Mastodon-era</string>
<string name="what_are_servers">Zer dira zerbitzariak?</string>
<string name="opening_link">Lotura irekitzen…</string>
<string name="retry">Berriro saiatu</string>
<!-- %s is formatted file size ("467 KB image") -->
<string name="attachment_type_image">Irudia</string>
<string name="attachment_type_video">Bideoa</string>
<string name="attachment_type_audio">Audioa</string>
<string name="attachment_type_gif">GIF-a</string>
<string name="attachment_type_unknown">Fitxategia</string>
<string name="add_poll_option">Gehitu inkesta aukera</string>
<string name="poll_style">Estiloa</string>
<string name="compose_poll_single_choice">Aukeratu bat</string>
<string name="compose_poll_multiple_choice">Aukera anitza</string>
<string name="delete_poll_option">Ezabatu inkesta aukera</string>
<string name="help">Laguntza</string>
<string name="edit_post">Editatu argitalpena</string>
<string name="compose_autocomplete_emoji_empty">Esploratu emojiak</string>
<string name="language">Hizkuntza</string>
<string name="language_default">Lehenetsia</string>
<string name="language_system">Sistema</string>
<string name="language_detecting">Hizkuntza detektatzen</string>
<string name="language_cant_detect">Ezin da hizkuntza detektatu</string>
<string name="language_detected">Detektatuta</string>
<string name="media_hidden">Multimedia ezkutatua</string>
<!-- %s is the server domain -->
<!-- Shown on the "stamp" on the screen that appears after you report a post/user. Please keep the translation short, preferably a single word -->
<string name="reported">Salatua</string>
<string name="muted_user">Mututu %s</string>
<string name="blocked_user">Blokeatu %s</string>
<string name="mark_all_notifications_read">Markatu denak irakurrita bezala</string>
<string name="settings_display">Bistaratzea</string>
<string name="settings_filters">Iragazkiak</string>
<!-- %s is the app name (Mastodon, key app_name). I made it a placeholder so everything Just Works™ with forks -->
<string name="about_app">%s(r)i buruz</string>
<string name="pause_all_notifications">Pausatu denak</string>
<string name="pause_notifications_off">Itzali</string>
<string name="notifications_policy_anyone">Edozein</string>
<string name="notifications_policy_followed">Zu jarraitzen zaituzten pertsonak</string>
<string name="notifications_policy_follower">Zuk jarraitzen dituzun pertsonak</string>
<string name="notifications_policy_no_one">Bat ere ez</string>
<string name="pause_all_notifications_title">Pausatu jakinarazpen guztiak</string>
<!-- %1$s is the date (may be relative, e.g. "today" or "yesterday"), %2$s is the time. You can reorder these placeholders if that works better for your language -->
<string name="today">gaur</string>
<string name="yesterday">atzo</string>
<string name="tomorrow">bihar</string>
<!-- %s is the timestamp ("tomorrow at 12:34") -->
<!-- %s is the timestamp ("tomorrow at 12:34") -->
<string name="about_server">Honi buruz</string>
<string name="server_rules">Arauak</string>
<string name="server_administrator">Administratzailea</string>
<string name="settings_show_cws">Erakutsi eduki abisuak</string>
<plurals name="in_x_hours">
<item quantity="one">Ordu %d barru</item>
<item quantity="other">%d ordu barru</item>
</plurals>
<string name="count_one">Bat</string>
<string name="count_two">Bi</string>
<string name="count_three">Hiru</string>
<string name="count_four">Lau</string>
<string name="alt_text_reminder_post_anyway">Bidalketa</string>
<!-- %s is the username -->
<string name="unfollow_confirmation">%s jarraitzeari utzi?</string>
<string name="filter_active">Aktiboa</string>
<string name="filter_inactive">Ez aktiboa</string>
<string name="settings_add_filter">Gehitu iragazkia</string>
<string name="settings_edit_filter">Editatu iragazkia</string>
<string name="settings_filter_duration">Iraupena</string>
<string name="settings_filter_muted_words">Mutututako hitzak</string>
<string name="settings_filter_show_cw">Erakutsi eduki abisuarekin</string>
<string name="settings_delete_filter">Ezabatu iragazkia</string>
<string name="filter_duration_forever">Betirakoa</string>
<!-- %s is the timestamp ("tomorrow at 12:34") -->
<string name="selection_2_options">%1$s eta %2$s</string>
<string name="selection_3_options">%1$s, %2$s, eta %3$s</string>
<string name="selection_4_or_more">%1$s, %2$s, eta beste %3$d</string>
<string name="filter_context_notifications">Jakinarazpenak</string>
<string name="filter_context_public_timelines">Denbora-lerro publikoak</string>
<string name="filter_context_profiles">Profilak</string>
<string name="settings_filter_title">Izenburua</string>
<string name="settings_delete_filter_title">Ezabatu \"%s\" iragazkia”?</string>
<string name="add">Gehitu</string>
<string name="enter_selection_mode">Hautatu</string>
<string name="select_all">Hautatu dena</string>
<string name="filter_duration_custom">Pertsonalizatua</string>
<string name="required_form_field_blank">Ezin da hutsik egon</string>
<string name="app_update_version">Bertsioa: %s</string>
<!-- Shown like a content warning, %s is the name of the filter -->
<string name="search_mastodon">Bilatu Mastodonen</string>
<string name="clear_all">Garbitu dena</string>
<string name="search_open_url">Ireki URLa Mastodonen</string>
<!-- Shown in the post header. Please keep it short -->
</resources>

View File

@@ -24,7 +24,7 @@
<string name="cancel">لغو</string>
<plurals name="followers">
<item quantity="one">پی‌گیر</item>
<item quantity="other">پی‌گیران</item>
<item quantity="other">پی‌گیرندگان</item>
</plurals>
<plurals name="following">
<item quantity="one">پی‌گرفته</item>
@@ -188,11 +188,11 @@
<string name="save">ذخیره</string>
<string name="add_alt_text">اضافه کردن متن جایگزین</string>
<string name="visibility_public">عمومی</string>
<string name="visibility_followers_only">فقط پی‌گیران</string>
<string name="visibility_followers_only">فقط پی‌گیرندگان</string>
<string name="visibility_private">فقط افرادی نام‌برده</string>
<string name="recent_searches">اخیر</string>
<string name="skip">بعدی</string>
<string name="notification_type_follow">پی‌گیران جدید</string>
<string name="notification_type_follow">پی‌گیرندگان جدید</string>
<string name="notification_type_favorite">برگزیده‌ها</string>
<string name="notification_type_reblog">تقویت‌ها</string>
<string name="notification_type_mention">نام‌بردن‌ها</string>
@@ -271,7 +271,7 @@
<!-- translators: %,d is a valid placeholder, it formats the number with locale-dependent grouping separators -->
<plurals name="x_followers">
<item quantity="one">%,d پی‌گیر</item>
<item quantity="other">%,d پی‌گیران</item>
<item quantity="other">%,d پی‌گیرندگان</item>
</plurals>
<plurals name="x_following">
<item quantity="one">%,d پی‌گیرنده</item>
@@ -419,6 +419,7 @@
<string name="edit_post">ویرایش فرسته</string>
<string name="no_verified_link">‏پیوند تأییدشده‌ای وجود ندارد</string>
<string name="compose_autocomplete_emoji_empty">مرور ایموجی</string>
<string name="compose_autocomplete_users_empty">پیدا کنید آنچه را که به دنبال آن هستید</string>
<string name="no_search_results">چیزی برای این عبارت جست‌وجو یافت نشد</string>
<string name="language">زبان</string>
<string name="language_default">پیش‌گزیده</string>
@@ -479,7 +480,10 @@
<string name="server_administrator">مدیر</string>
<string name="send_email_to_server_admin">ادمین پیام بده</string>
<string name="notifications_disabled_in_system">آگاهی‌ها را از تنظیمات دستگاهتان روشن کنید تا به‌روزرسانی‌ها را از هر کجا ببینید.</string>
<string name="settings_even_more">تنظیمات بیشتر</string>
<string name="settings_show_cws">نمایش هشدارهای محتوا</string>
<string name="settings_hide_sensitive_media">تصویرهایی را که به عنوان حساس علامت زده شده‌اند پنهان کن</string>
<string name="settings_show_interaction_counts">تعامل شمارش پست</string>
<string name="settings_show_emoji_in_names">ایموجی‌های سفارشی در نام های نمایشی</string>
<plurals name="in_x_seconds">
<item quantity="one">در %d ثانیه</item>

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