Compare commits

...

223 Commits

Author SHA1 Message Date
sk
c9f4df3d4e bump version, update changelog 2022-12-12 23:35:54 +01:00
sk22
9078667d51 Translated using Weblate (German)
Currently translated at 100.0% (59 of 59 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/de/
2022-12-12 22:29:04 +00:00
sk
7569e1aef6 add strings back 2022-12-12 23:26:52 +01:00
nitrogenez
723983dadf Translated using Weblate (Ukrainian)
Currently translated at 100.0% (8 of 8 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/uk/
2022-12-12 22:25:44 +00:00
lunarna
f87e020abd Translated using Weblate (Polish)
Currently translated at 25.0% (2 of 8 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/pl/
2022-12-12 22:25:44 +00:00
AiOO
fb5729d5cc Translated using Weblate (Korean)
Currently translated at 100.0% (8 of 8 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/ko/
2022-12-12 22:25:44 +00:00
Adolfo Jayme Barrientos
2ff6c53d6d Translated using Weblate (Spanish)
Currently translated at 100.0% (8 of 8 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/es/
2022-12-12 22:25:44 +00:00
edxkl
cfc6895711 Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (8 of 8 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/pt_BR/
2022-12-12 22:25:44 +00:00
Choukajohn
1c27fc68ee Translated using Weblate (French)
Currently translated at 100.0% (8 of 8 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/fr/
2022-12-12 22:25:44 +00:00
nitrogenez
df0d578573 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (56 of 56 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/uk/
2022-12-12 22:25:44 +00:00
edxkl
2fa3c69af1 Translated using Weblate (Portuguese (Brazil))
Currently translated at 96.4% (54 of 56 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/pt_BR/
2022-12-12 22:25:44 +00:00
lunarna
095bf92fed Translated using Weblate (Polish)
Currently translated at 100.0% (56 of 56 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/pl/
2022-12-12 22:25:44 +00:00
Choukajohn
debe017f12 Translated using Weblate (French)
Currently translated at 100.0% (56 of 56 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/fr/
2022-12-12 22:25:44 +00:00
Adolfo Jayme Barrientos
f956e12167 Translated using Weblate (Spanish)
Currently translated at 100.0% (56 of 56 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/es/
2022-12-12 22:25:44 +00:00
AiOO
2c50c38d82 Translated using Weblate (Korean)
Currently translated at 100.0% (56 of 56 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/ko/
2022-12-12 22:25:44 +00:00
sk22
b4980101ad Translated using Weblate (German)
Currently translated at 100.0% (56 of 56 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/de/
2022-12-12 22:25:44 +00:00
sk22
8395fca60f Translated using Weblate (English)
Currently translated at 100.0% (56 of 56 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/en/
2022-12-12 22:25:44 +00:00
AiOO
b22e7d277f Translated using Weblate (Korean)
Currently translated at 100.0% (7 of 7 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/ko/
2022-12-12 22:25:44 +00:00
edxkl
c0e67593ee Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (7 of 7 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/pt_BR/
2022-12-12 22:25:44 +00:00
edxkl
5dc4235724 Translated using Weblate (Portuguese (Brazil))
Currently translated at 97.8% (46 of 47 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/pt_BR/
2022-12-12 22:25:43 +00:00
kaea
f77caeefae Translated using Weblate (Polish)
Currently translated at 100.0% (47 of 47 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/pl/
2022-12-12 22:25:43 +00:00
Adolfo Jayme Barrientos
c1ef23bbe8 Translated using Weblate (Spanish)
Currently translated at 100.0% (47 of 47 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/es/
2022-12-12 22:25:43 +00:00
plutonemhikari
e7e80bcf7d Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (47 of 47 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/zh_Hans/
2022-12-12 22:25:43 +00:00
AiOO
c27f5aaf30 Translated using Weblate (Korean)
Currently translated at 100.0% (47 of 47 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/ko/
2022-12-12 22:25:43 +00:00
gallegonovato
d52728f22e Translated using Weblate (Spanish)
Currently translated at 100.0% (7 of 7 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/es/
2022-12-12 22:25:43 +00:00
Christian Elbrianno
3c7c962320 Translated using Weblate (Indonesian)
Currently translated at 71.4% (5 of 7 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/id/
2022-12-12 22:25:43 +00:00
Choukajohn
abf570d177 Translated using Weblate (French)
Currently translated at 100.0% (7 of 7 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/fr/
2022-12-12 22:25:43 +00:00
edxkl
46422cd62d Translated using Weblate (Portuguese (Brazil))
Currently translated at 93.6% (44 of 47 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/pt_BR/
2022-12-12 22:25:43 +00:00
tippete
f1ffa2629e Translated using Weblate (Italian)
Currently translated at 100.0% (47 of 47 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/it/
2022-12-12 22:25:43 +00:00
Christian Elbrianno
2074f3c33b Translated using Weblate (Indonesian)
Currently translated at 100.0% (47 of 47 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/id/
2022-12-12 22:25:43 +00:00
Choukajohn
7c51803674 Translated using Weblate (French)
Currently translated at 100.0% (47 of 47 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/fr/
2022-12-12 22:25:43 +00:00
Adolfo Jayme Barrientos
6d80c62f30 Translated using Weblate (Spanish)
Currently translated at 100.0% (47 of 47 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/es/
2022-12-12 22:25:43 +00:00
gallegonovato
64907a7e1c Translated using Weblate (Spanish)
Currently translated at 100.0% (47 of 47 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/es/
2022-12-12 22:25:43 +00:00
ca
17922ca1d5 Translated using Weblate (Catalan)
Currently translated at 100.0% (47 of 47 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/ca/
2022-12-12 22:25:43 +00:00
sk
01ac219854 remove strings 2022-12-12 23:25:31 +01:00
sk
9bbf8c4618 add custom login fragment 2022-12-12 23:18:01 +01:00
sk
978beaec77 use system default language for translation
fix #144
2022-12-12 09:09:45 +01:00
sk
0950e2eb7f Merge branch 'better-poll-voting' 2022-12-10 22:40:46 +01:00
sk
116328adb9 hide icons in own polls
closes #137
2022-12-10 22:40:35 +01:00
sk
32a2c66c34 Merge branch 'feature/language-selector' 2022-12-10 22:30:25 +01:00
sk
231ea46f9f change wording in string 2022-12-10 22:29:22 +01:00
sk
661f545e35 Merge branch 'feature/language-selector' 2022-12-10 22:24:05 +01:00
sk
600be455a3 save languages per account 2022-12-10 22:17:51 +01:00
sk
a4df06726f don't add to recent languages if replying 2022-12-10 21:31:04 +01:00
sk
e45e2c31d1 use mastodon languages list
fix #139
2022-12-10 21:07:34 +01:00
sk
d1e0cd3c20 Merge branch 'feature/display-reply-visibility' 2022-12-10 17:55:41 +01:00
sk
db16dde073 fix wrong visibility in reply
fix #140
2022-12-10 17:55:29 +01:00
sk
b3fe44bc08 update push settings on app start
fix #138, hopefully
2022-12-10 17:37:09 +01:00
sk
e5fab4a555 update changelog, bump version 2022-12-09 21:25:24 +01:00
sk
abe28179ec use saved default status visibility 2022-12-09 21:23:20 +01:00
sk
60d4e4d396 use default posting language 2022-12-09 21:15:11 +01:00
sk
435e73d718 Merge branch 'feature/language-selector' 2022-12-09 20:56:09 +01:00
sk
17dc0850d5 apply language when replying 2022-12-09 20:53:56 +01:00
sk
9667a32e44 save recently used languages 2022-12-09 20:48:51 +01:00
sk
4e6ba84bb3 implement language selector 2022-12-09 19:34:43 +01:00
sk
8714b24388 bump version 2022-12-09 03:15:01 +01:00
sk
495db142d7 update readme 2022-12-09 03:14:11 +01:00
sk
ac5d11159f Merge branch 'feature/translate-button' 2022-12-09 03:05:59 +01:00
sk
d1479f142b add progress spinner 2022-12-09 03:05:17 +01:00
sk
ee6ec631e8 Merge branch 'feature/translate-button' 2022-12-09 02:46:55 +01:00
sk
dee21222a7 implement translate feature 2022-12-09 02:37:38 +01:00
sk
cd8123ca34 Merge remote-tracking branch 'upstream/master' 2022-12-08 21:41:38 +01:00
sk
ceb08ea78d fix get started button color 2022-12-08 21:36:50 +01:00
Gregory K
b79ba71228 Merge pull request #445 from sk22/better-inline-emoji-search
Improve inline emoji search
2022-12-08 23:22:24 +03:00
Grishka
2903874dbc Splash fragment redesign 2022-12-08 23:22:10 +03:00
sk
cac5b554e2 Merge remote-tracking branch 'upstream/master' 2022-12-08 21:02:45 +01:00
sk
c4adbc8e45 update changelog/readme 2022-12-08 21:02:06 +01:00
sk
bb01077c3b Merge branch 'feature/mark-media-as-sensitive' 2022-12-08 21:00:43 +01:00
sk
b370fcda6d add bottom margin to sensitive toggle 2022-12-08 21:00:30 +01:00
sk
d364ebbb2f Merge branch 'better-poll-voting' 2022-12-08 20:56:19 +01:00
sk
5b28468efd add option to allow multiple poll choices 2022-12-08 20:53:34 +01:00
sk
6fd58c9682 improve semantics for poll options 2022-12-08 19:56:04 +01:00
sk
b580743619 fix poll option displaying wrong own vote
fixes #132
2022-12-08 16:03:46 +01:00
sk
4a9cb9f2dc fix poll option displaying wrong own vote
fixes #132
2022-12-08 15:53:35 +01:00
Grishka
202a5f9581 Fix #443 again 2022-12-08 01:48:23 +03:00
sk22
145f55817f Translated using Weblate (German)
Currently translated at 100.0% (7 of 7 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/de/
2022-12-07 18:20:46 +00:00
sk
79025c2f36 update description 2022-12-07 19:19:05 +01:00
sk
2515a8d381 add missing item to changelog 2022-12-07 19:16:14 +01:00
sk22
ede7ece25a Translated using Weblate (German)
Currently translated at 100.0% (7 of 7 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/de/
2022-12-07 18:11:14 +00:00
sk22
2db39f8c66 Translated using Weblate (German)
Currently translated at 100.0% (47 of 47 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/de/
2022-12-07 18:11:13 +00:00
sk
5f0382456f remove upstream fastlane changes 2022-12-07 19:09:13 +01:00
sk
63b1b58c4e use dashes instead of asterisks 2022-12-07 19:05:38 +01:00
sk
06f2f67f0c Merge branch 'main' of github.com:sk22/megalodon 2022-12-07 19:02:04 +01:00
sk
05c33be3f4 update readme, bump version 2022-12-07 19:01:58 +01:00
LucasGGamerM
e274b7e6d5 Implement a color picker (#124)
* Proper implementation on the color picker.
* Disabling the icons for the color picker menu
* Adding a green theme
* Making the green theme more readable
* More polishes over the green theme
* Adding blue theme and refactoring styles.xml
* Make badged settings icon follow accent colors
* Adding an icon to the color picker setting
* Fix readability issue on the light blue theme
* Adding orange theme, tweaking the blue and green theme
* Adding yellow theme
* Making it so that the fab follows the theme
* Fixing the TrueBlack themes for everything
* Make it so that the publish button also follows the theme
* Editing some drawable files to make them also follow the theme
* Making it so that the boost icon is also following the theme when clicked
* Make follow requests icon badge follow the color scheme and also make it that the profile top bar menu also follows the theme. This should be it

Co-authored-by: sk <sk22@mailbox.org>
2022-12-07 18:13:13 +01:00
sk
0806d0c5ea Merge branch 'external-share-include-subject' 2022-12-07 16:55:20 +01:00
sk
5c67dd0188 include subject line on external share
closes #123
2022-12-07 16:55:10 +01:00
sk
b4358f51cb Merge branch 'better-inline-emoji-search' 2022-12-07 16:35:58 +01:00
sk
622c6d503d improve inline emoji search
closes #131
closes mastodon#219
2022-12-07 16:34:52 +01:00
sk
b190480d77 add push post notifications
closes #93
2022-12-07 14:50:24 +01:00
sk
9a085beea8 fix #130 2022-12-07 14:24:13 +01:00
sk
1a42a77e24 fix #122 2022-12-07 14:12:11 +01:00
sk
e35794ef7d add new languages 2022-12-07 14:01:20 +01:00
sk
1f9611fc3e Merge remote-tracking branch 'upstream/master' 2022-12-07 13:55:36 +01:00
LucasGGamerM
563afd487c add new megalodon logo text
closes #129
2022-12-07 13:50:07 +01:00
Grishka
e10faeefc4 Update languages 2022-12-06 19:18:51 +03:00
Grishka
65dbbb3d61 Fix #443 2022-12-06 18:44:17 +03:00
Grishka
fa69868ca1 Merge branch 'l10n_master' 2022-12-06 18:33:35 +03:00
Eugen Rochko
9c18de7b90 New translations strings.xml (Icelandic) 2022-12-06 15:26:12 +01:00
Eugen Rochko
61bd19f6ff New translations strings.xml (Icelandic) 2022-12-06 14:24:50 +01:00
Eugen Rochko
ba0689aef7 New translations strings.xml (German) 2022-12-05 21:20:13 +01:00
Eugen Rochko
ad54e6bb4b New translations full_description.txt (German) 2022-12-05 20:15:33 +01:00
Eugen Rochko
f15fcb43da New translations strings.xml (German) 2022-12-05 20:15:32 +01:00
sk
66208f5694 make follow request button invisible until refreshed 2022-12-05 18:03:21 +01:00
sk
68863f28eb Merge branch 'better-poll-voting' 2022-12-05 17:57:46 +01:00
sk
7feaf093e2 Merge remote-tracking branch 'upstream/master' 2022-12-05 17:57:24 +01:00
sk
4ab9e25fec show own vote after voted
closes #113
2022-12-05 17:55:52 +01:00
sk
e14dfda2fd show poll vote button for single choice polls 2022-12-05 17:48:28 +01:00
sk
c9aae828e2 no ellipsis for poll options 2022-12-05 17:11:55 +01:00
sk
f346c0af26 Merge remote-tracking branch 'weblate/main' 2022-12-05 16:49:20 +01:00
Eugen Rochko
f2557b7815 New translations strings.xml (Thai) 2022-12-05 15:50:40 +01:00
Eugen Rochko
a2726f5b61 New translations strings.xml (Vietnamese) 2022-12-05 15:50:39 +01:00
gallegonovato
834ec1575d Translated using Weblate (Spanish)
Currently translated at 100.0% (5 of 5 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/es/
2022-12-05 13:40:26 +00:00
Eugen Rochko
a30f5bdee8 New translations strings.xml (Russian) 2022-12-05 12:12:10 +01:00
Eugen Rochko
4cef005286 New translations strings.xml (German) 2022-12-04 21:26:22 +01:00
Eugen Rochko
58a05681fe New translations strings.xml (Turkish) 2022-12-04 20:14:06 +01:00
Eugen Rochko
2589faf499 New translations full_description.txt (Hungarian) 2022-12-04 12:08:30 +01:00
Eugen Rochko
a5bdf34289 New translations strings.xml (French) 2022-12-04 10:30:25 +01:00
Eugen Rochko
09fdd7f492 New translations strings.xml (Turkish) 2022-12-04 06:23:19 +01:00
Eugen Rochko
519d8b887d New translations strings.xml (Filipino) 2022-12-03 23:52:40 +01:00
Eugen Rochko
a2f2263bf7 New translations strings.xml (Swedish) 2022-12-03 23:52:39 +01:00
Eugen Rochko
5b73b10b34 New translations strings.xml (Russian) 2022-12-03 23:52:38 +01:00
Eugen Rochko
b7a4364a28 New translations strings.xml (Portuguese) 2022-12-03 23:52:37 +01:00
Eugen Rochko
3f075aff7b New translations strings.xml (Korean) 2022-12-03 23:52:37 +01:00
Eugen Rochko
f4c33a5970 New translations strings.xml (Japanese) 2022-12-03 23:52:36 +01:00
Eugen Rochko
809af0ec18 New translations strings.xml (Italian) 2022-12-03 23:52:35 +01:00
Eugen Rochko
4ee640e072 New translations strings.xml (Armenian) 2022-12-03 23:52:34 +01:00
Eugen Rochko
1cbf310555 New translations strings.xml (Hebrew) 2022-12-03 23:52:33 +01:00
Eugen Rochko
f1fdc8aa43 New translations strings.xml (Turkish) 2022-12-03 23:52:32 +01:00
Eugen Rochko
d696daece3 New translations strings.xml (Czech) 2022-12-03 23:52:29 +01:00
Eugen Rochko
967bb09282 New translations strings.xml (Catalan) 2022-12-03 23:52:28 +01:00
Eugen Rochko
136d910b3b New translations strings.xml (Spanish) 2022-12-03 23:52:27 +01:00
Eugen Rochko
51eb48a455 New translations strings.xml (French) 2022-12-03 23:52:26 +01:00
Eugen Rochko
6ee8afcf96 New translations strings.xml (Arabic) 2022-12-03 23:52:25 +01:00
Eugen Rochko
a59f2d4609 New translations strings.xml (German) 2022-12-03 23:52:24 +01:00
Eugen Rochko
b75d871837 New translations strings.xml (Chinese Traditional) 2022-12-03 23:52:23 +01:00
Eugen Rochko
c72f93b990 New translations strings.xml (Basque) 2022-12-03 23:52:22 +01:00
Eugen Rochko
586d337ead New translations strings.xml (Polish) 2022-12-03 23:52:21 +01:00
Eugen Rochko
d84e10a22e New translations strings.xml (Ukrainian) 2022-12-03 23:52:20 +01:00
Eugen Rochko
351ec89207 New translations strings.xml (Vietnamese) 2022-12-03 23:52:19 +01:00
Eugen Rochko
7db7bf0220 New translations strings.xml (Hungarian) 2022-12-03 23:52:18 +01:00
Eugen Rochko
a9764c4f46 New translations strings.xml (Icelandic) 2022-12-03 23:52:17 +01:00
Eugen Rochko
a430b6a280 New translations strings.xml (Slovenian) 2022-12-03 23:52:16 +01:00
Eugen Rochko
6a01124d13 New translations strings.xml (Romanian) 2022-12-03 23:52:14 +01:00
Eugen Rochko
2843e445e2 New translations strings.xml (Chinese Simplified) 2022-12-03 23:52:12 +01:00
Eugen Rochko
5c947d14b2 New translations strings.xml (Scottish Gaelic) 2022-12-03 23:52:11 +01:00
Eugen Rochko
590adba3e3 New translations strings.xml (Indonesian) 2022-12-03 23:52:10 +01:00
Eugen Rochko
efee249173 New translations strings.xml (Dutch) 2022-12-03 23:52:09 +01:00
Eugen Rochko
6d2ed27364 New translations strings.xml (Kabyle) 2022-12-03 23:52:08 +01:00
Eugen Rochko
55716d742f New translations strings.xml (Bosnian) 2022-12-03 23:52:06 +01:00
Eugen Rochko
e4555da735 New translations strings.xml (Croatian) 2022-12-03 23:52:05 +01:00
Eugen Rochko
8b4b99bec7 New translations strings.xml (Thai) 2022-12-03 23:52:04 +01:00
Eugen Rochko
5de4b19969 New translations strings.xml (Galician) 2022-12-03 23:52:03 +01:00
Eugen Rochko
a9460f401e New translations strings.xml (Portuguese, Brazilian) 2022-12-03 23:52:01 +01:00
Grishka
012cca550e New login screen 2022-12-04 01:34:03 +03:00
Eugen Rochko
0c743db412 New translations strings.xml (German) 2022-12-03 22:56:40 +01:00
Eugen Rochko
b819ee7d6d New translations strings.xml (German) 2022-12-03 21:24:27 +01:00
Eugen Rochko
e7e3a249b5 New translations strings.xml (German) 2022-12-03 20:28:32 +01:00
Eugen Rochko
980c580b55 New translations full_description.txt (Hungarian) 2022-12-03 18:23:40 +01:00
Eugen Rochko
e23c530e74 New translations short_description.txt (Hungarian) 2022-12-03 17:20:17 +01:00
Eugen Rochko
a64caccca2 New translations full_description.txt (Hungarian) 2022-12-03 17:20:16 +01:00
wileyfoxyx
829bcafcf2 Translated using Weblate (Russian)
Currently translated at 20.0% (1 of 5 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/ru/
2022-12-03 09:28:09 +00:00
edxkl
e2a935c647 Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (5 of 5 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/pt_BR/
2022-12-03 09:28:08 +00:00
wileyfoxyx
2e7afdb49e Translated using Weblate (Russian)
Currently translated at 39.4% (15 of 38 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/ru/
2022-12-03 09:28:08 +00:00
edxkl
cdc965e026 Translated using Weblate (Portuguese (Portugal))
Currently translated at 5.2% (2 of 38 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/pt_PT/
2022-12-03 09:28:08 +00:00
Cloudstar
dd4faa005e Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (38 of 38 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/pt_BR/
2022-12-03 09:28:08 +00:00
Eugen Rochko
726ec7159c New translations strings.xml (Kabyle) 2022-12-03 07:19:07 +01:00
Eugen Rochko
e74256ef6f New translations strings.xml (Polish) 2022-12-02 22:37:01 +01:00
Eugen Rochko
a18718ca81 New translations strings.xml (Chinese Traditional) 2022-12-02 17:34:15 +01:00
Eugen Rochko
5a9bc0e269 New translations full_description.txt (German) 2022-12-02 16:33:35 +01:00
Eugen Rochko
2d39c62ff0 New translations strings.xml (German) 2022-12-02 16:33:34 +01:00
Eugen Rochko
0da4f79413 New translations strings.xml (Hungarian) 2022-12-02 14:17:58 +01:00
Eugen Rochko
2bdef776a2 New translations strings.xml (Hungarian) 2022-12-02 13:08:55 +01:00
wileyfoxyx
1819d6f042 Added translation using Weblate (Russian) 2022-12-02 09:00:36 +00:00
edxkl
2f6a707847 Translated using Weblate (Portuguese (Brazil))
Currently translated at 80.0% (4 of 5 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/pt_BR/
2022-12-02 05:30:42 +00:00
ca
4aaf017824 Translated using Weblate (Catalan)
Currently translated at 100.0% (5 of 5 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/ca/
2022-12-02 05:30:42 +00:00
edxkl
fb05ed48d0 Translated using Weblate (Portuguese (Brazil))
Currently translated at 94.7% (36 of 38 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/pt_BR/
2022-12-02 05:30:42 +00:00
mondstern
49203ae539 Translated using Weblate (Polish)
Currently translated at 100.0% (38 of 38 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/pl/
2022-12-02 05:30:42 +00:00
bart
d17660d516 Translated using Weblate (Dutch)
Currently translated at 100.0% (38 of 38 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/nl/
2022-12-02 05:30:41 +00:00
Choukajohn
513ce34671 Translated using Weblate (French)
Currently translated at 100.0% (38 of 38 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/fr/
2022-12-02 05:30:41 +00:00
ca
44ce48009b Translated using Weblate (Catalan)
Currently translated at 100.0% (38 of 38 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/ca/
2022-12-02 05:30:41 +00:00
Eugen Rochko
a57ad67308 New translations strings.xml (Italian) 2022-12-01 22:01:18 +01:00
Eugen Rochko
e63d04cea9 New translations strings.xml (Basque) 2022-12-01 22:01:17 +01:00
Eugen Rochko
cf48cb6f75 New translations strings.xml (Hungarian) 2022-12-01 19:43:19 +01:00
Eugen Rochko
542e53cf6a New translations strings.xml (Basque) 2022-12-01 19:43:18 +01:00
Eugen Rochko
bab1d40038 New translations strings.xml (Filipino) 2022-12-01 18:41:53 +01:00
sk
2f4a8247e8 update changelog 2022-12-01 16:37:21 +01:00
Eugen Rochko
18f605e5c5 New translations strings.xml (Catalan) 2022-11-30 22:54:44 +01:00
Eugen Rochko
cd8a80a6a1 New translations strings.xml (Slovenian) 2022-11-29 22:06:16 +01:00
Eugen Rochko
3ce8aa7894 New translations strings.xml (Hungarian) 2022-11-29 21:05:04 +01:00
Eugen Rochko
b356794da9 New translations strings.xml (Icelandic) 2022-11-29 18:17:42 +01:00
Eugen Rochko
afe8f6cf6a New translations strings.xml (Vietnamese) 2022-11-29 14:58:28 +01:00
Eugen Rochko
ed0df82fe9 New translations strings.xml (Thai) 2022-11-29 14:03:11 +01:00
Eugen Rochko
d3bc7a9790 New translations strings.xml (Japanese) 2022-11-29 05:13:57 +01:00
Eugen Rochko
633c0f870d New translations strings.xml (Galician) 2022-11-28 08:56:16 +01:00
Eugen Rochko
f9fe7819f9 New translations strings.xml (Hungarian) 2022-11-28 00:06:00 +01:00
Eugen Rochko
f3d13545e7 New translations strings.xml (Hungarian) 2022-11-27 23:06:27 +01:00
Eugen Rochko
f6b77777b5 New translations full_description.txt (Czech) 2022-11-27 20:34:15 +01:00
Eugen Rochko
340990fbd9 New translations strings.xml (Czech) 2022-11-27 20:34:14 +01:00
Eugen Rochko
a7687f8e35 New translations strings.xml (Czech) 2022-11-27 19:31:30 +01:00
Eugen Rochko
52aa4a5289 New translations strings.xml (Czech) 2022-11-27 17:43:12 +01:00
Eugen Rochko
268accea14 New translations strings.xml (Filipino) 2022-11-27 16:43:39 +01:00
Eugen Rochko
101cde4d84 New translations strings.xml (French) 2022-11-27 16:43:37 +01:00
Eugen Rochko
8863446f6a New translations strings.xml (Czech) 2022-11-27 15:03:46 +01:00
Eugen Rochko
28a0824f6b New translations strings.xml (Czech) 2022-11-27 13:59:12 +01:00
Eugen Rochko
b1f9d0516d New translations strings.xml (Dutch) 2022-11-27 11:30:42 +01:00
Eugen Rochko
5b21747d5d New translations strings.xml (Filipino) 2022-11-27 10:01:36 +01:00
Eugen Rochko
9fda48cff0 New translations strings.xml (Filipino) 2022-11-27 09:04:16 +01:00
Eugen Rochko
0e6f3df212 New translations strings.xml (Galician) 2022-11-27 07:48:06 +01:00
Eugen Rochko
a8c3f1555e New translations title.txt (Filipino) 2022-11-26 22:17:53 +01:00
Eugen Rochko
cd797a637b New translations short_description.txt (Filipino) 2022-11-26 22:17:53 +01:00
Eugen Rochko
53b2eb59d3 New translations full_description.txt (Filipino) 2022-11-26 22:17:52 +01:00
Eugen Rochko
09e2224596 New translations strings.xml (Filipino) 2022-11-26 22:17:51 +01:00
Eugen Rochko
5999aad21b New translations title.txt (Hungarian) 2022-11-26 22:17:50 +01:00
Eugen Rochko
874ce07c3e New translations short_description.txt (Hungarian) 2022-11-26 22:17:49 +01:00
Eugen Rochko
1787d08718 New translations full_description.txt (Hungarian) 2022-11-26 22:17:48 +01:00
Eugen Rochko
9a12be88da New translations strings.xml (Hungarian) 2022-11-26 22:17:47 +01:00
Eugen Rochko
8f6bb74e61 New translations strings.xml (Thai) 2022-11-26 21:16:11 +01:00
Eugen Rochko
e67bd2972a New translations strings.xml (Catalan) 2022-11-26 18:17:13 +01:00
Eugen Rochko
de5929d8d2 New translations strings.xml (Icelandic) 2022-11-26 16:28:32 +01:00
Eugen Rochko
d7699ef079 New translations strings.xml (Galician) 2022-11-26 15:29:20 +01:00
Eugen Rochko
3ab04ebca8 New translations strings.xml (Icelandic) 2022-11-26 12:55:03 +01:00
Eugen Rochko
78d2aa96d7 New translations strings.xml (Belarusian) 2022-11-25 01:47:56 +01:00
199 changed files with 5025 additions and 989 deletions

View File

@@ -3,12 +3,15 @@
# Megalodon
[![Translation status](https://translate.codeberg.org/widgets/megalodon/-/svg-badge.svg)](https://translate.codeberg.org/engage/megalodon/)
&nbsp;
[![Download latest release](https://img.shields.io/badge/dynamic/json?color=d92aad&label=Download%20APK&query=%24.tag_name&url=https%3A%2F%2Fapi.github.com%2Frepos%2Fsk22%2Fmegalodon%2Freleases%2Flatest&style=flat)](https://github.com/sk22/megalodon/releases/latest/download/megalodon.apk)
> A fork of the [official Mastodon Android app](https://github.com/mastodon/mastodon-android) adding important features that are missing in the official app and possibly wont ever be implemented, such as the federated timeline, unlisted posting, bookmarks and an image description viewer.
<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>
&nbsp;
<a href="https://apt.izzysoft.de/fdroid/index/apk/org.joinmastodon.android.sk"><img height="50" alt="Get it on F-Droid" src="img/f-droid-badge.png"></a>
**Warning! [A previous version's integrated updater was broken](https://github.com/sk22/megalodon/issues/106) I already published a fixed version! If you're not updating through Izzy's F-Droid repository (more sources to come, hopefully!), you'll have to download the current release manually. Sorry about that!**
> A fork of the [official Mastodon Android app](https://github.com/mastodon/mastodon-android) adding important features that are missing in the official app and possibly wont ever be implemented, such as the federated timeline, unlisted posting and an image description viewer.
[![Download latest release](https://img.shields.io/badge/dynamic/json?color=d92aad&label=download%20apk&query=%24.tag_name&url=https%3A%2F%2Fapi.github.com%2Frepos%2Fsk22%2Fmastodon-android-fork%2Freleases%2Flatest&style=for-the-badge)](https://github.com/sk22/megalodon/releases/latest/download/megalodon.apk)
---
@@ -43,26 +46,32 @@ This is important to **ensure the content youre sharing is as accessible as p
On the Fediverse, its quite common for people to pin posts they want others to read before following them. You can pin/unpin posts yourself by clicking the `⋯` button in the top right corner of your posts.
### **Bookmarks**
**They allow for quickly saving posts and viewing them through the Bookmarks button on the top right of your profile.**
To bookmark a post, press the button between the Favorite and Share buttons on the bottom of the post. Bookmarks are saved privately, so the post authors wont know you saved their post the list of bookmarked posts is only visible to you.
## Installation
**In short: Press the download button to download the APK. Open the downloaded file on your Android device to install it. Megalodon will automatically notify you about new updates inside the app.**
### From app stores
[![Download latest release](https://img.shields.io/badge/dynamic/json?color=d92aad&label=download%20apk&query=%24.tag_name&url=https%3A%2F%2Fapi.github.com%2Frepos%2Fsk22%2Fmastodon-android-fork%2Freleases%2Flatest&style=for-the-badge)](https://github.com/sk22/megalodon/releases/latest/download/megalodon.apk)
* **[Izzy's F-Droid repository](https://apt.izzysoft.de/fdroid/repo)**: [apt.izzysoft.de/fdroid/index/apk/org.joinmastodon.android.sk](https://apt.izzysoft.de/fdroid/index/apk/org.joinmastodon.android.sk)
To install this app on your Android device, download the [latest release from GitHub](https://github.com/sk22/megalodon/releases/latest/download/megalodon.apk) and open it. You might have to accept installing APK files from your browser when trying to install it. You can also take a look at all releases on the [Releases](https://github.com/sk22/megalodon/releases) page.
Note that you'll need to add Izzy's F-Droid repository to your F-Droid app first:
`https://apt.izzysoft.de/fdroid/repo`
* **[Google Play Store](https://play.google.com/store/apps/details?id=org.joinmastodon.android.sk)**: [play.google.com/store/apps/details?id=org.joinmastodon.android.sk](https://play.google.com/store/apps/details?id=org.joinmastodon.android.sk)
* **[F-Droid.org](https://f-droid.org)?** Not yet, sorry!
If you want, you can help me figure out if something's missing in the [Issue #47: F-Droid.org](https://github.com/sk22/megalodon/issues/47)
### Directly from GitHub
Press the download button to download the APK. Open the downloaded file on your Android device to install it. Megalodon will automatically notify you about new updates inside the app.
[![Download latest release](https://img.shields.io/badge/dynamic/json?color=d92aad&label=Download%20APK&query=%24.tag_name&url=https%3A%2F%2Fapi.github.com%2Frepos%2Fsk22%2Fmegalodon%2Freleases%2Flatest&style=flat)](https://github.com/sk22/megalodon/releases/latest/download/megalodon.apk)
You might have to accept installing APK files from your browser when trying to install it. You can also take a look at all releases on the [Releases](https://github.com/sk22/megalodon/releases) page.
Megalodon makes use of [Mastodon for Android](https://github.com/mastodon/mastodon-android)s automatic update checker. Megalodon will check for new updates available on GitHub and offer to download and install them. You can also manually press “Check for updates” at the bottom of the settings page!
### Other sources
* **[Izzy's F-Droid repository](https://apt.izzysoft.de/fdroid/repo)**: https://apt.izzysoft.de/fdroid/index/apk/org.joinmastodon.android.sk
---
@@ -92,7 +101,7 @@ As with the source code, the translation is sourced from the official project, w
There's also a handful of custom strings exclusive to this projects that would need to be translated. You can help translate **Megalodon** on Weblate: https://translate.codeberg.org/projects/megalodon/
[![Translation status](https://translate.codeberg.org/widgets/megalodon/-/multi-auto.svg)](https://translate.codeberg.org/engage/megalodon/)
[![Translation status](https://translate.codeberg.org/widgets/megalodon/-/horizontal-auto.svg)](https://translate.codeberg.org/engage/megalodon/)
---
@@ -122,6 +131,10 @@ There's also a handful of custom strings exclusive to this projects that would n
* [Show visibility of original post when replying](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/display-reply-visibility)
* [Clickable reply/boost line above posts](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:clickable-boost-reply-line)
* [Clickable reply line while replying to open original post](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/clickable-reply-line-compose)
* [Add push notification setting for post notifications](https://github.com/sk22/megalodon/commit/b190480d7739be47f23543d9e7644660f9b4b4ee)
* [Add option to allow voting for multiple options on polls](https://github.com/sk22/megalodon/commit/5b28468efd49387b4f8b83f142f3adf3104ca60c)
* [Add translate function](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/translate-button)
* [Add language selector](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/language-selector)
### Behavior
@@ -133,6 +146,12 @@ There's also a handful of custom strings exclusive to this projects that would n
* [Option to hide interaction numbers](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:settings/hide-interaction-numbers)
* [Option to always reveal content warnings](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/cw-above-text)
* [Option to disable scrolling title bars](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:settings/disable-marquee)
* [No ellipsis for long poll answers](https://github.com/mastodon/mastodon-android/commit/c9aae828e2518adccdc092e41f8d1f0489636271)
* [Show poll vote button for multiple and single answer polls](https://github.com/mastodon/mastodon-android/commit/e14dfda2fdf32f0fa3043504ac5831683a87559a)
* [Show own vote after voting](https://github.com/mastodon/mastodon-android/commit/4ab9e25fec4fd9c10b7a8ddd1be522b3cc12cf28) ([Closes issue](https://github.com/mastodon/mastodon-android/commit/4ab9e25fec4fd9c10b7a8ddd1be522b3cc12cf28))
* [Make inline emoji search case-insensitive and don't only search from start of emoji names](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:better-inline-emoji-search) ([Pull request](https://github.com/mastodon/mastodon-android/pull/445))
* [Include subject line when sharing e.g. a website to Megalodon](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:external-share-include-subject)
* [Improve semantics for voting on polls (radio buttons and checkboxes)](https://github.com/sk22/megalodon/commit/6fd58c96827cb1d2da329cebdc170a1425dd18d7)
### Visual
@@ -140,6 +159,9 @@ There's also a handful of custom strings exclusive to this projects that would n
* [Custom extended footer redesign](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:compact-extended-footer)
* [Improvements to the true black mode](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:true-black-improvements)
* [Profile header tweaks](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:ui/profile-header-tweaks)
* [Custom color themes](https://github.com/sk22/megalodon/pull/124) by [@LucasGGamerM](https://github.com/LucasGGamerM)
* [Custom "megalodon" text logo](https://github.com/sk22/megalodon/commit/563afd487ca5c608cfbb00fa3909d3c27384acc0) by [@LucasGGamerM](https://github.com/LucasGGamerM)
* [Custom login screen](https://github.com/sk22/megalodon/commit/9bbf8c4618dbe13accaeb3b5482bf3fe88cac4c0)
## Building

BIN
img/f-droid-badge.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -9,8 +9,8 @@ android {
applicationId "org.joinmastodon.android.sk"
minSdk 23
targetSdk 33
versionCode 55
versionName "1.1.4+fork.55"
versionCode 59
versionName "1.1.4+fork.59"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
resConfigs "en", "ar-rSA", "bs-rBA", "ca-rES", "cs-rCZ", "de-rDE", "el-rGR", "es-rES",
"eu-rES", "fi-rFI", "fr-rFR", "gl-rES", "hr-rHR", "hy-rAM", "it-rIT", "iw-rIL",

View File

@@ -51,7 +51,10 @@ public class ExternalShareActivity extends FragmentStackActivity{
getWindow().setBackgroundDrawable(null);
Intent intent=getIntent();
String text=intent.getStringExtra(Intent.EXTRA_TEXT);
StringBuilder builder=new StringBuilder();
if (intent.hasExtra(Intent.EXTRA_SUBJECT)) builder.append(intent.getStringExtra(Intent.EXTRA_SUBJECT)).append("\n");
if (intent.hasExtra(Intent.EXTRA_TEXT)) builder.append(intent.getStringExtra(Intent.EXTRA_TEXT)).append("\n");
String text=builder.toString();
List<Uri> mediaUris;
if(Intent.ACTION_SEND.equals(intent.getAction())){
Uri singleUri=intent.getParcelableExtra(Intent.EXTRA_STREAM);

View File

@@ -1,8 +1,18 @@
package org.joinmastodon.android;
import static org.joinmastodon.android.api.MastodonAPIController.gson;
import android.content.Context;
import android.content.SharedPreferences;
import com.google.gson.JsonSyntaxException;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class GlobalUserPreferences{
public static boolean playGifs;
public static boolean useCustomTabs;
@@ -14,12 +24,22 @@ public class GlobalUserPreferences{
public static boolean showInteractionCounts;
public static boolean alwaysExpandContentWarnings;
public static boolean disableMarquee;
public static boolean voteButtonForSingleChoice;
public static ThemePreference theme;
public static ColorPreference color;
private static SharedPreferences getPrefs(){
private final static Type recentLanguagesType = new TypeToken<Map<String, List<String>>>() {}.getType();
public static Map<String, List<String>> recentLanguages;
private static SharedPreferences getPrefs(){
return MastodonApp.context.getSharedPreferences("global", Context.MODE_PRIVATE);
}
private static <T> T fromJson(String json, Type type, T orElse) {
try { return gson.fromJson(json, type); }
catch (JsonSyntaxException ignored) { return orElse; }
}
public static void load(){
SharedPreferences prefs=getPrefs();
playGifs=prefs.getBoolean("playGifs", true);
@@ -32,7 +52,10 @@ public class GlobalUserPreferences{
showInteractionCounts=prefs.getBoolean("showInteractionCounts", false);
alwaysExpandContentWarnings=prefs.getBoolean("alwaysExpandContentWarnings", false);
disableMarquee=prefs.getBoolean("disableMarquee", false);
voteButtonForSingleChoice=prefs.getBoolean("voteButtonForSingleChoice", true);
theme=ThemePreference.values()[prefs.getInt("theme", 0)];
color=ColorPreference.values()[prefs.getInt("color", 0)];
recentLanguages=fromJson(prefs.getString("recentLanguages", "{}"), recentLanguagesType, new HashMap<>());
}
public static void save(){
@@ -48,12 +71,24 @@ public class GlobalUserPreferences{
.putBoolean("alwaysExpandContentWarnings", alwaysExpandContentWarnings)
.putBoolean("disableMarquee", disableMarquee)
.putInt("theme", theme.ordinal())
.putInt("color", color.ordinal())
.putString("recentLanguages", gson.toJson(recentLanguages))
.apply();
}
public enum ColorPreference{
PINK,
PURPLE,
GREEN,
BLUE,
BROWN,
YELLOW
}
public enum ThemePreference{
AUTO,
LIGHT,
DARK
}
}

View File

@@ -14,9 +14,9 @@ import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.fragments.ComposeFragment;
import org.joinmastodon.android.fragments.HomeFragment;
import org.joinmastodon.android.fragments.ProfileFragment;
import org.joinmastodon.android.fragments.SplashFragment;
import org.joinmastodon.android.fragments.ThreadFragment;
import org.joinmastodon.android.fragments.onboarding.AccountActivationFragment;
import org.joinmastodon.android.fragments.onboarding.MegalodonLoginFragment;
import org.joinmastodon.android.model.Notification;
import org.joinmastodon.android.ui.utils.UiUtils;
import org.joinmastodon.android.updater.GithubSelfUpdater;
@@ -33,7 +33,7 @@ public class MainActivity extends FragmentStackActivity{
if(savedInstanceState==null){
if(AccountSessionManager.getInstance().getLoggedInAccounts().isEmpty()){
showFragmentClearingBackStack(new SplashFragment());
showFragmentClearingBackStack(new MegalodonLoginFragment());
}else{
AccountSessionManager.getInstance().maybeUpdateLocalInfo();
AccountSession session;

View File

@@ -370,7 +370,7 @@ public class PushSubscriptionManager{
for(AccountSession session:AccountSessionManager.getInstance().getLoggedInAccounts()){
if(session.pushSubscription==null || forceReRegister)
session.getPushSubscriptionManager().registerAccountForPush(session.pushSubscription);
else if(session.needUpdatePushSettings)
else
session.getPushSubscriptionManager().updatePushSettings(session.pushSubscription);
}
}

View File

@@ -7,4 +7,15 @@ public class GetInstance extends MastodonAPIRequest<Instance>{
public GetInstance(){
super(HttpMethod.GET, "/instance", Instance.class);
}
public static class V2 extends MastodonAPIRequest<Instance.V2>{
public V2(){
super(HttpMethod.GET, "/instance", Instance.V2.class);
}
@Override
protected String getPathPrefix() {
return "/api/v2";
}
}
}

View File

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

View File

@@ -7,6 +7,7 @@ import org.joinmastodon.android.api.StatusInteractionController;
import org.joinmastodon.android.model.Account;
import org.joinmastodon.android.model.Application;
import org.joinmastodon.android.model.Filter;
import org.joinmastodon.android.model.Preferences;
import org.joinmastodon.android.model.PushSubscription;
import org.joinmastodon.android.model.Token;
@@ -28,6 +29,7 @@ public class AccountSession{
public long filtersLastUpdated;
public List<Filter> wordFilters=new ArrayList<>();
public String pushAccountID;
public Preferences preferences;
private transient MastodonAPIController apiController;
private transient StatusInteractionController statusInteractionController;
private transient CacheController cacheController;

View File

@@ -22,6 +22,7 @@ import org.joinmastodon.android.MastodonApp;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.MastodonAPIController;
import org.joinmastodon.android.api.PushSubscriptionManager;
import org.joinmastodon.android.api.requests.accounts.GetPreferences;
import org.joinmastodon.android.api.requests.accounts.GetWordFilters;
import org.joinmastodon.android.api.requests.instance.GetCustomEmojis;
import org.joinmastodon.android.api.requests.accounts.GetOwnAccount;
@@ -34,6 +35,7 @@ import org.joinmastodon.android.model.Emoji;
import org.joinmastodon.android.model.EmojiCategory;
import org.joinmastodon.android.model.Filter;
import org.joinmastodon.android.model.Instance;
import org.joinmastodon.android.model.Preferences;
import org.joinmastodon.android.model.Token;
import java.io.File;
@@ -248,12 +250,13 @@ public class AccountSessionManager{
HashSet<String> domains=new HashSet<>();
for(AccountSession session:sessions.values()){
domains.add(session.domain.toLowerCase());
if(now-session.infoLastUpdated>24L*3600_000L){
updateSessionLocalInfo(session);
}
if(now-session.filtersLastUpdated>3600_000L){
updateSessionWordFilters(session);
}
// if(now-session.infoLastUpdated>24L*3600_000L){
updateSessionPreferences(session);
updateSessionLocalInfo(session);
// }
// if(now-session.filtersLastUpdated>3600_000L){
updateSessionWordFilters(session);
// }
}
if(loadedInstances){
maybeUpdateCustomEmojis(domains);
@@ -263,10 +266,10 @@ public class AccountSessionManager{
private void maybeUpdateCustomEmojis(Set<String> domains){
long now=System.currentTimeMillis();
for(String domain:domains){
Long lastUpdated=instancesLastUpdated.get(domain);
if(lastUpdated==null || now-lastUpdated>24L*3600_000L){
updateInstanceInfo(domain);
}
// Long lastUpdated=instancesLastUpdated.get(domain);
// if(lastUpdated==null || now-lastUpdated>24L*3600_000L){
updateInstanceInfo(domain);
// }
}
}
@@ -288,6 +291,18 @@ public class AccountSessionManager{
.exec(session.getID());
}
private void updateSessionPreferences(AccountSession session){
new GetPreferences().setCallback(new Callback<>() {
@Override
public void onSuccess(Preferences preferences) {
session.preferences=preferences;
}
@Override
public void onError(ErrorResponse error) {}
}).exec(session.getID());
}
private void updateSessionWordFilters(AccountSession session){
new GetWordFilters()
.setCallback(new Callback<>(){
@@ -313,6 +328,11 @@ public class AccountSessionManager{
public void onSuccess(Instance instance){
instances.put(domain, instance);
updateInstanceEmojis(instance, domain);
try {
if (Integer.parseInt(instance.version.split("\\.")[0]) >= 4) {
updateInstanceInfoV2(domain);
}
} catch (Exception ignored) {}
}
@Override
@@ -323,6 +343,19 @@ public class AccountSessionManager{
.execNoAuth(domain);
}
public void updateInstanceInfoV2(String domain) {
new GetInstance.V2().setCallback(new Callback<>() {
@Override
public void onSuccess(Instance.V2 v2) {
Instance instanceInfo = instances.get(domain);
if (instanceInfo != null) instanceInfo.v2 = v2;
}
@Override
public void onError(ErrorResponse errorResponse) {}
}).execNoAuth(domain);
}
private void updateInstanceEmojis(Instance instance, String domain){
new GetCustomEmojis()
.setCallback(new Callback<>(){
@@ -398,6 +431,10 @@ public class AccountSessionManager{
return instances.get(domain);
}
public Instance getInstanceInfoForAccount(String account) {
return AccountSessionManager.getInstance().getInstanceInfo(instance.getAccount(account).domain);
}
public void updateAccountInfo(String id, Account account){
AccountSession session=getAccount(id);
session.self=account;

View File

@@ -400,10 +400,12 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
public void onPollOptionClick(PollOptionStatusDisplayItem.Holder holder){
Poll poll=holder.getItem().poll;
Poll.Option option=holder.getItem().option;
if(poll.multiple){
if(poll.multiple || GlobalUserPreferences.voteButtonForSingleChoice){
if(poll.selectedOptions==null)
poll.selectedOptions=new ArrayList<>();
if(poll.selectedOptions.contains(option)){
boolean optionContained=poll.selectedOptions.contains(option);
if(!poll.multiple) poll.selectedOptions.clear();
if(optionContained){
poll.selectedOptions.remove(option);
holder.itemView.setSelected(false);
}else{
@@ -412,6 +414,9 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
}
for(int i=0;i<list.getChildCount();i++){
RecyclerView.ViewHolder vh=list.getChildViewHolder(list.getChildAt(i));
if(!poll.multiple && vh instanceof PollOptionStatusDisplayItem.Holder item){
if (item != holder) item.itemView.setSelected(false);
}
if(vh instanceof PollFooterStatusDisplayItem.Holder footer){
if(footer.getItemID().equals(holder.getItemID())){
footer.rebind();

View File

@@ -1,5 +1,9 @@
package org.joinmastodon.android.fragments;
import static org.joinmastodon.android.GlobalUserPreferences.recentLanguages;
import static org.joinmastodon.android.utils.MastodonLanguage.allLanguages;
import static org.joinmastodon.android.utils.MastodonLanguage.defaultRecentLanguages;
import android.animation.ObjectAnimator;
import android.annotation.SuppressLint;
import android.app.Activity;
@@ -29,11 +33,13 @@ import android.text.Spanned;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.Log;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.SubMenu;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
@@ -41,6 +47,7 @@ import android.view.WindowManager;
import android.view.animation.LinearInterpolator;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.ImageButton;
@@ -54,6 +61,7 @@ import android.widget.Toast;
import com.twitter.twittertext.TwitterTextEmojiRegex;
import org.joinmastodon.android.E;
import org.joinmastodon.android.GlobalUserPreferences;
import org.joinmastodon.android.MastodonApp;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.MastodonAPIController;
@@ -94,6 +102,7 @@ import org.joinmastodon.android.ui.views.ComposeEditText;
import org.joinmastodon.android.ui.views.ComposeMediaLayout;
import org.joinmastodon.android.ui.views.ReorderableLinearLayout;
import org.joinmastodon.android.ui.views.SizeListenerLinearLayout;
import org.joinmastodon.android.utils.MastodonLanguage;
import org.parceler.Parcel;
import org.parceler.Parcels;
@@ -104,6 +113,7 @@ import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -144,7 +154,8 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
private String accountID;
private int charCount, charLimit, trimmedCharCount;
private Button publishButton;
private Button publishButton, languageButton;
private PopupMenu languagePopup;
private ImageButton mediaBtn, pollBtn, emojiBtn, spoilerBtn, visibilityBtn;
private ImageView sensitiveIcon;
private ComposeMediaLayout attachmentsView;
@@ -153,6 +164,8 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
private View pollWrap;
private View addPollOptionBtn;
private View sensitiveItem;
private View pollAllowMultipleItem;
private CheckBox pollAllowMultipleCheckbox;
private TextView pollDurationView;
private ArrayList<DraftPollOption> pollOptions=new ArrayList<>();
@@ -187,6 +200,9 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
private boolean ignoreSelectionChanges=false;
private Runnable updateUploadEtaRunnable;
private String language;
private MastodonLanguage.LanguageResolver languageResolver;
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
@@ -198,6 +214,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
instanceDomain=session.domain;
customEmojis=AccountSessionManager.getInstance().getCustomEmojis(instanceDomain);
instance=AccountSessionManager.getInstance().getInstanceInfo(instanceDomain);
languageResolver=new MastodonLanguage.LanguageResolver(instance);
if(getArguments().containsKey("editStatus")){
editingStatus=Parcels.unwrap(getArguments().getParcelable("editStatus"));
redraftStatus=getArguments().getBoolean("redraftStatus");
@@ -216,8 +233,6 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
charLimit=instance.configuration.statuses.maxCharacters;
else
charLimit=500;
loadDefaultStatusVisibility(savedInstanceState);
}
@Override
@@ -297,6 +312,9 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
pollOptionsView=view.findViewById(R.id.poll_options);
pollWrap=view.findViewById(R.id.poll_wrap);
addPollOptionBtn=view.findViewById(R.id.add_poll_option);
pollAllowMultipleItem=view.findViewById(R.id.poll_allow_multiple);
pollAllowMultipleCheckbox=view.findViewById(R.id.poll_allow_multiple_checkbox);
pollAllowMultipleItem.setOnClickListener(v->this.togglePollAllowMultiple());
addPollOptionBtn.setOnClickListener(v->{
createDraftPollOption().edit.requestFocus();
@@ -311,6 +329,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
pollBtn.setSelected(true);
mediaBtn.setEnabled(false);
pollWrap.setVisibility(View.VISIBLE);
updatePollAllowMultiple(savedInstanceState.getBoolean("pollAllowMultiple", false));
for(String oldText:savedInstanceState.getStringArrayList("pollOptions")){
DraftPollOption opt=createDraftPollOption();
opt.edit.setText(oldText);
@@ -321,6 +340,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
pollBtn.setSelected(true);
mediaBtn.setEnabled(false);
pollWrap.setVisibility(View.VISIBLE);
updatePollAllowMultiple(editingStatus.poll.multiple);
for(Poll.Option eopt:editingStatus.poll.options){
DraftPollOption opt=createDraftPollOption();
opt.edit.setText(eopt.title);
@@ -367,6 +387,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
statusVisibility=editingStatus.visibility;
}
loadDefaultStatusVisibility(savedInstanceState);
updateVisibilityIcon();
autocompleteViewController=new ComposeAutocompleteViewController(getActivity(), accountID);
@@ -391,9 +412,11 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
outState.putStringArrayList("pollOptions", opts);
outState.putInt("pollDuration", pollDuration);
outState.putString("pollDurationStr", pollDurationStr);
outState.putBoolean("pollAllowMultiple", pollAllowMultipleItem.isSelected());
}
outState.putBoolean("sensitive", sensitive);
outState.putBoolean("hasSpoiler", hasSpoiler);
outState.putString("language", language);
if(!attachments.isEmpty()){
ArrayList<Parcelable> serializedAttachments=new ArrayList<>(attachments.size());
for(DraftMediaAttachment att:attachments){
@@ -485,14 +508,14 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
spoilerEdit.addTextChangedListener(new SimpleTextWatcher(e->updateCharCounter()));
if(replyTo!=null){
replyText.setText(getString(R.string.in_reply_to, replyTo.account.displayName));
int visibilityNameRes = switch (statusVisibility) {
int visibilityNameRes = switch (replyTo.visibility) {
case PUBLIC -> R.string.visibility_public;
case UNLISTED -> R.string.sk_visibility_unlisted;
case PRIVATE -> R.string.visibility_followers_only;
case DIRECT -> R.string.visibility_private;
};
replyText.setContentDescription(getString(R.string.in_reply_to, replyTo.account.displayName) + ". " + getString(R.string.post_visibility) + ": " + getString(visibilityNameRes));
Drawable visibilityIcon = getActivity().getDrawable(switch(statusVisibility){
Drawable visibilityIcon = getActivity().getDrawable(switch(replyTo.visibility){
case PUBLIC -> R.drawable.ic_fluent_earth_20_regular;
case UNLISTED -> R.drawable.ic_fluent_people_community_20_regular;
case PRIVATE -> R.drawable.ic_fluent_people_checkmark_20_regular;
@@ -533,6 +556,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
spoilerEdit.setText(replyTo.spoilerText);
spoilerBtn.setSelected(true);
}
if (replyTo.language != null && !replyTo.language.isEmpty()) updateLanguage(replyTo.language);
}
}else if (editingStatus==null || editingStatus.inReplyToId==null){
// TODO: remove workaround after https://github.com/mastodon/mastodon-android/issues/341 gets fixed
@@ -545,6 +569,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
ignoreSelectionChanges=true;
mainEditText.setSelection(mainEditText.length());
ignoreSelectionChanges=false;
updateLanguage(editingStatus.language);
if(!editingStatus.mediaAttachments.isEmpty()){
attachmentsView.setVisibility(View.VISIBLE);
for(Attachment att:editingStatus.mediaAttachments){
@@ -607,6 +632,10 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
sendError.setVisibility(View.GONE);
sendProgress.setVisibility(View.GONE);
LinearLayout.LayoutParams langParams=new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
langParams.setMarginEnd(V.dp(8));
wrap.addView(buildLanguageSelector(), langParams);
wrap.addView(publishButton, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
wrap.setPadding(V.dp(16), V.dp(4), V.dp(16), V.dp(8));
wrap.setClipToPadding(false);
@@ -616,6 +645,56 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
updatePublishButtonState();
}
private void updateLanguage(String lang) {
updateLanguage(languageResolver.from(lang));
}
private void updateLanguage(MastodonLanguage loc) {
language = loc.getLanguage();
languageButton.setText(loc.getLanguageName());
languageButton.setContentDescription(getActivity().getString(R.string.sk_post_language, loc.getDefaultName()));
}
@SuppressLint("ClickableViewAccessibility")
private Button buildLanguageSelector() {
TypedValue typedValue = new TypedValue();
getActivity().getTheme().resolveAttribute(android.R.attr.textColorSecondary, typedValue, true);
languageButton=new Button(getActivity());
languageButton.setTextColor(typedValue.data);
languageButton.setBackground(getActivity().getDrawable(R.drawable.bg_text_button));
languageButton.setPadding(V.dp(8), 0, V.dp(8), 0);
languageButton.setCompoundDrawablesRelativeWithIntrinsicBounds(getActivity().getDrawable(R.drawable.ic_fluent_local_language_16_regular), null, null, null);
languageButton.setCompoundDrawableTintList(languageButton.getTextColors());
languageButton.setCompoundDrawablePadding(V.dp(6));
languagePopup=new PopupMenu(getActivity(), languageButton);
languageButton.setOnTouchListener(languagePopup.getDragToOpenListener());
languageButton.setOnClickListener(v->languagePopup.show());
updateLanguage(languageResolver.getDefault());
Menu languageMenu = languagePopup.getMenu();
for (String recentLanguage : Optional.ofNullable(recentLanguages.get(accountID)).orElse(defaultRecentLanguages)) {
MastodonLanguage l = languageResolver.from(recentLanguage);
languageMenu.add(0, allLanguages.indexOf(l), Menu.NONE, getActivity().getString(R.string.sk_language_name, l.getDefaultName(), l.getLanguageName()));
}
SubMenu allLanguagesMenu = languageMenu.addSubMenu(R.string.sk_available_languages);
for (int i = 0; i < allLanguages.size(); i++) {
MastodonLanguage l = allLanguages.get(i);
allLanguagesMenu.add(0, i, Menu.NONE, getActivity().getString(R.string.sk_language_name, l.getDefaultName(), l.getLanguageName()));
}
languagePopup.setOnMenuItemClickListener(i->{
if (i.hasSubMenu()) return false;
updateLanguage(allLanguages.get(i.getItemId()));
return true;
});
return languageButton;
}
@Override
public boolean onOptionsItemSelected(MenuItem item){
return true;
@@ -689,6 +768,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
req.status=text;
req.visibility=statusVisibility;
req.sensitive=sensitive;
req.language=language;
if(!attachments.isEmpty()){
req.mediaIds=attachments.stream().map(a->a.serverAttachment.id).collect(Collectors.toList());
}
@@ -698,6 +778,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
if(!pollOptions.isEmpty()){
req.poll=new CreateStatus.Request.Poll();
req.poll.expiresIn=pollDuration;
req.poll.multiple=pollAllowMultipleItem.isSelected();
for(DraftPollOption opt:pollOptions)
req.poll.options.add(opt.edit.getText().toString());
}
@@ -765,6 +846,14 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
.setCallback(resCallback)
.exec(accountID);
}
if (replyTo == null) {
List<String> newRecentLanguages = new ArrayList<>(Optional.ofNullable(recentLanguages.get(accountID)).orElse(defaultRecentLanguages));
newRecentLanguages.remove(language);
newRecentLanguages.add(0, language);
recentLanguages.put(accountID, newRecentLanguages.stream().limit(4).collect(Collectors.toList()));
GlobalUserPreferences.save();
}
}
private boolean hasDraft(){
@@ -1208,6 +1297,11 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
option.view=LayoutInflater.from(getActivity()).inflate(R.layout.compose_poll_option, pollOptionsView, false);
option.edit=option.view.findViewById(R.id.edit);
option.dragger=option.view.findViewById(R.id.dragger_thingy);
ImageView icon = option.view.findViewById(R.id.icon);
icon.setImageDrawable(getContext().getDrawable(pollAllowMultipleItem.isSelected() ?
R.drawable.ic_poll_checkbox_regular_selector :
R.drawable.ic_poll_option_button
));
option.dragger.setOnLongClickListener(v->{
pollOptionsView.startDragging(option.view);
@@ -1343,34 +1437,23 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
statusVisibility = (StatusPrivacy) savedInstanceState.getSerializable("visibility");
}
new GetPreferences()
.setCallback(new Callback<>(){
@Override
public void onSuccess(Preferences result){
// Only override the reply visibility if our preference is more private
if (result.postingDefaultVisibility.isLessVisibleThan(statusVisibility)) {
statusVisibility = switch (result.postingDefaultVisibility) {
case PUBLIC -> StatusPrivacy.PUBLIC;
case UNLISTED -> StatusPrivacy.UNLISTED;
case PRIVATE -> StatusPrivacy.PRIVATE;
case DIRECT -> StatusPrivacy.DIRECT;
};
}
Preferences prefs = AccountSessionManager.getInstance().getAccount(accountID).preferences;
if (prefs != null) {
// Only override the reply visibility if our preference is more private
if (prefs.postingDefaultVisibility.isLessVisibleThan(statusVisibility)) {
statusVisibility = switch (prefs.postingDefaultVisibility) {
case PUBLIC -> StatusPrivacy.PUBLIC;
case UNLISTED -> StatusPrivacy.UNLISTED;
case PRIVATE -> StatusPrivacy.PRIVATE;
case DIRECT -> StatusPrivacy.DIRECT;
};
}
// A saved privacy setting from a previous compose session wins over all
if(savedInstanceState !=null){
statusVisibility = (StatusPrivacy) savedInstanceState.getSerializable("visibility");
}
updateVisibilityIcon ();
}
@Override
public void onError(ErrorResponse error){
Log.w(TAG, "Unable to get user preferences to set default post privacy");
}
})
.exec(accountID);
// A saved privacy setting from a previous compose session wins over all
if(savedInstanceState !=null){
statusVisibility = (StatusPrivacy) savedInstanceState.getSerializable("visibility");
}
}
}
private void updateVisibilityIcon(){
@@ -1385,6 +1468,27 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
});
}
private void togglePollAllowMultiple() {
updatePollAllowMultiple(!pollAllowMultipleItem.isSelected());
}
private void updatePollAllowMultiple(boolean multiple){
pollAllowMultipleItem.setSelected(multiple);
pollAllowMultipleCheckbox.setChecked(multiple);
ImageView btn = addPollOptionBtn.findViewById(R.id.add_poll_option_icon);
btn.setImageDrawable(getContext().getDrawable(multiple ?
R.drawable.ic_fluent_add_square_24_regular :
R.drawable.ic_fluent_add_circle_24_regular
));
for (DraftPollOption opt:pollOptions) {
ImageView icon = opt.view.findViewById(R.id.icon);
icon.setImageDrawable(getContext().getDrawable(multiple ?
R.drawable.ic_poll_checkbox_regular_selector :
R.drawable.ic_poll_option_button
));
}
}
@Override
public void onSelectionChanged(int start, int end){
if(ignoreSelectionChanges)

View File

@@ -5,6 +5,7 @@ import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.app.Activity;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.os.Build;
import android.os.Bundle;
@@ -17,6 +18,7 @@ import android.view.ViewGroup;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toolbar;
@@ -56,7 +58,7 @@ import me.grishka.appkit.utils.V;
public class HomeTimelineFragment extends StatusListFragment{
private ImageButton fab;
private TextView toolbarLogo;
private ImageView toolbarLogo;
private Button toolbarShowNewPostsBtn;
private boolean newPostsBtnShown;
private AnimatorSet currentNewPostsAnim;
@@ -315,9 +317,10 @@ public class HomeTimelineFragment extends StatusListFragment{
}
private void updateToolbarLogo(){
toolbarLogo =new TextView(getActivity());
toolbarLogo.setText(getString(R.string.sk_app_name).toLowerCase(Locale.getDefault()));
toolbarLogo.setTextAppearance(R.style.app_title);
toolbarLogo=new ImageView(getActivity());
toolbarLogo.setScaleType(ImageView.ScaleType.CENTER);
toolbarLogo.setImageResource(R.drawable.logo);
toolbarLogo.setImageTintList(ColorStateList.valueOf(UiUtils.getThemeColor(getActivity(), android.R.attr.textColorPrimary)));
toolbarShowNewPostsBtn=new Button(getActivity());
toolbarShowNewPostsBtn.setTextAppearance(R.style.m3_title_medium);
@@ -345,7 +348,9 @@ public class HomeTimelineFragment extends StatusListFragment{
}
FrameLayout logoWrap=new FrameLayout(getActivity());
logoWrap.addView(toolbarLogo, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER));
FrameLayout.LayoutParams logoParams=new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER);
logoParams.setMargins(0, V.dp(2), 0, 0);
logoWrap.addView(toolbarLogo, logoParams);
logoWrap.addView(toolbarShowNewPostsBtn, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, V.dp(32), Gravity.CENTER));
Toolbar toolbar=getToolbar();

View File

@@ -555,11 +555,14 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
menu.findItem(R.id.mute).setTitle(getString(relationship.muting ? R.string.unmute_user : R.string.mute_user, account.getDisplayUsername()));
menu.findItem(R.id.block).setTitle(getString(relationship.blocking ? R.string.unblock_user : R.string.block_user, account.getDisplayUsername()));
menu.findItem(R.id.report).setTitle(getString(R.string.report_user, account.getDisplayUsername()));
MenuItem manageUserLists=menu.findItem(R.id.manage_user_lists);
if(relationship.following) {
menu.findItem(R.id.hide_boosts).setTitle(getString(relationship.showingReblogs ? R.string.hide_boosts_from_user : R.string.show_boosts_from_user, account.getDisplayUsername()));
manageUserLists.setTitle(getString(R.string.sk_lists_with_user, account.getDisplayUsername()));
manageUserLists.setVisible(true);
}else {
menu.findItem(R.id.hide_boosts).setVisible(false);
menu.findItem(R.id.manage_user_lists).setVisible(false);
manageUserLists.setVisible(false);
}
if(!account.isLocal())
menu.findItem(R.id.block_domain).setTitle(getString(relationship.domainBlocking ? R.string.unblock_domain : R.string.block_domain, account.getDomain()));

View File

@@ -48,6 +48,7 @@ import org.joinmastodon.android.ui.utils.UiUtils;
import org.joinmastodon.android.updater.GithubSelfUpdater;
import java.util.ArrayList;
import java.util.Objects;
import java.util.function.Consumer;
import androidx.annotation.DrawableRes;
@@ -99,6 +100,7 @@ public class SettingsFragment extends MastodonToolbarFragment{
GlobalUserPreferences.disableMarquee=i.checked;
GlobalUserPreferences.save();
}));
items.add(new ColorPicker());
items.add(new HeaderItem(R.string.settings_behavior));
items.add(new SwitchItem(R.string.settings_gif, R.drawable.ic_fluent_gif_24_regular, GlobalUserPreferences.playGifs, i->{
@@ -144,6 +146,7 @@ public class SettingsFragment extends MastodonToolbarFragment{
items.add(new SwitchItem(R.string.notify_follow, R.drawable.ic_fluent_person_add_24_regular, pushSubscription.alerts.follow, i->onNotificationsChanged(PushNotification.Type.FOLLOW, i.checked)));
items.add(new SwitchItem(R.string.notify_reblog, R.drawable.ic_fluent_arrow_repeat_all_24_regular, pushSubscription.alerts.reblog, i->onNotificationsChanged(PushNotification.Type.REBLOG, i.checked)));
items.add(new SwitchItem(R.string.notify_mention, R.drawable.ic_at_symbol, pushSubscription.alerts.mention, i->onNotificationsChanged(PushNotification.Type.MENTION, i.checked)));
items.add(new SwitchItem(R.string.sk_notify_posts, R.drawable.ic_fluent_alert_24_regular, pushSubscription.alerts.status, i->onNotificationsChanged(PushNotification.Type.STATUS, i.checked)));
items.add(new HeaderItem(R.string.settings_boring));
items.add(new TextItem(R.string.settings_account, ()->UiUtils.launchWebBrowser(getActivity(), "https://"+session.domain+"/auth/edit")));
@@ -157,6 +160,10 @@ public class SettingsFragment extends MastodonToolbarFragment{
}
items.add(new TextItem(R.string.sk_settings_contribute, ()->UiUtils.launchWebBrowser(getActivity(), "https://github.com/sk22/megalodon")));
items.add(new TextItem(R.string.settings_clear_cache, this::clearImageCache));
items.add(new TextItem(R.string.sk_clear_recent_languages, ()->UiUtils.showConfirmationAlert(getActivity(), R.string.sk_clear_recent_languages, R.string.sk_confirm_clear_recent_languages, R.string.clear, ()->{
GlobalUserPreferences.recentLanguages.remove(accountID);
GlobalUserPreferences.save();
})));
items.add(new TextItem(R.string.log_out, this::confirmLogOut));
items.add(new FooterItem(getString(R.string.sk_settings_app_version, BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE)));
@@ -234,6 +241,12 @@ public class SettingsFragment extends MastodonToolbarFragment{
restartActivityToApplyNewTheme();
}
private void onColorPreferenceClick(GlobalUserPreferences.ColorPreference color){
GlobalUserPreferences.color=color;
GlobalUserPreferences.save();
restartActivityToApplyNewTheme();
}
private void onTrueBlackThemeChanged(SwitchItem item){
GlobalUserPreferences.trueBlackTheme=item.checked;
GlobalUserPreferences.save();
@@ -293,6 +306,7 @@ public class SettingsFragment extends MastodonToolbarFragment{
case FOLLOW -> subscription.alerts.follow=enabled;
case REBLOG -> subscription.alerts.reblog=enabled;
case MENTION -> subscription.alerts.mention=subscription.alerts.poll=enabled;
case STATUS -> subscription.alerts.status=enabled;
}
needUpdateNotificationSettings=true;
}
@@ -436,6 +450,13 @@ public class SettingsFragment extends MastodonToolbarFragment{
}
}
public class ColorPicker extends Item{
@Override
public int getViewType(){
return 8;
}
}
private static class ThemeItem extends Item{
@Override
@@ -520,6 +541,7 @@ public class SettingsFragment extends MastodonToolbarFragment{
case 5 -> new HeaderViewHolder(true);
case 6 -> new FooterViewHolder();
case 7 -> new UpdateViewHolder();
case 8 -> new ColorPickerViewHolder();
default -> throw new IllegalStateException("Unexpected value: "+viewType);
};
}
@@ -648,6 +670,68 @@ public class SettingsFragment extends MastodonToolbarFragment{
}
}
}
private class ColorPickerViewHolder extends BindableViewHolder<ColorPicker>{
private final Button button;
private final PopupMenu popupMenu;
private final ImageView icon;
@SuppressLint("ClickableViewAccessibility")
public ColorPickerViewHolder(){
super(getActivity(), R.layout.item_settings_color_picker, list);
icon=findViewById(R.id.icon);
button=findViewById(R.id.color_picker_button);
popupMenu=new PopupMenu(getActivity(), button, Gravity.CENTER_HORIZONTAL);
popupMenu.inflate(R.menu.color_picker);
popupMenu.setOnMenuItemClickListener(item->{
GlobalUserPreferences.ColorPreference pref;
int id=item.getItemId();
if(id==R.id.pink_color) {
pref = GlobalUserPreferences.ColorPreference.PINK;
onColorPreferenceClick(pref);
}
else if(id==R.id.purple_color) {
pref = GlobalUserPreferences.ColorPreference.PURPLE;
onColorPreferenceClick(pref);
}
else if(id==R.id.green_color) {
pref = GlobalUserPreferences.ColorPreference.GREEN;
onColorPreferenceClick(pref);
}
else if(id==R.id.blue_color) {
pref = GlobalUserPreferences.ColorPreference.BLUE;
onColorPreferenceClick(pref);
}
else if(id==R.id.brown_color) {
pref = GlobalUserPreferences.ColorPreference.BROWN;
onColorPreferenceClick(pref);
}
else if(id==R.id.yellow_color) {
pref = GlobalUserPreferences.ColorPreference.YELLOW;
onColorPreferenceClick(pref);
}
else
return false;
return true;
});
// UiUtils.enablePopupMenuIcons(getActivity(), popupMenu);
button.setOnTouchListener(popupMenu.getDragToOpenListener());
button.setOnClickListener(v->popupMenu.show());
}
@Override
public void onBind(ColorPicker item){
icon.setImageResource(R.drawable.ic_fluent_color_24_regular);
button.setText(switch(GlobalUserPreferences.color){
case PINK -> R.string.sk_color_theme_pink;
case PURPLE -> R.string.sk_color_theme_purple;
case GREEN -> R.string.sk_color_theme_green;
case BLUE -> R.string.sk_color_theme_blue;
case BROWN -> R.string.sk_color_theme_brown;
case YELLOW -> R.string.sk_color_theme_yellow;
});
}
}
private class NotificationPolicyViewHolder extends BindableViewHolder<NotificationPolicyItem>{
private final Button button;

View File

@@ -1,20 +1,32 @@
package org.joinmastodon.android.fragments;
import android.content.res.Configuration;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.text.SpannableString;
import android.text.style.ImageSpan;
import android.text.style.ReplacementSpan;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.WindowInsets;
import android.widget.LinearLayout;
import android.widget.TextView;
import org.joinmastodon.android.MastodonApp;
import org.joinmastodon.android.R;
import org.joinmastodon.android.fragments.onboarding.InstanceCatalogFragment;
import org.joinmastodon.android.fragments.onboarding.InstanceCatalogSignupFragment;
import org.joinmastodon.android.fragments.onboarding.InstanceChooserLoginFragment;
import org.joinmastodon.android.ui.InterpolatingMotionEffect;
import org.joinmastodon.android.ui.views.SizeListenerFrameLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
import androidx.viewpager.widget.PagerAdapter;
import androidx.viewpager2.widget.ViewPager2;
import me.grishka.appkit.Nav;
import me.grishka.appkit.fragments.AppKitFragment;
import me.grishka.appkit.utils.V;
@@ -23,12 +35,13 @@ public class SplashFragment extends AppKitFragment{
private SizeListenerFrameLayout contentView;
private View artContainer, blueFill, greenFill;
private InterpolatingMotionEffect motionEffect;
private ViewPager2 pager;
private ViewGroup pagerDots;
private View artClouds, artPlaneElephant, artRightHill, artLeftHill, artCenterHill;
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
motionEffect=new InterpolatingMotionEffect(MastodonApp.context);
}
@Nullable
@@ -37,15 +50,44 @@ public class SplashFragment extends AppKitFragment{
contentView=(SizeListenerFrameLayout) inflater.inflate(R.layout.fragment_splash, container, false);
contentView.findViewById(R.id.btn_get_started).setOnClickListener(this::onButtonClick);
contentView.findViewById(R.id.btn_log_in).setOnClickListener(this::onButtonClick);
artClouds=contentView.findViewById(R.id.art_clouds);
artPlaneElephant=contentView.findViewById(R.id.art_plane_elephant);
artRightHill=contentView.findViewById(R.id.art_right_hill);
artLeftHill=contentView.findViewById(R.id.art_left_hill);
artCenterHill=contentView.findViewById(R.id.art_center_hill);
pager=contentView.findViewById(R.id.pager);
pagerDots=contentView.findViewById(R.id.pager_dots);
pager.setAdapter(new PagerAdapter());
pager.setOffscreenPageLimit(3);
pager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback(){
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels){
for(int i=0;i<pagerDots.getChildCount();i++){
float alpha;
if(i==position){
alpha=0.3f+0.7f*(1f-positionOffset);
}else if(i==position+1){
alpha=0.3f+0.7f*positionOffset;
}else{
alpha=0.3f;
}
pagerDots.getChildAt(i).setAlpha(alpha);
}
float parallaxProgress=(position+positionOffset)/2f;
artClouds.setTranslationX(V.dp(-27)*(position>=1 ? 1f : positionOffset));
artPlaneElephant.setTranslationX(V.dp(101.55f)*parallaxProgress);
artLeftHill.setTranslationX(V.dp(-88)*parallaxProgress);
artLeftHill.setTranslationY(V.dp(24)*parallaxProgress);
artRightHill.setTranslationX(V.dp(-88)*parallaxProgress);
artRightHill.setTranslationY(V.dp(-24)*parallaxProgress);
artCenterHill.setTranslationX(V.dp(-40)*parallaxProgress);
}
});
artContainer=contentView.findViewById(R.id.art_container);
blueFill=contentView.findViewById(R.id.blue_fill);
greenFill=contentView.findViewById(R.id.green_fill);
motionEffect.addViewEffect(new InterpolatingMotionEffect.ViewEffect(contentView.findViewById(R.id.art_clouds), V.dp(-5), V.dp(5), V.dp(-5), V.dp(5)));
motionEffect.addViewEffect(new InterpolatingMotionEffect.ViewEffect(contentView.findViewById(R.id.art_right_hill), V.dp(-15), V.dp(25), V.dp(-10), V.dp(10)));
motionEffect.addViewEffect(new InterpolatingMotionEffect.ViewEffect(contentView.findViewById(R.id.art_left_hill), V.dp(-25), V.dp(15), V.dp(-15), V.dp(15)));
motionEffect.addViewEffect(new InterpolatingMotionEffect.ViewEffect(contentView.findViewById(R.id.art_center_hill), V.dp(-14), V.dp(14), V.dp(-5), V.dp(25)));
motionEffect.addViewEffect(new InterpolatingMotionEffect.ViewEffect(contentView.findViewById(R.id.art_plane_elephant), V.dp(-20), V.dp(12), V.dp(-20), V.dp(12)));
contentView.setSizeListener(new SizeListenerFrameLayout.OnSizeChangedListener(){
@Override
@@ -66,15 +108,16 @@ public class SplashFragment extends AppKitFragment{
private void onButtonClick(View v){
Bundle extras=new Bundle();
extras.putBoolean("signup", v.getId()==R.id.btn_get_started);
Nav.go(getActivity(), InstanceCatalogFragment.class, extras);
boolean isSignup=v.getId()==R.id.btn_get_started;
extras.putBoolean("signup", isSignup);
Nav.go(getActivity(), isSignup ? InstanceCatalogSignupFragment.class : InstanceChooserLoginFragment.class, extras);
}
private void updateArtSize(int w, int h){
float scale=w/(float)V.dp(412);
float scale=w/(float)V.dp(360);
artContainer.setScaleX(scale);
artContainer.setScaleY(scale);
blueFill.setScaleY(h/2f);
blueFill.setScaleY(artContainer.getBottom()-V.dp(90));
greenFill.setScaleY(h-artContainer.getBottom()+V.dp(90));
}
@@ -100,15 +143,91 @@ public class SplashFragment extends AppKitFragment{
return true;
}
@Override
protected void onShown(){
super.onShown();
motionEffect.activate();
private class PagerAdapter extends RecyclerView.Adapter<PagerViewHolder>{
@NonNull
@Override
public PagerViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
return new PagerViewHolder(viewType);
}
@Override
public void onBindViewHolder(@NonNull PagerViewHolder holder, int position){}
@Override
public int getItemCount(){
return 3;
}
@Override
public int getItemViewType(int position){
return position;
}
}
@Override
protected void onHidden(){
super.onHidden();
motionEffect.deactivate();
private class PagerViewHolder extends RecyclerView.ViewHolder{
public PagerViewHolder(int page){
super(new LinearLayout(getActivity()));
LinearLayout ll=(LinearLayout) itemView;
ll.setOrientation(LinearLayout.VERTICAL);
int pad=V.dp(16);
ll.setPadding(pad, pad, pad, pad);
ll.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
TextView title=new TextView(getActivity());
title.setTextAppearance(R.style.m3_headline_medium);
title.setText(switch(page){
case 0 -> {
String src=getString(R.string.welcome_page1_title);
SpannableString ss=new SpannableString(src);
int start=src.indexOf("{logo}");
if(start!=-1){
LogoSpan span=new LogoSpan(getResources().getDrawable(R.drawable.splash_logo, getActivity().getTheme()));
ss.setSpan(span, start, start+6, 0);
}
yield ss;
}
case 1 -> getString(R.string.welcome_page2_title);
case 2 -> getString(R.string.welcome_page3_title);
default -> throw new IllegalStateException("Unexpected value: "+page);
});
title.setTextColor(0xFF17063B);
LinearLayout.LayoutParams lp=new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, V.dp(page==0 ? 46 : 36));
lp.bottomMargin=V.dp(page==0 ? 4 : 14);
ll.addView(title, lp);
TextView text=new TextView(getActivity());
text.setTextAppearance(R.style.m3_body_medium);
text.setText(switch(page){
case 0 -> R.string.welcome_page1_text;
case 1 -> R.string.welcome_page2_text;
case 2 -> R.string.welcome_page3_text;
default -> throw new IllegalStateException("Unexpected value: "+page);
});
text.setTextColor(0xFF17063B);
ll.addView(text, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
}
}
private class LogoSpan extends ReplacementSpan{
private final Drawable drawable;
private LogoSpan(Drawable drawable){
this.drawable=drawable;
}
@Override
public int getSize(@NonNull Paint paint, CharSequence text, int start, int end, @Nullable Paint.FontMetricsInt fm){
return drawable.getIntrinsicWidth();
}
@Override
public void draw(@NonNull Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, @NonNull Paint paint){
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
canvas.save();
canvas.translate(x, y-V.dp(20));
drawable.draw(canvas);
canvas.restore();
}
}
}

View File

@@ -286,13 +286,16 @@ public abstract class BaseAccountListFragment extends BaseRecyclerFragment<BaseA
menu.findItem(R.id.mute).setTitle(getString(relationship.muting ? R.string.unmute_user : R.string.mute_user, account.getDisplayUsername()));
menu.findItem(R.id.block).setTitle(getString(relationship.blocking ? R.string.unblock_user : R.string.block_user, account.getDisplayUsername()));
menu.findItem(R.id.report).setTitle(getString(R.string.report_user, account.getDisplayUsername()));
menu.findItem(R.id.manage_user_lists).setTitle(getString(R.string.sk_lists_with_user, account.getDisplayUsername()));
MenuItem hideBoosts=menu.findItem(R.id.hide_boosts);
MenuItem manageUserLists=menu.findItem(R.id.manage_user_lists);
if(relationship.following){
hideBoosts.setTitle(getString(relationship.showingReblogs ? R.string.hide_boosts_from_user : R.string.show_boosts_from_user, account.getDisplayUsername()));
hideBoosts.setVisible(true);
manageUserLists.setTitle(getString(R.string.sk_lists_with_user, account.getDisplayUsername()));
manageUserLists.setVisible(true);
}else{
hideBoosts.setVisible(false);
manageUserLists.setVisible(true);
}
MenuItem blockDomain=menu.findItem(R.id.block_domain);
if(!account.isLocal()){

View File

@@ -2,46 +2,30 @@ package org.joinmastodon.android.fragments.onboarding;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.LocaleList;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowInsets;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.RadioButton;
import android.widget.TextView;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.MastodonAPIController;
import org.joinmastodon.android.api.MastodonAPIRequest;
import org.joinmastodon.android.api.MastodonErrorResponse;
import org.joinmastodon.android.api.requests.instance.GetInstance;
import org.joinmastodon.android.api.requests.catalog.GetCatalogCategories;
import org.joinmastodon.android.api.requests.catalog.GetCatalogInstances;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.model.Instance;
import org.joinmastodon.android.model.catalog.CatalogCategory;
import org.joinmastodon.android.model.catalog.CatalogInstance;
import org.joinmastodon.android.ui.BetterItemAnimator;
import org.joinmastodon.android.ui.DividerItemDecoration;
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
import org.joinmastodon.android.ui.tabs.TabLayout;
import org.joinmastodon.android.ui.utils.UiUtils;
import org.parceler.Parcels;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
@@ -59,317 +43,53 @@ import java.util.stream.Collectors;
import javax.xml.parsers.DocumentBuilderFactory;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.RecyclerView;
import me.grishka.appkit.Nav;
import me.grishka.appkit.api.Callback;
import me.grishka.appkit.api.ErrorResponse;
import me.grishka.appkit.fragments.BaseRecyclerFragment;
import me.grishka.appkit.utils.BindableViewHolder;
import me.grishka.appkit.utils.MergeRecyclerAdapter;
import me.grishka.appkit.utils.SingleViewRecyclerAdapter;
import me.grishka.appkit.utils.V;
import me.grishka.appkit.views.UsableRecyclerView;
import okhttp3.Call;
import okhttp3.Request;
import okhttp3.Response;
public class InstanceCatalogFragment extends BaseRecyclerFragment<CatalogInstance>{
private InstancesAdapter adapter;
private MergeRecyclerAdapter mergeAdapter;
private View headerView;
private CatalogInstance chosenInstance;
private List<CatalogInstance> filteredData=new ArrayList<>();
private Button nextButton;
private MastodonAPIRequest<?> getCategoriesRequest;
private EditText searchEdit;
private TabLayout categoriesList;
private Runnable searchDebouncer=this::onSearchChangedDebounced;
private String currentSearchQuery;
private String currentCategory="all";
private List<CatalogCategory> categories=new ArrayList<>();
private String loadingInstanceDomain;
private GetInstance loadingInstanceRequest;
private Call loadingInstanceRedirectRequest;
private HashMap<String, Instance> instancesCache=new HashMap<>();
private ProgressDialog instanceProgressDialog;
private View buttonBar;
private HashMap<String, String> redirects=new HashMap<>(), redirectsInverse=new HashMap<>();
private boolean isSignup;
abstract class InstanceCatalogFragment extends BaseRecyclerFragment<CatalogInstance>{
protected RecyclerView.Adapter adapter;
protected MergeRecyclerAdapter mergeAdapter;
protected CatalogInstance chosenInstance;
protected Button nextButton;
protected EditText searchEdit;
protected Runnable searchDebouncer=this::onSearchChangedDebounced;
protected String currentSearchQuery;
protected String loadingInstanceDomain;
protected HashMap<String, Instance> instancesCache=new HashMap<>();
protected View buttonBar;
protected List<CatalogInstance> filteredData=new ArrayList<>();
protected GetInstance loadingInstanceRequest;
protected Call loadingInstanceRedirectRequest;
protected ProgressDialog instanceProgressDialog;
protected HashMap<String, String> redirects=new HashMap<>();
protected HashMap<String, String> redirectsInverse=new HashMap<>();
protected boolean isSignup;
protected CatalogInstance fakeInstance=new CatalogInstance();
private static final double DUNBAR=Math.log(800);
public InstanceCatalogFragment(){
super(R.layout.fragment_onboarding_common, 10);
public InstanceCatalogFragment(int layout, int perPage){
super(layout, perPage);
}
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
isSignup=getArguments().getBoolean("signup");
isSignup=getArguments() != null && getArguments().getBoolean("signup");
}
@Override
public void onAttach(Context context){
super.onAttach(context);
setRefreshEnabled(false);
loadData();
}
protected abstract void proceedWithAuthOrSignup(Instance instance);
@Override
protected void doLoadData(int offset, int count){
currentRequest=new GetCatalogInstances(null, null)
.setCallback(new Callback<>(){
@Override
public void onSuccess(List<CatalogInstance> result){
if(getActivity()==null)
return;
Map<String, List<CatalogInstance>> byLang=result.stream().collect(Collectors.groupingBy(ci->ci.language));
for(List<CatalogInstance> group:byLang.values()){
Collections.sort(group, (a, b)->{
double aa=Math.abs(DUNBAR-Math.log(a.lastWeekUsers));
double bb=Math.abs(DUNBAR-Math.log(b.lastWeekUsers));
return Double.compare(aa, bb);
});
}
// get the list of user-configured system languages
List<String> userLangs;
if(Build.VERSION.SDK_INT<24){
userLangs=Collections.singletonList(getResources().getConfiguration().locale.getLanguage());
}else{
LocaleList ll=getResources().getConfiguration().getLocales();
userLangs=new ArrayList<>(ll.size());
for(int i=0;i<ll.size();i++){
userLangs.add(ll.get(i).getLanguage());
}
}
// add instances in preferred languages to the top of the list, in the order of preference
ArrayList<CatalogInstance> sortedList=new ArrayList<>();
for(String lang:userLangs){
List<CatalogInstance> langInstances=byLang.remove(lang);
if(langInstances!=null){
sortedList.addAll(langInstances);
}
}
// sort the remaining language groups by aggregate lastWeekUsers
class InstanceGroup{
public int activeUsers;
public List<CatalogInstance> instances;
}
byLang.values().stream().map(il->{
InstanceGroup group=new InstanceGroup();
group.instances=il;
for(CatalogInstance instance:il){
group.activeUsers+=instance.lastWeekUsers;
}
return group;
}).sorted(Comparator.comparingInt((InstanceGroup g)->g.activeUsers).reversed()).forEachOrdered(ig->sortedList.addAll(ig.instances));
onDataLoaded(sortedList, false);
updateFilteredList();
}
@Override
public void onError(ErrorResponse error){
error.showToast(getActivity());
onDataLoaded(Collections.emptyList(), false);
}
})
.execNoAuth("");
getCategoriesRequest=new GetCatalogCategories(null)
.setCallback(new Callback<>(){
@Override
public void onSuccess(List<CatalogCategory> result){
getCategoriesRequest=null;
CatalogCategory all=new CatalogCategory();
all.category="all";
categories.add(all);
result.stream().sorted(Comparator.comparingInt((CatalogCategory cc)->cc.serversCount).reversed()).forEach(categories::add);
updateCategories();
}
@Override
public void onError(ErrorResponse error){
getCategoriesRequest=null;
error.showToast(getActivity());
CatalogCategory all=new CatalogCategory();
all.category="all";
categories.add(all);
updateCategories();
}
})
.execNoAuth("");
}
private void updateCategories(){
categoriesList.removeAllTabs();
for(CatalogCategory cat:categories){
int titleRes=getTitleForCategory(cat.category);
TabLayout.Tab tab=categoriesList.newTab().setText(titleRes!=0 ? getString(titleRes) : cat.category).setCustomView(R.layout.item_instance_category);
ImageView emoji=tab.getCustomView().findViewById(R.id.emoji);
emoji.setImageResource(getEmojiForCategory(cat.category));
categoriesList.addTab(tab);
}
}
@Override
public void onDestroy(){
super.onDestroy();
if(getCategoriesRequest!=null)
getCategoriesRequest.cancel();
}
@Override
protected RecyclerView.Adapter getAdapter(){
headerView=getActivity().getLayoutInflater().inflate(R.layout.header_onboarding_instance_catalog, list, false);
searchEdit=headerView.findViewById(R.id.search_edit);
categoriesList=headerView.findViewById(R.id.categories_list);
categoriesList.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener(){
@Override
public void onTabSelected(TabLayout.Tab tab){
CatalogCategory category=categories.get(tab.getPosition());
currentCategory=category.category;
updateFilteredList();
}
@Override
public void onTabUnselected(TabLayout.Tab tab){
}
@Override
public void onTabReselected(TabLayout.Tab tab){
}
});
searchEdit.setOnEditorActionListener(this::onSearchEnterPressed);
searchEdit.addTextChangedListener(new TextWatcher(){
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after){
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count){
searchEdit.removeCallbacks(searchDebouncer);
searchEdit.postDelayed(searchDebouncer, 300);
}
@Override
public void afterTextChanged(Editable s){
}
});
mergeAdapter=new MergeRecyclerAdapter();
mergeAdapter.addAdapter(new SingleViewRecyclerAdapter(headerView));
mergeAdapter.addAdapter(adapter=new InstancesAdapter());
return mergeAdapter;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState){
super.onViewCreated(view, savedInstanceState);
nextButton=view.findViewById(R.id.btn_next);
nextButton.setOnClickListener(this::onNextClick);
nextButton.setEnabled(chosenInstance!=null);
view.findViewById(R.id.btn_back).setOnClickListener(v->Nav.finish(this));
list.setItemAnimator(new BetterItemAnimator());
list.addItemDecoration(new DividerItemDecoration(getActivity(), R.attr.colorPollVoted, 1, 16, 16, DividerItemDecoration.NOT_FIRST));
view.setBackgroundColor(UiUtils.getThemeColor(getActivity(), R.attr.colorBackgroundLight));
buttonBar=view.findViewById(R.id.button_bar);
setStatusBarColor(UiUtils.getThemeColor(getActivity(), R.attr.colorBackgroundLight));
}
private void onNextClick(View v){
String domain=chosenInstance.domain;
Instance instance=instancesCache.get(domain);
if(instance!=null){
proceedWithAuthOrSignup(instance);
}else{
showProgressDialog();
if(!domain.equals(loadingInstanceDomain)){
loadInstanceInfo(domain, false);
}
}
}
private void proceedWithAuthOrSignup(Instance instance){
getActivity().getSystemService(InputMethodManager.class).hideSoftInputFromWindow(contentView.getWindowToken(), 0);
if(isSignup){
if(!instance.registrations){
new M3AlertDialogBuilder(getActivity())
.setTitle(R.string.error)
.setMessage(R.string.instance_signup_closed)
.setPositiveButton(R.string.ok, null)
.show();
return;
}
Bundle args=new Bundle();
args.putParcelable("instance", Parcels.wrap(instance));
Nav.go(getActivity(), InstanceRulesFragment.class, args);
}else{
AccountSessionManager.getInstance().authenticate(getActivity(), instance);
}
}
// private String getEmojiForCategory(String category){
// return switch(category){
// case "all" -> "💬";
// case "academia" -> "📚";
// case "activism" -> "✊";
// case "food" -> "🍕";
// case "furry" -> "🦁";
// case "games" -> "🕹";
// case "general" -> "🐘";
// case "journalism" -> "📰";
// case "lgbt" -> "🏳️‍🌈";
// case "regional" -> "📍";
// case "art" -> "🎨";
// case "music" -> "🎼";
// case "tech" -> "📱";
// default -> "❓";
// };
// }
private int getEmojiForCategory(String category){
return switch(category){
case "all" -> R.drawable.ic_category_all;
case "academia" -> R.drawable.ic_category_academia;
case "activism" -> R.drawable.ic_category_activism;
case "food" -> R.drawable.ic_category_food;
case "furry" -> R.drawable.ic_category_furry;
case "games" -> R.drawable.ic_category_games;
case "general" -> R.drawable.ic_category_general;
case "journalism" -> R.drawable.ic_category_journalism;
case "lgbt" -> R.drawable.ic_category_lgbt;
case "regional" -> R.drawable.ic_category_regional;
case "art" -> R.drawable.ic_category_art;
case "music" -> R.drawable.ic_category_music;
case "tech" -> R.drawable.ic_category_tech;
default -> R.drawable.ic_category_unknown;
};
}
private int getTitleForCategory(String category){
return switch(category){
case "all" -> R.string.category_all;
case "academia" -> R.string.category_academia;
case "activism" -> R.string.category_activism;
case "food" -> R.string.category_food;
case "furry" -> R.string.category_furry;
case "games" -> R.string.category_games;
case "general" -> R.string.category_general;
case "journalism" -> R.string.category_journalism;
case "lgbt" -> R.string.category_lgbt;
case "regional" -> R.string.category_regional;
case "art" -> R.string.category_art;
case "music" -> R.string.category_music;
case "tech" -> R.string.category_tech;
default -> 0;
};
}
private boolean onSearchEnterPressed(TextView v, int actionId, KeyEvent event){
protected boolean onSearchEnterPressed(TextView v, int actionId, KeyEvent event){
if(event!=null && event.getAction()!=KeyEvent.ACTION_DOWN)
return true;
currentSearchQuery=searchEdit.getText().toString().toLowerCase();
@@ -385,60 +105,73 @@ public class InstanceCatalogFragment extends BaseRecyclerFragment<CatalogInstanc
return true;
}
private void onSearchChangedDebounced(){
protected void onSearchChangedDebounced(){
currentSearchQuery=searchEdit.getText().toString().toLowerCase();
updateFilteredList();
loadInstanceInfo(currentSearchQuery, false);
}
private void updateFilteredList(){
ArrayList<CatalogInstance> prevData=new ArrayList<>(filteredData);
filteredData.clear();
for(CatalogInstance instance:data){
if(currentCategory.equals("all") || instance.categories.contains(currentCategory)){
if(TextUtils.isEmpty(currentSearchQuery) || instance.domain.contains(currentSearchQuery)){
if(instance.domain.equals(currentSearchQuery) || !isSignup || !instance.approvalRequired)
filteredData.add(instance);
}
protected List<CatalogInstance> sortInstances(List<CatalogInstance> result){
Map<String, List<CatalogInstance>> byLang=result.stream().collect(Collectors.groupingBy(ci->ci.language));
for(List<CatalogInstance> group:byLang.values()){
Collections.sort(group, (a, b)->{
double aa=Math.abs(DUNBAR-Math.log(a.lastWeekUsers));
double bb=Math.abs(DUNBAR-Math.log(b.lastWeekUsers));
return Double.compare(aa, bb);
});
}
// get the list of user-configured system languages
List<String> userLangs;
if(Build.VERSION.SDK_INT<24){
userLangs=Collections.singletonList(getResources().getConfiguration().locale.getLanguage());
}else{
LocaleList ll=getResources().getConfiguration().getLocales();
userLangs=new ArrayList<>(ll.size());
for(int i=0;i<ll.size();i++){
userLangs.add(ll.get(i).getLanguage());
}
}
DiffUtil.calculateDiff(new DiffUtil.Callback(){
@Override
public int getOldListSize(){
return prevData.size();
// add instances in preferred languages to the top of the list, in the order of preference
ArrayList<CatalogInstance> sortedList=new ArrayList<>();
for(String lang:userLangs){
List<CatalogInstance> langInstances=byLang.remove(lang);
if(langInstances!=null){
sortedList.addAll(langInstances);
}
@Override
public int getNewListSize(){
return filteredData.size();
}
// sort the remaining language groups by aggregate lastWeekUsers
class InstanceGroup{
public int activeUsers;
public List<CatalogInstance> instances;
}
byLang.values().stream().map(il->{
InstanceGroup group=new InstanceGroup();
group.instances=il;
for(CatalogInstance instance:il){
group.activeUsers+=instance.lastWeekUsers;
}
@Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition){
return prevData.get(oldItemPosition)==filteredData.get(newItemPosition);
}
@Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition){
return prevData.get(oldItemPosition)==filteredData.get(newItemPosition);
}
}).dispatchUpdatesTo(adapter);
return group;
}).sorted(Comparator.comparingInt((InstanceGroup g)->g.activeUsers).reversed()).forEachOrdered(ig->sortedList.addAll(ig.instances));
return sortedList;
}
private void showProgressDialog(){
protected abstract void updateFilteredList();
protected void showProgressDialog(){
instanceProgressDialog=new ProgressDialog(getActivity());
instanceProgressDialog.setMessage(getString(R.string.loading_instance));
instanceProgressDialog.setOnCancelListener(dialog->cancelLoadingInstanceInfo());
instanceProgressDialog.show();
}
private String normalizeInstanceDomain(String _domain){
protected String normalizeInstanceDomain(String _domain){
if(TextUtils.isEmpty(_domain))
return null;
if(_domain.contains(":")){
try{
_domain=Uri.parse(_domain).getAuthority();
}catch(Exception ignore){}
}catch(Exception ignore){
}
if(TextUtils.isEmpty(_domain))
return null;
}
@@ -453,12 +186,12 @@ public class InstanceCatalogFragment extends BaseRecyclerFragment<CatalogInstanc
return domain;
}
private void loadInstanceInfo(String _domain, boolean isFromRedirect){
protected void loadInstanceInfo(String _domain, boolean isFromRedirect){
String domain=normalizeInstanceDomain(_domain);
Instance cachedInstance=instancesCache.get(domain);
if(cachedInstance!=null){
for(CatalogInstance ci:filteredData){
if(ci.domain.equals(domain))
for(CatalogInstance ci : filteredData){
if(ci.domain.equals(domain) && ci!=fakeInstance)
return;
}
CatalogInstance ci=cachedInstance.toCatalogInstance();
@@ -476,44 +209,57 @@ public class InstanceCatalogFragment extends BaseRecyclerFragment<CatalogInstanc
loadingInstanceDomain=domain;
loadingInstanceRequest=new GetInstance();
loadingInstanceRequest.setCallback(new Callback<>(){
@Override
public void onSuccess(Instance result){
loadingInstanceRequest=null;
loadingInstanceDomain=null;
result.uri=domain; // needed for instances that use domain redirection
instancesCache.put(domain, result);
if(instanceProgressDialog!=null){
instanceProgressDialog.dismiss();
instanceProgressDialog=null;
proceedWithAuthOrSignup(result);
}
if(Objects.equals(domain, currentSearchQuery) || Objects.equals(currentSearchQuery, redirects.get(domain)) || Objects.equals(currentSearchQuery, redirectsInverse.get(domain))){
boolean found=false;
for(CatalogInstance ci:filteredData){
if(ci.domain.equals(domain)){
found=true;
break;
}
}
if(!found){
CatalogInstance ci=result.toCatalogInstance();
filteredData.add(0, ci);
adapter.notifyItemInserted(0);
}
@Override
public void onSuccess(Instance result){
loadingInstanceRequest=null;
loadingInstanceDomain=null;
result.uri=domain; // needed for instances that use domain redirection
instancesCache.put(domain, result);
if(instanceProgressDialog!=null){
instanceProgressDialog.dismiss();
instanceProgressDialog=null;
proceedWithAuthOrSignup(result);
}
if(Objects.equals(domain, currentSearchQuery) || Objects.equals(currentSearchQuery, redirects.get(domain)) || Objects.equals(currentSearchQuery, redirectsInverse.get(domain))){
boolean found=false;
for(CatalogInstance ci : filteredData){
if(ci.domain.equals(domain) && ci!=fakeInstance){
found=true;
break;
}
}
if(!found){
CatalogInstance ci=result.toCatalogInstance();
if(filteredData.size()==1 && filteredData.get(0)==fakeInstance){
filteredData.set(0, ci);
adapter.notifyItemChanged(0);
}else{
filteredData.add(0, ci);
adapter.notifyItemInserted(0);
}
}
}
}
@Override
public void onError(ErrorResponse error){
loadingInstanceRequest=null;
if(!isFromRedirect && error instanceof MastodonErrorResponse me && me.httpStatus==404){
fetchDomainFromHostMetaAndMaybeRetry(domain, error);
return;
@Override
public void onError(ErrorResponse error){
loadingInstanceRequest=null;
if(!isFromRedirect && error instanceof MastodonErrorResponse me && me.httpStatus==404){
fetchDomainFromHostMetaAndMaybeRetry(domain, error);
return;
}
loadingInstanceDomain=null;
showInstanceInfoLoadError(domain, error);
if(fakeInstance!=null){
fakeInstance.description=getString(R.string.error);
if(filteredData.size()>0 && filteredData.get(0)==fakeInstance){
if(list.findViewHolderForAdapterPosition(1) instanceof BindableViewHolder<?> ivh){
ivh.rebind();
}
loadingInstanceDomain=null;
showInstanceInfoLoadError(domain, error);
}
}).execNoAuth(domain);
}
}
}).execNoAuth(domain);
}
private void cancelLoadingInstanceInfo(){
@@ -584,7 +330,7 @@ public class InstanceCatalogFragment extends BaseRecyclerFragment<CatalogInstanc
InputSource source=new InputSource(response.body().charStream());
Document doc=DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(source);
NodeList list=doc.getElementsByTagName("Link");
for(int i=0;i<list.getLength();i++){
for(int i=0; i<list.getLength(); i++){
if(list.item(i) instanceof Element el){
String template=el.getAttribute("template");
if("lrdd".equals(el.getAttribute("rel")) && !TextUtils.isEmpty(template) && template.contains("{uri}")){
@@ -616,78 +362,26 @@ public class InstanceCatalogFragment extends BaseRecyclerFragment<CatalogInstanc
}
}
private class InstancesAdapter extends UsableRecyclerView.Adapter<InstanceViewHolder>{
public InstancesAdapter(){
super(imgLoader);
}
@NonNull
@Override
public InstanceViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
return new InstanceViewHolder();
}
@Override
public void onBindViewHolder(InstanceViewHolder holder, int position){
holder.bind(filteredData.get(position));
super.onBindViewHolder(holder, position);
}
@Override
public int getItemCount(){
return filteredData.size();
}
@Override
public int getItemViewType(int position){
return -1;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState){
super.onViewCreated(view, savedInstanceState);
nextButton=view.findViewById(R.id.btn_next);
nextButton.setOnClickListener(this::onNextClick);
nextButton.setEnabled(chosenInstance!=null);
buttonBar=view.findViewById(R.id.button_bar);
setRefreshEnabled(false);
}
private class InstanceViewHolder extends BindableViewHolder<CatalogInstance> implements UsableRecyclerView.Clickable{
private final TextView title, description, userCount, lang;
private final RadioButton radioButton;
public InstanceViewHolder(){
super(getActivity(), R.layout.item_instance_catalog, list);
title=findViewById(R.id.title);
description=findViewById(R.id.description);
userCount=findViewById(R.id.user_count);
lang=findViewById(R.id.lang);
radioButton=findViewById(R.id.radiobtn);
if(Build.VERSION.SDK_INT<Build.VERSION_CODES.N){
UiUtils.fixCompoundDrawableTintOnAndroid6(userCount);
UiUtils.fixCompoundDrawableTintOnAndroid6(lang);
protected void onNextClick(View v){
String domain=chosenInstance.domain;
Instance instance=instancesCache.get(domain);
if(instance!=null){
proceedWithAuthOrSignup(instance);
}else{
showProgressDialog();
if(!domain.equals(loadingInstanceDomain)){
loadInstanceInfo(domain, false);
}
}
@Override
public void onBind(CatalogInstance item){
title.setText(item.normalizedDomain);
description.setText(item.description);
userCount.setText(UiUtils.abbreviateNumber(item.totalUsers));
lang.setText(item.language.toUpperCase());
radioButton.setChecked(chosenInstance==item);
}
@Override
public void onClick(){
if(chosenInstance==item)
return;
if(chosenInstance!=null){
int idx=filteredData.indexOf(chosenInstance);
if(idx!=-1){
RecyclerView.ViewHolder holder=list.findViewHolderForAdapterPosition(mergeAdapter.getPositionForAdapter(adapter)+idx);
if(holder instanceof InstanceViewHolder ivh){
ivh.radioButton.setChecked(false);
}
}
}
radioButton.setChecked(true);
if(chosenInstance==null)
nextButton.setEnabled(true);
chosenInstance=item;
loadInstanceInfo(chosenInstance.domain, false);
}
}
}

View File

@@ -0,0 +1,374 @@
package org.joinmastodon.android.fragments.onboarding;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.os.LocaleList;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.ImageView;
import android.widget.RadioButton;
import android.widget.TextView;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.MastodonAPIRequest;
import org.joinmastodon.android.api.requests.catalog.GetCatalogCategories;
import org.joinmastodon.android.api.requests.catalog.GetCatalogInstances;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.model.Instance;
import org.joinmastodon.android.model.catalog.CatalogCategory;
import org.joinmastodon.android.model.catalog.CatalogInstance;
import org.joinmastodon.android.ui.BetterItemAnimator;
import org.joinmastodon.android.ui.DividerItemDecoration;
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
import org.joinmastodon.android.ui.tabs.TabLayout;
import org.joinmastodon.android.ui.utils.UiUtils;
import org.parceler.Parcels;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.RecyclerView;
import me.grishka.appkit.Nav;
import me.grishka.appkit.api.Callback;
import me.grishka.appkit.api.ErrorResponse;
import me.grishka.appkit.utils.BindableViewHolder;
import me.grishka.appkit.utils.MergeRecyclerAdapter;
import me.grishka.appkit.utils.SingleViewRecyclerAdapter;
import me.grishka.appkit.views.UsableRecyclerView;
public class InstanceCatalogSignupFragment extends InstanceCatalogFragment{
private View headerView;
private MastodonAPIRequest<?> getCategoriesRequest;
private TabLayout categoriesList;
private String currentCategory="all";
private List<CatalogCategory> categories=new ArrayList<>();
public InstanceCatalogSignupFragment(){
super(R.layout.fragment_onboarding_common, 10);
}
@Override
public void onAttach(Context context){
super.onAttach(context);
setRefreshEnabled(false);
loadData();
}
@Override
protected void doLoadData(int offset, int count){
currentRequest=new GetCatalogInstances(null, null)
.setCallback(new Callback<>(){
@Override
public void onSuccess(List<CatalogInstance> result){
if(getActivity()==null)
return;
onDataLoaded(sortInstances(result), false);
updateFilteredList();
}
@Override
public void onError(ErrorResponse error){
error.showToast(getActivity());
onDataLoaded(Collections.emptyList(), false);
}
})
.execNoAuth("");
getCategoriesRequest=new GetCatalogCategories(null)
.setCallback(new Callback<>(){
@Override
public void onSuccess(List<CatalogCategory> result){
getCategoriesRequest=null;
CatalogCategory all=new CatalogCategory();
all.category="all";
categories.add(all);
result.stream().sorted(Comparator.comparingInt((CatalogCategory cc)->cc.serversCount).reversed()).forEach(categories::add);
updateCategories();
}
@Override
public void onError(ErrorResponse error){
getCategoriesRequest=null;
error.showToast(getActivity());
CatalogCategory all=new CatalogCategory();
all.category="all";
categories.add(all);
updateCategories();
}
})
.execNoAuth("");
}
private void updateCategories(){
categoriesList.removeAllTabs();
for(CatalogCategory cat:categories){
int titleRes=getTitleForCategory(cat.category);
TabLayout.Tab tab=categoriesList.newTab().setText(titleRes!=0 ? getString(titleRes) : cat.category).setCustomView(R.layout.item_instance_category);
ImageView emoji=tab.getCustomView().findViewById(R.id.emoji);
emoji.setImageResource(getEmojiForCategory(cat.category));
categoriesList.addTab(tab);
}
}
@Override
public void onDestroy(){
super.onDestroy();
if(getCategoriesRequest!=null)
getCategoriesRequest.cancel();
}
@Override
protected RecyclerView.Adapter getAdapter(){
headerView=getActivity().getLayoutInflater().inflate(R.layout.header_onboarding_instance_catalog, list, false);
searchEdit=headerView.findViewById(R.id.search_edit);
categoriesList=headerView.findViewById(R.id.categories_list);
categoriesList.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener(){
@Override
public void onTabSelected(TabLayout.Tab tab){
CatalogCategory category=categories.get(tab.getPosition());
currentCategory=category.category;
updateFilteredList();
}
@Override
public void onTabUnselected(TabLayout.Tab tab){
}
@Override
public void onTabReselected(TabLayout.Tab tab){
}
});
searchEdit.setOnEditorActionListener(this::onSearchEnterPressed);
searchEdit.addTextChangedListener(new TextWatcher(){
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after){
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count){
searchEdit.removeCallbacks(searchDebouncer);
searchEdit.postDelayed(searchDebouncer, 300);
}
@Override
public void afterTextChanged(Editable s){
}
});
mergeAdapter=new MergeRecyclerAdapter();
mergeAdapter.addAdapter(new SingleViewRecyclerAdapter(headerView));
mergeAdapter.addAdapter(adapter=new InstancesAdapter());
return mergeAdapter;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState){
super.onViewCreated(view, savedInstanceState);
view.findViewById(R.id.btn_back).setOnClickListener(v->Nav.finish(this));
list.setItemAnimator(new BetterItemAnimator());
list.addItemDecoration(new DividerItemDecoration(getActivity(), R.attr.colorPollVoted, 1, 16, 16, DividerItemDecoration.NOT_FIRST));
view.setBackgroundColor(UiUtils.getThemeColor(getActivity(), R.attr.colorBackgroundLight));
setStatusBarColor(UiUtils.getThemeColor(getActivity(), R.attr.colorBackgroundLight));
}
@Override
protected void proceedWithAuthOrSignup(Instance instance){
getActivity().getSystemService(InputMethodManager.class).hideSoftInputFromWindow(contentView.getWindowToken(), 0);
if(isSignup){
if(!instance.registrations){
new M3AlertDialogBuilder(getActivity())
.setTitle(R.string.error)
.setMessage(R.string.instance_signup_closed)
.setPositiveButton(R.string.ok, null)
.show();
return;
}
Bundle args=new Bundle();
args.putParcelable("instance", Parcels.wrap(instance));
Nav.go(getActivity(), InstanceRulesFragment.class, args);
}else{
}
}
// private String getEmojiForCategory(String category){
// return switch(category){
// case "all" -> "💬";
// case "academia" -> "📚";
// case "activism" -> "✊";
// case "food" -> "🍕";
// case "furry" -> "🦁";
// case "games" -> "🕹";
// case "general" -> "🐘";
// case "journalism" -> "📰";
// case "lgbt" -> "🏳️‍🌈";
// case "regional" -> "📍";
// case "art" -> "🎨";
// case "music" -> "🎼";
// case "tech" -> "📱";
// default -> "❓";
// };
// }
private int getEmojiForCategory(String category){
return switch(category){
case "all" -> R.drawable.ic_category_all;
case "academia" -> R.drawable.ic_category_academia;
case "activism" -> R.drawable.ic_category_activism;
case "food" -> R.drawable.ic_category_food;
case "furry" -> R.drawable.ic_category_furry;
case "games" -> R.drawable.ic_category_games;
case "general" -> R.drawable.ic_category_general;
case "journalism" -> R.drawable.ic_category_journalism;
case "lgbt" -> R.drawable.ic_category_lgbt;
case "regional" -> R.drawable.ic_category_regional;
case "art" -> R.drawable.ic_category_art;
case "music" -> R.drawable.ic_category_music;
case "tech" -> R.drawable.ic_category_tech;
default -> R.drawable.ic_category_unknown;
};
}
private int getTitleForCategory(String category){
return switch(category){
case "all" -> R.string.category_all;
case "academia" -> R.string.category_academia;
case "activism" -> R.string.category_activism;
case "food" -> R.string.category_food;
case "furry" -> R.string.category_furry;
case "games" -> R.string.category_games;
case "general" -> R.string.category_general;
case "journalism" -> R.string.category_journalism;
case "lgbt" -> R.string.category_lgbt;
case "regional" -> R.string.category_regional;
case "art" -> R.string.category_art;
case "music" -> R.string.category_music;
case "tech" -> R.string.category_tech;
default -> 0;
};
}
@Override
protected void updateFilteredList(){
ArrayList<CatalogInstance> prevData=new ArrayList<>(filteredData);
filteredData.clear();
for(CatalogInstance instance:data){
if(currentCategory.equals("all") || instance.categories.contains(currentCategory)){
if(TextUtils.isEmpty(currentSearchQuery) || instance.domain.contains(currentSearchQuery)){
if(instance.domain.equals(currentSearchQuery) || !isSignup || !instance.approvalRequired)
filteredData.add(instance);
}
}
}
DiffUtil.calculateDiff(new DiffUtil.Callback(){
@Override
public int getOldListSize(){
return prevData.size();
}
@Override
public int getNewListSize(){
return filteredData.size();
}
@Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition){
return prevData.get(oldItemPosition)==filteredData.get(newItemPosition);
}
@Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition){
return prevData.get(oldItemPosition)==filteredData.get(newItemPosition);
}
}).dispatchUpdatesTo(adapter);
}
private class InstancesAdapter extends UsableRecyclerView.Adapter<InstanceCatalogSignupFragment.InstanceViewHolder>{
public InstancesAdapter(){
super(imgLoader);
}
@NonNull
@Override
public InstanceCatalogSignupFragment.InstanceViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
return new InstanceCatalogSignupFragment.InstanceViewHolder();
}
@Override
public void onBindViewHolder(InstanceCatalogSignupFragment.InstanceViewHolder holder, int position){
holder.bind(filteredData.get(position));
super.onBindViewHolder(holder, position);
}
@Override
public int getItemCount(){
return filteredData.size();
}
@Override
public int getItemViewType(int position){
return -1;
}
}
private class InstanceViewHolder extends BindableViewHolder<CatalogInstance> implements UsableRecyclerView.Clickable{
private final TextView title, description, userCount, lang;
private final RadioButton radioButton;
public InstanceViewHolder(){
super(getActivity(), R.layout.item_instance_catalog, list);
title=findViewById(R.id.title);
description=findViewById(R.id.description);
userCount=findViewById(R.id.user_count);
lang=findViewById(R.id.lang);
radioButton=findViewById(R.id.radiobtn);
if(Build.VERSION.SDK_INT<Build.VERSION_CODES.N){
UiUtils.fixCompoundDrawableTintOnAndroid6(userCount);
UiUtils.fixCompoundDrawableTintOnAndroid6(lang);
}
}
@Override
public void onBind(CatalogInstance item){
title.setText(item.normalizedDomain);
description.setText(item.description);
userCount.setText(UiUtils.abbreviateNumber(item.totalUsers));
lang.setText(item.language.toUpperCase());
radioButton.setChecked(chosenInstance==item);
}
@Override
public void onClick(){
if(chosenInstance==item)
return;
if(chosenInstance!=null){
int idx=filteredData.indexOf(chosenInstance);
if(idx!=-1){
RecyclerView.ViewHolder holder=list.findViewHolderForAdapterPosition(mergeAdapter.getPositionForAdapter(adapter)+idx);
if(holder instanceof InstanceCatalogSignupFragment.InstanceViewHolder ivh){
ivh.radioButton.setChecked(false);
}
}
}
radioButton.setChecked(true);
if(chosenInstance==null)
nextButton.setEnabled(true);
chosenInstance=item;
loadInstanceInfo(chosenInstance.domain, false);
}
}
}

View File

@@ -0,0 +1,263 @@
package org.joinmastodon.android.fragments.onboarding;
import android.graphics.Canvas;
import android.graphics.Outline;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Build;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
import android.widget.ImageButton;
import android.widget.RadioButton;
import android.widget.TextView;
import android.widget.Toolbar;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.requests.catalog.GetCatalogInstances;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.model.Instance;
import org.joinmastodon.android.model.catalog.CatalogInstance;
import org.joinmastodon.android.ui.utils.UiUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import me.grishka.appkit.api.Callback;
import me.grishka.appkit.api.ErrorResponse;
import me.grishka.appkit.utils.BindableViewHolder;
import me.grishka.appkit.utils.MergeRecyclerAdapter;
import me.grishka.appkit.utils.SingleViewRecyclerAdapter;
import me.grishka.appkit.utils.V;
import me.grishka.appkit.views.UsableRecyclerView;
public class InstanceChooserLoginFragment extends InstanceCatalogFragment{
private View headerView;
private boolean loadedAutocomplete;
private ImageButton clearBtn;
public InstanceChooserLoginFragment(){
super(R.layout.fragment_login, 10);
}
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
dataLoaded();
setTitle(R.string.login_title);
if(!loadedAutocomplete){
loadAutocompleteServers();
}
}
@Override
protected void proceedWithAuthOrSignup(Instance instance){
AccountSessionManager.getInstance().authenticate(getActivity(), instance);
}
@Override
protected void updateFilteredList(){
ArrayList<CatalogInstance> prevData=new ArrayList<>(filteredData);
filteredData.clear();
if(currentSearchQuery.length()>0){
boolean foundExactMatch=false;
for(CatalogInstance inst:data){
if(inst.normalizedDomain.contains(currentSearchQuery)){
filteredData.add(inst);
if(inst.normalizedDomain.equals(currentSearchQuery))
foundExactMatch=true;
}
}
if(!foundExactMatch)
filteredData.add(0, fakeInstance);
}
UiUtils.updateList(prevData, filteredData, list, adapter, Objects::equals);
for(int i=0;i<list.getChildCount();i++){
list.getChildAt(i).invalidateOutline();
}
}
@Override
protected void doLoadData(int offset, int count){
}
private void loadAutocompleteServers(){
loadedAutocomplete=true;
new GetCatalogInstances(null, null)
.setCallback(new Callback<>(){
@Override
public void onSuccess(List<CatalogInstance> result){
data.clear();
data.addAll(sortInstances(result));
}
@Override
public void onError(ErrorResponse error){
}
})
.execNoAuth("");
}
@Override
protected void onUpdateToolbar(){
super.onUpdateToolbar();
Toolbar toolbar=getToolbar();
toolbar.setElevation(0);
toolbar.setBackground(null);
}
@Override
protected RecyclerView.Adapter getAdapter(){
headerView=getActivity().getLayoutInflater().inflate(R.layout.header_onboarding_login, list, false);
clearBtn=headerView.findViewById(R.id.search_clear);
searchEdit=headerView.findViewById(R.id.search_edit);
searchEdit.setOnEditorActionListener(this::onSearchEnterPressed);
searchEdit.addTextChangedListener(new TextWatcher(){
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after){
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count){
searchEdit.removeCallbacks(searchDebouncer);
searchEdit.postDelayed(searchDebouncer, 300);
if(s.length()>0){
fakeInstance.domain=fakeInstance.normalizedDomain=s.toString();
fakeInstance.description=getString(R.string.loading_instance);
if(filteredData.size()>0 && filteredData.get(0)==fakeInstance){
if(list.findViewHolderForAdapterPosition(1) instanceof InstanceViewHolder ivh){
ivh.rebind();
}
}
if(filteredData.isEmpty()){
filteredData.add(fakeInstance);
adapter.notifyItemInserted(0);
}
clearBtn.setVisibility(View.VISIBLE);
}else{
clearBtn.setVisibility(View.GONE);
}
}
@Override
public void afterTextChanged(Editable s){
}
});
clearBtn.setOnClickListener(v->searchEdit.setText(""));
mergeAdapter=new MergeRecyclerAdapter();
mergeAdapter.addAdapter(new SingleViewRecyclerAdapter(headerView));
mergeAdapter.addAdapter(adapter=new InstancesAdapter());
return mergeAdapter;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState){
super.onViewCreated(view, savedInstanceState);
setStatusBarColor(UiUtils.getThemeColor(getActivity(), R.attr.colorM3Background));
list.addItemDecoration(new RecyclerView.ItemDecoration(){
@Override
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state){
if(parent.getChildViewHolder(view) instanceof InstanceViewHolder){
outRect.left=outRect.right=V.dp(16);
}
}
});
((UsableRecyclerView)list).setDrawSelectorOnTop(true);
}
private class InstancesAdapter extends UsableRecyclerView.Adapter<InstanceViewHolder>{
public InstancesAdapter(){
super(imgLoader);
}
@NonNull
@Override
public InstanceViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
return new InstanceViewHolder();
}
@Override
public void onBindViewHolder(InstanceViewHolder holder, int position){
holder.bind(filteredData.get(position));
super.onBindViewHolder(holder, position);
}
@Override
public int getItemCount(){
return filteredData.size();
}
@Override
public int getItemViewType(int position){
return -1;
}
}
private class InstanceViewHolder extends BindableViewHolder<CatalogInstance> implements UsableRecyclerView.Clickable{
private final TextView title, description;
private final RadioButton radioButton;
public InstanceViewHolder(){
super(getActivity(), R.layout.item_instance_login, list);
title=findViewById(R.id.title);
description=findViewById(R.id.description);
radioButton=findViewById(R.id.radiobtn);
radioButton.setMinWidth(0);
radioButton.setMinHeight(0);
itemView.setOutlineProvider(new ViewOutlineProvider(){
@Override
public void getOutline(View view, Outline outline){
outline.setRoundRect(0, getAbsoluteAdapterPosition()==1 ? 0 : V.dp(-4), view.getWidth(), view.getHeight()+(getAbsoluteAdapterPosition()==filteredData.size() ? 0 : V.dp(4)), V.dp(4));
}
});
itemView.setClipToOutline(true);
}
@Override
public void onBind(CatalogInstance item){
title.setText(item.normalizedDomain);
description.setText(item.description);
radioButton.setChecked(chosenInstance==item);
}
@Override
public void onClick(){
if(chosenInstance==item)
return;
if(chosenInstance!=null){
int idx=filteredData.indexOf(chosenInstance);
if(idx!=-1){
boolean found=false;
for(int i=0;i<list.getChildCount();i++){
RecyclerView.ViewHolder holder=list.getChildViewHolder(list.getChildAt(i));
if(holder.getAbsoluteAdapterPosition()==mergeAdapter.getPositionForAdapter(adapter)+idx && holder instanceof InstanceViewHolder ivh){
ivh.radioButton.setChecked(false);
found=true;
break;
}
}
if(!found)
adapter.notifyItemChanged(idx);
}
}
radioButton.setChecked(true);
if(chosenInstance==null)
nextButton.setEnabled(true);
chosenInstance=item;
loadInstanceInfo(chosenInstance.domain, false);
}
}
}

View File

@@ -0,0 +1,267 @@
package org.joinmastodon.android.fragments.onboarding;
import android.content.Context;
import android.content.res.ColorStateList;
import android.os.Build;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RadioButton;
import android.widget.Space;
import android.widget.TextView;
import android.widget.Toolbar;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.RecyclerView;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.model.Instance;
import org.joinmastodon.android.model.catalog.CatalogInstance;
import org.joinmastodon.android.ui.BetterItemAnimator;
import org.joinmastodon.android.ui.DividerItemDecoration;
import org.joinmastodon.android.ui.utils.UiUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import me.grishka.appkit.FragmentStackActivity;
import me.grishka.appkit.Nav;
import me.grishka.appkit.api.PaginatedList;
import me.grishka.appkit.utils.BindableViewHolder;
import me.grishka.appkit.utils.MergeRecyclerAdapter;
import me.grishka.appkit.utils.SingleViewRecyclerAdapter;
import me.grishka.appkit.utils.V;
import me.grishka.appkit.views.UsableRecyclerView;
public class MegalodonLoginFragment extends InstanceCatalogFragment {
private View headerView;
public MegalodonLoginFragment() {
super(R.layout.fragment_megalodon_welcome, 1);
}
@Override
public void onAttach(Context context){
super.onAttach(context);
setRefreshEnabled(false);
}
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
dataLoaded();
}
@Override
protected void onUpdateToolbar(){
super.onUpdateToolbar();
if (!canGoBack()) {
ImageView toolbarLogo=new ImageView(getActivity());
toolbarLogo.setScaleType(ImageView.ScaleType.CENTER);
toolbarLogo.setImageResource(R.drawable.logo);
toolbarLogo.setImageTintList(ColorStateList.valueOf(UiUtils.getThemeColor(getActivity(), android.R.attr.textColorPrimary)));
FrameLayout logoWrap=new FrameLayout(getActivity());
FrameLayout.LayoutParams logoParams=new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER);
logoParams.setMargins(0, V.dp(2), 0, 0);
logoWrap.addView(toolbarLogo, logoParams);
getToolbar().addView(logoWrap, new Toolbar.LayoutParams(Gravity.CENTER));
} else {
setTitle(R.string.add_account);
}
}
@Override
protected void proceedWithAuthOrSignup(Instance instance) {
AccountSessionManager.getInstance().authenticate(getActivity(), instance);
}
@Override
protected void updateFilteredList(){
boolean addFakeInstance = currentSearchQuery.length()>0 && currentSearchQuery.matches("^\\S+\\.[^\\.]+$");
if(addFakeInstance){
fakeInstance.domain=fakeInstance.normalizedDomain=currentSearchQuery;
fakeInstance.description=getString(R.string.loading_instance);
if(filteredData.size()>0 && filteredData.get(0)==fakeInstance){
if(list.findViewHolderForAdapterPosition(1) instanceof InstanceViewHolder ivh){
ivh.rebind();
}
}
if(filteredData.isEmpty()){
filteredData.add(fakeInstance);
adapter.notifyItemInserted(0);
}
}
ArrayList<CatalogInstance> prevData=new ArrayList<>(filteredData);
filteredData.clear();
if(currentSearchQuery.length()>0){
boolean foundExactMatch=false;
for(CatalogInstance inst:data){
if(inst.normalizedDomain.contains(currentSearchQuery)){
filteredData.add(inst);
if(inst.normalizedDomain.equals(currentSearchQuery))
foundExactMatch=true;
}
}
if(!foundExactMatch && addFakeInstance) {
filteredData.add(0, fakeInstance);
adapter.notifyItemChanged(0);
}
}
UiUtils.updateList(prevData, filteredData, list, adapter, Objects::equals);
for(int i=0;i<list.getChildCount();i++){
list.getChildAt(i).invalidateOutline();
}
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
view.setBackgroundColor(UiUtils.getThemeColor(getActivity(), R.attr.colorWindowBackground));
list.setItemAnimator(new BetterItemAnimator());
}
@Override
protected void doLoadData(int offset, int count) {}
@Override
protected RecyclerView.Adapter getAdapter(){
headerView=getActivity().getLayoutInflater().inflate(R.layout.header_megalodon_welcome, list, false);
searchEdit=headerView.findViewById(R.id.search_edit);
searchEdit.setOnEditorActionListener(this::onSearchEnterPressed);
headerView.findViewById(R.id.more).setVisibility(View.GONE);
headerView.findViewById(R.id.visibility).setVisibility(View.GONE);
((TextView) headerView.findViewById(R.id.username)).setText("@megalodon");
((TextView) headerView.findViewById(R.id.name)).setText(R.string.sk_app_name);
((TextView) headerView.findViewById(R.id.timestamp)).setText(R.string.time_now);
((ImageView) headerView.findViewById(R.id.avatar)).setImageDrawable(getActivity().getDrawable(R.mipmap.ic_launcher));
((FragmentStackActivity) getActivity()).invalidateSystemBarColors(this);
searchEdit.addTextChangedListener(new TextWatcher(){
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after){}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count){
nextButton.setEnabled(false);
chosenInstance = null;
searchEdit.removeCallbacks(searchDebouncer);
searchEdit.postDelayed(searchDebouncer, 300);
}
@Override
public void afterTextChanged(Editable s){}
});
mergeAdapter=new MergeRecyclerAdapter();
mergeAdapter.addAdapter(new SingleViewRecyclerAdapter(headerView));
mergeAdapter.addAdapter(adapter=new InstancesAdapter());
View spacer = new Space(getActivity());
spacer.setMinimumHeight(V.dp(8));
mergeAdapter.addAdapter(new SingleViewRecyclerAdapter(spacer));
return mergeAdapter;
}
private class InstancesAdapter extends UsableRecyclerView.Adapter<InstanceViewHolder>{
public InstancesAdapter(){
super(imgLoader);
}
@NonNull
@Override
public InstanceViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
return new InstanceViewHolder();
}
@Override
public void onBindViewHolder(InstanceViewHolder holder, int position){
holder.bind(filteredData.get(position));
chosenInstance = filteredData.get(position);
if (chosenInstance != fakeInstance) nextButton.setEnabled(true);
super.onBindViewHolder(holder, position);
}
@Override
public int getItemCount(){
return filteredData.size();
}
@Override
public int getItemViewType(int position){
return -1;
}
}
private class InstanceViewHolder extends BindableViewHolder<CatalogInstance> implements UsableRecyclerView.Clickable{
private final TextView title, description, userCount, lang;
private final RadioButton radioButton;
public InstanceViewHolder(){
super(getActivity(), R.layout.item_megalodon_instance, list);
// itemView.setPadding(V.dp(16), V.dp(16), V.dp(16), V.dp(16));
// TypedValue value = new TypedValue();
// getActivity().getTheme().resolveAttribute(android.R.attr.selectableItemBackground, value, true);
// itemView.setBackground(getActivity().getTheme().getDrawable(R.drawable.bg_search_field));
title=findViewById(R.id.title);
description=findViewById(R.id.description);
userCount=findViewById(R.id.user_count);
lang=findViewById(R.id.lang);
radioButton=findViewById(R.id.radiobtn);
if(Build.VERSION.SDK_INT<Build.VERSION_CODES.N){
UiUtils.fixCompoundDrawableTintOnAndroid6(userCount);
UiUtils.fixCompoundDrawableTintOnAndroid6(lang);
}
}
@Override
public void onBind(CatalogInstance item){
title.setText(item.normalizedDomain);
description.setText(item.description);
if (item == fakeInstance) {
userCount.setVisibility(View.GONE);
lang.setVisibility(View.GONE);
} else {
userCount.setVisibility(View.VISIBLE);
lang.setVisibility(View.VISIBLE);
userCount.setText(UiUtils.abbreviateNumber(item.totalUsers));
lang.setText(item.language.toUpperCase());
}
radioButton.setChecked(chosenInstance==item);
radioButton.setVisibility(View.GONE);
}
@Override
public void onClick(){
if(chosenInstance!=null){
int idx=filteredData.indexOf(chosenInstance);
if(idx!=-1){
RecyclerView.ViewHolder holder=list.findViewHolderForAdapterPosition(mergeAdapter.getPositionForAdapter(adapter)+idx);
if(holder instanceof InstanceViewHolder ivh){
ivh.radioButton.setChecked(false);
}
}
}
radioButton.setChecked(true);
if(chosenInstance==null)
nextButton.setEnabled(true);
chosenInstance=item;
loadInstanceInfo(chosenInstance.domain, false);
onNextClick(null);
}
}
}

View File

@@ -82,6 +82,8 @@ public class Instance extends BaseModel{
// non-standard field in some Mastodon forks
public int maxTootChars;
public V2 v2;
@Override
public void postprocess() throws ObjectValidationException{
super.postprocess();
@@ -176,4 +178,19 @@ public class Instance extends BaseModel{
public int minExpiration;
public int maxExpiration;
}
@Parcel
public static class V2 extends BaseModel {
public V2.Configuration configuration;
@Parcel
public static class Configuration {
public TranslationConfiguration translation;
}
@Parcel
public static class TranslationConfiguration{
public boolean enabled;
}
}
}

View File

@@ -43,7 +43,9 @@ public class PushNotification extends BaseModel{
@SerializedName("follow")
FOLLOW(R.string.notification_type_follow),
@SerializedName("poll")
POLL(R.string.notification_type_poll);
POLL(R.string.notification_type_poll),
@SerializedName("status")
STATUS(R.string.sk_notification_type_status);
@StringRes
public final int localizedName;

View File

@@ -43,10 +43,11 @@ public class PushSubscription extends BaseModel implements Cloneable{
public boolean reblog;
public boolean mention;
public boolean poll;
public boolean status;
public static Alerts ofAll(){
Alerts alerts=new Alerts();
alerts.follow=alerts.favourite=alerts.reblog=alerts.mention=alerts.poll=true;
alerts.follow=alerts.favourite=alerts.reblog=alerts.mention=alerts.poll=alerts.status=true;
return alerts;
}
@@ -58,6 +59,7 @@ public class PushSubscription extends BaseModel implements Cloneable{
", reblog="+reblog+
", mention="+mention+
", poll="+poll+
", status="+status+
'}';
}

View File

@@ -0,0 +1,7 @@
package org.joinmastodon.android.model;
public class TranslatedStatus extends BaseModel {
public String content;
public String detectedSourceLanguage;
public String provider;
}

View File

@@ -2,14 +2,12 @@ package org.joinmastodon.android.ui;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.res.ColorStateList;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowInsets;
@@ -25,8 +23,7 @@ import org.joinmastodon.android.R;
import org.joinmastodon.android.api.requests.oauth.RevokeOauthToken;
import org.joinmastodon.android.api.session.AccountSession;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.fragments.SplashFragment;
import org.joinmastodon.android.model.Account;
import org.joinmastodon.android.fragments.onboarding.MegalodonLoginFragment;
import org.joinmastodon.android.ui.utils.UiUtils;
import java.util.List;
@@ -80,7 +77,7 @@ public class AccountSwitcherSheet extends BottomSheet{
holder.avatar.setImageResource(R.drawable.ic_fluent_add_circle_24_filled);
holder.avatar.setImageTintList(ColorStateList.valueOf(UiUtils.getThemeColor(activity, android.R.attr.textColorPrimary)));
adapter.addAdapter(new ClickableSingleViewRecyclerAdapter(holder.itemView, ()->{
Nav.go(activity, SplashFragment.class, null);
Nav.go(activity, MegalodonLoginFragment.class, null);
dismiss();
}));

View File

@@ -28,6 +28,7 @@ import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.LinearLayoutManager;
@@ -154,11 +155,16 @@ public class ComposeAutocompleteViewController{
}else if(mode==Mode.EMOJIS){
String _text=text.substring(1); // remove ':'
List<WrappedEmoji> oldList=emojis;
emojis=AccountSessionManager.getInstance()
List<Emoji> allEmojis = AccountSessionManager.getInstance()
.getCustomEmojis(AccountSessionManager.getInstance().getAccount(accountID).domain)
.stream()
.flatMap(ec->ec.emojis.stream())
.filter(e->e.visibleInPicker && e.shortcode.startsWith(_text))
.filter(e->e.visibleInPicker)
.collect(Collectors.toList());
List<Emoji> startsWithSearch = allEmojis.stream().filter(e -> e.shortcode.toLowerCase().startsWith(_text.toLowerCase())).collect(Collectors.toList());
emojis=Stream.concat(startsWithSearch.stream(), allEmojis.stream()
.filter(e -> !startsWithSearch.contains(e))
.filter(e -> e.shortcode.toLowerCase().contains(_text.toLowerCase())))
.map(WrappedEmoji::new)
.collect(Collectors.toList());
UiUtils.updateList(oldList, emojis, list, emojisAdapter, (e1, e2)->e1.emoji.shortcode.equals(e2.emoji.shortcode));

View File

@@ -6,6 +6,7 @@ import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import org.joinmastodon.android.GlobalUserPreferences;
import org.joinmastodon.android.R;
import org.joinmastodon.android.fragments.BaseStatusListFragment;
import org.joinmastodon.android.model.Poll;
@@ -44,7 +45,7 @@ public class PollFooterStatusDisplayItem extends StatusDisplayItem{
text+=" · "+item.parentFragment.getString(R.string.poll_closed);
}
this.text.setText(text);
button.setVisibility(item.poll.isExpired() || item.poll.voted || !item.poll.multiple ? View.GONE : View.VISIBLE);
button.setVisibility(item.poll.isExpired() || item.poll.voted || (!item.poll.multiple && !GlobalUserPreferences.voteButtonForSingleChoice) ? View.GONE : View.VISIBLE);
button.setEnabled(item.poll.selectedOptions!=null && !item.poll.selectedOptions.isEmpty());
}
}

View File

@@ -5,6 +5,7 @@ import android.graphics.drawable.Animatable;
import android.graphics.drawable.Drawable;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import org.joinmastodon.android.R;
@@ -60,7 +61,8 @@ public class PollOptionStatusDisplayItem extends StatusDisplayItem{
public static class Holder extends StatusDisplayItem.Holder<PollOptionStatusDisplayItem> implements ImageLoaderViewHolder{
private final TextView text, percent;
private final View icon, button;
private final View button;
private final ImageView icon;
private final Drawable progressBg;
public Holder(Activity activity, ViewGroup parent){
@@ -76,17 +78,23 @@ public class PollOptionStatusDisplayItem extends StatusDisplayItem{
@Override
public void onBind(PollOptionStatusDisplayItem item){
text.setText(item.text);
icon.setVisibility(item.showResults ? View.GONE : View.VISIBLE);
percent.setVisibility(item.showResults ? View.VISIBLE : View.GONE);
itemView.setClickable(!item.showResults);
icon.setImageDrawable(itemView.getContext().getDrawable(item.poll.multiple ?
item.showResults ? R.drawable.ic_poll_checkbox_regular_selector : R.drawable.ic_poll_checkbox_filled_selector :
item.showResults ? R.drawable.ic_poll_option_button : R.drawable.ic_fluent_radio_button_24_selector
));
if(item.showResults){
progressBg.setLevel(Math.round(10000f*item.votesFraction));
button.setBackground(progressBg);
itemView.setSelected(item.isMostVoted);
icon.setSelected(item.poll.ownVotes.contains(item.poll.options.indexOf(item.option)));
icon.setVisibility(item.poll.voted && item.poll.ownVotes.isEmpty() ? View.GONE : View.VISIBLE);
percent.setText(String.format(Locale.getDefault(), "%d%%", Math.round(item.votesFraction*100f)));
}else{
itemView.setSelected(item.poll.selectedOptions!=null && item.poll.selectedOptions.contains(item.option));
button.setBackgroundResource(R.drawable.bg_poll_option_clickable);
icon.setVisibility(View.VISIBLE);
}
}

View File

@@ -1,9 +1,12 @@
package org.joinmastodon.android.ui.displayitems;
import static org.joinmastodon.android.MastodonApp.context;
import android.app.Activity;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.text.SpannableStringBuilder;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
@@ -38,6 +41,8 @@ public class ReblogOrReplyLineStatusDisplayItem extends StatusDisplayItem{
emojiHelper.setText(ssb);
this.icon=icon;
this.handleClick=handleClick;
TypedValue outValue = new TypedValue();
context.getTheme().resolveAttribute(android.R.attr.selectableItemBackground, outValue, true);
}
@Override
@@ -67,6 +72,8 @@ public class ReblogOrReplyLineStatusDisplayItem extends StatusDisplayItem{
text.setText(item.text);
text.setCompoundDrawablesRelativeWithIntrinsicBounds(item.icon, 0, 0, 0);
if(item.handleClick!=null) text.setOnClickListener(item.handleClick);
text.setEnabled(!item.inset);
text.setClickable(!item.inset);
if(Build.VERSION.SDK_INT<Build.VERSION_CODES.N)
UiUtils.fixCompoundDrawableTintOnAndroid6(text);
}

View File

@@ -9,16 +9,25 @@ import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.Button;
import android.widget.TextView;
import org.joinmastodon.android.R;
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.model.Instance;
import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.ui.drawables.SpoilerStripesDrawable;
import org.joinmastodon.android.model.StatusPrivacy;
import org.joinmastodon.android.model.TranslatedStatus;
import org.joinmastodon.android.ui.text.HtmlParser;
import org.joinmastodon.android.ui.utils.CustomEmojiHelper;
import org.joinmastodon.android.ui.views.LinkedTextView;
import me.grishka.appkit.api.Callback;
import me.grishka.appkit.api.ErrorResponse;
import me.grishka.appkit.imageloader.ImageLoaderViewHolder;
import me.grishka.appkit.imageloader.MovieDrawable;
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
@@ -30,6 +39,12 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
private CharSequence parsedSpoilerText;
public boolean textSelectable;
public final Status status;
public boolean translated = false;
public TranslatedStatus translation = null;
private AccountSession session;
private Instance instanceInfo;
private boolean translateEnabled;
public TextStatusDisplayItem(String parentID, CharSequence text, BaseStatusListFragment parentFragment, Status status){
super(parentID, parentFragment);
@@ -41,6 +56,9 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
spoilerEmojiHelper=new CustomEmojiHelper();
spoilerEmojiHelper.setText(parsedSpoilerText);
}
session = AccountSessionManager.getInstance().getAccount(parentFragment.getAccountID());
instanceInfo = AccountSessionManager.getInstance().getInstanceInfo(session.domain);
translateEnabled = instanceInfo.v2 != null && instanceInfo.v2.configuration.translation != null && instanceInfo.v2.configuration.translation.enabled;
}
@Override
@@ -65,9 +83,10 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
public static class Holder extends StatusDisplayItem.Holder<TextStatusDisplayItem> implements ImageLoaderViewHolder{
private final LinkedTextView text;
private final LinearLayout spoilerHeader;
private final TextView spoilerTitle, spoilerTitleInline;
private final View spoilerOverlay, borderTop, borderBottom;
private final TextView spoilerTitle, spoilerTitleInline, translateInfo;
private final View spoilerOverlay, borderTop, borderBottom, textWrap, translateWrap, translateProgress;
private final Drawable backgroundColor, borderColor;
private final Button translateButton;
public Holder(Activity activity, ViewGroup parent){
super(activity, R.layout.display_item_text, parent);
@@ -78,6 +97,11 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
spoilerOverlay=findViewById(R.id.spoiler_overlay);
borderTop=findViewById(R.id.border_top);
borderBottom=findViewById(R.id.border_bottom);
textWrap=findViewById(R.id.text_wrap);
translateWrap=findViewById(R.id.translate_wrap);
translateButton=findViewById(R.id.translate_btn);
translateInfo=findViewById(R.id.translate_info);
translateProgress=findViewById(R.id.translate_progress);
itemView.setOnClickListener(v->item.parentFragment.onRevealSpoilerClick(this));
TypedValue outValue=new TypedValue();
@@ -91,7 +115,9 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
@Override
public void onBind(TextStatusDisplayItem item){
text.setText(item.text);
text.setText(item.translated
? HtmlParser.parse(item.translation.content, item.status.emojis, item.status.mentions, item.status.tags, item.parentFragment.getAccountID())
: item.text);
text.setTextIsSelectable(item.textSelectable);
spoilerTitleInline.setTextIsSelectable(item.textSelectable);
text.setInvalidateOnEveryFrame(false);
@@ -105,20 +131,53 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
if(item.status.spoilerRevealed){
spoilerOverlay.setVisibility(View.GONE);
spoilerHeader.setVisibility(View.VISIBLE);
text.setVisibility(View.VISIBLE);
textWrap.setVisibility(View.VISIBLE);
itemView.setClickable(false);
}else{
spoilerOverlay.setVisibility(View.VISIBLE);
spoilerHeader.setVisibility(View.GONE);
text.setVisibility(View.GONE);
textWrap.setVisibility(View.GONE);
itemView.setClickable(true);
}
}else{
spoilerOverlay.setVisibility(View.GONE);
spoilerHeader.setVisibility(View.GONE);
text.setVisibility(View.VISIBLE);
textWrap.setVisibility(View.VISIBLE);
itemView.setClickable(false);
}
translateWrap.setVisibility(item.textSelectable && item.translateEnabled &&
!item.status.visibility.isLessVisibleThan(StatusPrivacy.UNLISTED) &&
(item.session.preferences == null || !item.status.language.equalsIgnoreCase(item.session.preferences.postingDefaultLanguage))
? View.VISIBLE : View.GONE);
translateButton.setText(item.translated ? R.string.sk_translate_show_original : R.string.sk_translate_post);
translateInfo.setText(item.translated ? itemView.getResources().getString(R.string.sk_translated_using, item.translation.provider) : "");
translateButton.setOnClickListener(v->{
if (item.translation == null) {
translateProgress.setVisibility(View.VISIBLE);
translateButton.setClickable(false);
new TranslateStatus(item.status.id).setCallback(new Callback<>() {
@Override
public void onSuccess(TranslatedStatus translatedStatus) {
item.translation = translatedStatus;
item.translated = true;
translateProgress.setVisibility(View.GONE);
translateButton.setClickable(true);
rebind();
}
@Override
public void onError(ErrorResponse error) {
translateProgress.setVisibility(View.GONE);
translateButton.setClickable(true);
error.showToast(itemView.getContext());
}
}).exec(item.parentFragment.getAccountID());
} else {
item.translated = !item.translated;
rebind();
}
});
}
@Override

View File

@@ -20,6 +20,7 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.provider.OpenableColumns;
import android.provider.Settings;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.view.Menu;
@@ -655,13 +656,71 @@ public class UiUtils{
}
public static void setUserPreferredTheme(Context context){
context.setTheme(switch(GlobalUserPreferences.theme){
case AUTO -> GlobalUserPreferences.trueBlackTheme ? R.style.Theme_Mastodon_AutoLightDark_TrueBlack : R.style.Theme_Mastodon_AutoLightDark;
case LIGHT -> R.style.Theme_Mastodon_Light;
case DARK -> GlobalUserPreferences.trueBlackTheme ? R.style.Theme_Mastodon_Dark_TrueBlack : R.style.Theme_Mastodon_Dark;
});
}
// boolean isDarkTheme = isDarkTheme();
switch(GlobalUserPreferences.color){
case PINK:
context.setTheme(switch(GlobalUserPreferences.theme){
case AUTO ->
GlobalUserPreferences.trueBlackTheme ? R.style.Theme_Mastodon_AutoLightDark_TrueBlack : R.style.Theme_Mastodon_AutoLightDark;
case LIGHT ->
R.style.Theme_Mastodon_Light;
case DARK ->
GlobalUserPreferences.trueBlackTheme ? R.style.Theme_Mastodon_Dark_TrueBlack : R.style.Theme_Mastodon_Dark;
});
break;
case PURPLE:
context.setTheme(switch(GlobalUserPreferences.theme){
case AUTO ->
GlobalUserPreferences.trueBlackTheme ? R.style.Theme_Mastodon_AutoLightDark_TrueBlack_Original : R.style.Theme_Mastodon_AutoLightDark_Original;
case LIGHT ->
R.style.Theme_Mastodon_Light_Original;
case DARK ->
GlobalUserPreferences.trueBlackTheme ? R.style.Theme_Mastodon_Dark_TrueBlack_Original : R.style.Theme_Mastodon_Dark_Original;
});
break;
case GREEN:
context.setTheme(switch(GlobalUserPreferences.theme){
case AUTO ->
GlobalUserPreferences.trueBlackTheme ? R.style.Theme_Mastodon_AutoLightDark_TrueBlack_Green : R.style.Theme_Mastodon_AutoLightDark_Green;
case LIGHT ->
R.style.Theme_Mastodon_Light_Green;
case DARK ->
GlobalUserPreferences.trueBlackTheme ? R.style.Theme_Mastodon_Dark_TrueBlack_Green : R.style.Theme_Mastodon_Dark_Green;
});
break;
case BLUE:
context.setTheme(switch(GlobalUserPreferences.theme){
case AUTO ->
GlobalUserPreferences.trueBlackTheme ? R.style.Theme_Mastodon_AutoLightDark_TrueBlack_Blue : R.style.Theme_Mastodon_AutoLightDark_Blue;
case LIGHT ->
R.style.Theme_Mastodon_Light_Blue;
case DARK ->
GlobalUserPreferences.trueBlackTheme ? R.style.Theme_Mastodon_Dark_TrueBlack_Blue : R.style.Theme_Mastodon_Dark_Blue;
});
break;
case BROWN:
context.setTheme(switch(GlobalUserPreferences.theme){
case AUTO ->
GlobalUserPreferences.trueBlackTheme ? R.style.Theme_Mastodon_AutoLightDark_TrueBlack_Brown : R.style.Theme_Mastodon_AutoLightDark_Brown;
case LIGHT ->
R.style.Theme_Mastodon_Light_Brown;
case DARK ->
GlobalUserPreferences.trueBlackTheme ? R.style.Theme_Mastodon_Dark_TrueBlack_Brown : R.style.Theme_Mastodon_Dark_Brown;
});
break;
case YELLOW:
context.setTheme(switch(GlobalUserPreferences.theme){
case AUTO ->
GlobalUserPreferences.trueBlackTheme ? R.style.Theme_Mastodon_AutoLightDark_TrueBlack_Yellow : R.style.Theme_Mastodon_AutoLightDark_Yellow;
case LIGHT ->
R.style.Theme_Mastodon_Light_Yellow;
case DARK ->
GlobalUserPreferences.trueBlackTheme ? R.style.Theme_Mastodon_Dark_TrueBlack_Yellow : R.style.Theme_Mastodon_Dark_Yellow;
});
break;
}
}
public static boolean isDarkTheme(){
if(GlobalUserPreferences.theme==GlobalUserPreferences.ThemePreference.AUTO)
return (MastodonApp.context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK)==Configuration.UI_MODE_NIGHT_YES;

View File

@@ -0,0 +1,124 @@
package org.joinmastodon.android.ui.views;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.text.Editable;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.TextView;
import org.joinmastodon.android.R;
import org.joinmastodon.android.ui.utils.SimpleTextWatcher;
import me.grishka.appkit.utils.CubicBezierInterpolator;
import me.grishka.appkit.utils.V;
public class FloatingHintEditTextLayout extends FrameLayout{
private EditText edit;
private TextView label;
private int labelTextSize;
private int offsetY;
private boolean hintVisible;
private Animator currentAnim;
public FloatingHintEditTextLayout(Context context){
this(context, null);
}
public FloatingHintEditTextLayout(Context context, AttributeSet attrs){
this(context, attrs, 0);
}
public FloatingHintEditTextLayout(Context context, AttributeSet attrs, int defStyle){
super(context, attrs, defStyle);
if(isInEditMode())
V.setApplicationContext(context);
TypedArray ta=context.obtainStyledAttributes(attrs, R.styleable.FloatingHintEditTextLayout);
labelTextSize=ta.getDimensionPixelSize(R.styleable.FloatingHintEditTextLayout_android_labelTextSize, V.dp(12));
offsetY=ta.getDimensionPixelOffset(R.styleable.FloatingHintEditTextLayout_editTextOffsetY, 0);
ta.recycle();
}
@Override
protected void onFinishInflate(){
super.onFinishInflate();
if(getChildCount()>0 && getChildAt(0) instanceof EditText et){
edit=et;
}else{
throw new IllegalStateException("First child must be an EditText");
}
label=new TextView(getContext());
label.setTextSize(TypedValue.COMPLEX_UNIT_PX, labelTextSize);
label.setTextColor(edit.getHintTextColors());
label.setText(edit.getHint());
label.setSingleLine();
label.setPivotX(0f);
label.setPivotY(0f);
LayoutParams lp=new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.START | Gravity.TOP);
lp.setMarginStart(edit.getPaddingStart());
addView(label, lp);
hintVisible=edit.getText().length()==0;
if(hintVisible)
label.setAlpha(0f);
edit.addTextChangedListener(new SimpleTextWatcher(this::onTextChanged));
}
private void onTextChanged(Editable text){
boolean newHintVisible=text.length()==0;
if(newHintVisible==hintVisible)
return;
if(currentAnim!=null)
currentAnim.cancel();
hintVisible=newHintVisible;
label.setAlpha(1);
float scale=edit.getLineHeight()/(float)label.getLineHeight();
float transY=edit.getHeight()/2f-edit.getLineHeight()/2f+(edit.getTop()-label.getTop())-(label.getHeight()/2f-label.getLineHeight()/2f);
AnimatorSet anim=new AnimatorSet();
if(hintVisible){
anim.playTogether(
ObjectAnimator.ofFloat(edit, TRANSLATION_Y, 0),
ObjectAnimator.ofFloat(label, SCALE_X, scale),
ObjectAnimator.ofFloat(label, SCALE_Y, scale),
ObjectAnimator.ofFloat(label, TRANSLATION_Y, transY)
);
edit.setHintTextColor(0);
}else{
label.setScaleX(scale);
label.setScaleY(scale);
label.setTranslationY(transY);
anim.playTogether(
ObjectAnimator.ofFloat(edit, TRANSLATION_Y, offsetY),
ObjectAnimator.ofFloat(label, SCALE_X, 1f),
ObjectAnimator.ofFloat(label, SCALE_Y, 1f),
ObjectAnimator.ofFloat(label, TRANSLATION_Y, 0f)
);
}
anim.setDuration(150);
anim.setInterpolator(CubicBezierInterpolator.DEFAULT);
anim.start();
anim.addListener(new AnimatorListenerAdapter(){
@Override
public void onAnimationEnd(Animator animation){
currentAnim=null;
if(hintVisible){
label.setAlpha(0);
edit.setHintTextColor(label.getTextColors());
}
}
});
currentAnim=anim;
}
}

File diff suppressed because one or more lines are too long

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/boost_selected" android:state_selected="true"/>
<item android:color="?android:colorAccent" android:state_selected="true"/>
<item android:color="?android:textColorSecondary" android:state_enabled="true"/>
<item android:color="?android:textColorSecondary" android:alpha="0.3"/>
</selector>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/gray_800" android:state_enabled="true"/>
<item android:color="@color/gray_300"/>
<item android:color="?android:colorPrimary" android:state_enabled="true"/>
<item android:color="?colorPollVoted"/>
</selector>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/gray_100" android:state_enabled="true"/>
<item android:color="@color/gray_500"/>
<item android:color="?colorSecondary" android:state_enabled="true"/>
<item android:color="?colorPollVoted"/>
</selector>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/gray_600" android:state_enabled="true"/>
<item android:color="@color/gray_300"/>
<item android:color="?colorPollVoted" android:state_enabled="true"/>
<item android:color="?colorSearchHint"/>
</selector>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="?colorM3OnPrimary" android:state_enabled="true"/>
<item android:color="?colorM3OnSurface" android:alpha="0.38"/>
</selector>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="?colorM3Primary" android:state_enabled="true"/>
<item android:color="?colorM3OnSurface" android:alpha="0.38"/>
</selector>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/gray_50" android:state_enabled="true"/>
<item android:color="@color/gray_400"/>
<item android:color="?colorTabInactive"/>
</selector>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/gray_800" android:state_enabled="true"/>
<item android:color="@color/gray_400"/>
<item android:color="?colorTabInactive"/>
</selector>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/gray_800" android:state_enabled="true"/>
<item android:color="@color/gray_400"/>
<item android:color="?android:colorPrimary" android:state_enabled="true"/>
<item android:color="?colorTabInactive"/>
</selector>

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/gray_50"/>
<item android:color="?colorSecondary"/>
</selector>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="?colorM3PressedOverlay" android:alpha="0.12"/>
</selector>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="?colorM3Primary" android:alpha="0.12"/>
</selector>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="?colorM3Primary" android:state_checked="true"/>
<item android:color="?colorM3OnSurfaceVariant"/>
</selector>

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="true">
<ripple android:color="@color/m3_pressed_overlay">
<item>
<shape>
<solid android:color="?colorM3Primary"/>
<corners android:radius="20dp"/>
</shape>
</item>
</ripple>
</item>
<item>
<shape>
<solid android:color="?colorM3DisabledBackground"/>
<corners android:radius="20dp"/>
</shape>
</item>
</selector>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<ripple android:color="@color/m3_primary_overlay" xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@android:id/mask">
<shape>
<solid android:color="#000"/>
<corners android:radius="20dp"/>
</shape>
</item>
</ripple>

View File

@@ -2,7 +2,7 @@
<ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="@color/highlight_over_dark">
<item>
<shape android:shape="oval">
<solid android:color="@color/gray_600"/>
<solid android:color="?colorSearchHint"/>
</shape>
</item>
</ripple>

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 7c0.414 0 0.75 0.336 0.75 0.75v3.5h3.5c0.414 0 0.75 0.336 0.75 0.75s-0.336 0.75-0.75 0.75h-3.5v3.5c0 0.414-0.336 0.75-0.75 0.75s-0.75-0.336-0.75-0.75v-3.5h-3.5C7.336 12.75 7 12.414 7 12s0.336-0.75 0.75-0.75h3.5v-3.5C11.25 7.336 11.586 7 12 7zM3 6.25C3 4.455 4.455 3 6.25 3h11.5C19.545 3 21 4.455 21 6.25v11.5c0 1.795-1.455 3.25-3.25 3.25H6.25C4.455 21 3 19.545 3 17.75V6.25zM6.25 4.5C5.284 4.5 4.5 5.284 4.5 6.25v11.5c0 0.966 0.784 1.75 1.75 1.75h11.5c0.966 0 1.75-0.784 1.75-1.75V6.25c0-0.966-0.784-1.75-1.75-1.75H6.25z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
<path android:pathData="M6.25 3C4.455 3 3 4.455 3 6.25v11.5C3 19.545 4.455 21 6.25 21h11.5c1.795 0 3.25-1.455 3.25-3.25V6.25C21 4.455 19.545 3 17.75 3H6.25zm11.03 6.28l-6.754 6.747c-0.293 0.292-0.767 0.292-1.06 0L6.72 13.28c-0.293-0.293-0.293-0.768 0-1.06 0.293-0.293 0.768-0.293 1.06 0l2.217 2.216 6.223-6.217c0.293-0.292 0.768-0.292 1.06 0.001 0.293 0.293 0.293 0.768 0 1.06z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
<path android:pathData="M6.25 3C4.455 3 3 4.455 3 6.25v11.5C3 19.545 4.455 21 6.25 21h11.5c1.795 0 3.25-1.455 3.25-3.25V6.25C21 4.455 19.545 3 17.75 3H6.25zM4.5 6.25c0-0.966 0.784-1.75 1.75-1.75h11.5c0.966 0 1.75 0.784 1.75 1.75v11.5c0 0.966-0.784 1.75-1.75 1.75H6.25c-0.966 0-1.75-0.784-1.75-1.75V6.25zm12.78 3.03c0.293-0.292 0.293-0.767 0-1.06-0.292-0.293-0.767-0.293-1.06 0l-6.223 6.216L7.78 12.22c-0.293-0.293-0.768-0.293-1.06 0-0.294 0.292-0.294 0.767 0 1.06l2.745 2.746c0.293 0.293 0.767 0.293 1.06 0l6.754-6.745z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
<path android:pathData="M3 6.25C3 4.455 4.455 3 6.25 3h11.5C19.545 3 21 4.455 21 6.25v11.5c0 1.795-1.455 3.25-3.25 3.25H6.25C4.455 21 3 19.545 3 17.75V6.25zM6.25 5C5.56 5 5 5.56 5 6.25v11.5C5 18.44 5.56 19 6.25 19h11.5c0.69 0 1.25-0.56 1.25-1.25V6.25C19 5.56 18.44 5 17.75 5H6.25z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
<path android:pathData="M3 6.25C3 4.455 4.455 3 6.25 3h11.5C19.545 3 21 4.455 21 6.25v11.5c0 1.795-1.455 3.25-3.25 3.25H6.25C4.455 21 3 19.545 3 17.75V6.25zM6.25 4.5C5.284 4.5 4.5 5.284 4.5 6.25v11.5c0 0.966 0.784 1.75 1.75 1.75h11.5c0.966 0 1.75-0.784 1.75-1.75V6.25c0-0.966-0.784-1.75-1.75-1.75H6.25z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
<path android:pathData="M3.839 5.858c2.94-3.916 9.03-5.055 13.364-2.36 4.28 2.66 5.854 7.777 4.1 12.577-1.655 4.533-6.016 6.328-9.159 4.048-1.177-0.854-1.634-1.925-1.854-3.664l-0.106-0.987-0.045-0.398c-0.123-0.934-0.311-1.352-0.705-1.572C8.9 13.204 8.542 13.197 7.84 13.47l-0.351 0.146-0.179 0.078c-1.014 0.44-1.688 0.595-2.541 0.416l-0.2-0.047-0.164-0.047c-2.79-0.865-3.203-4.648-0.565-8.158zm0.984 6.716l0.123 0.037 0.134 0.03c0.439 0.087 0.814 0.015 1.437-0.242l0.602-0.257c1.202-0.493 1.985-0.54 3.046 0.05 0.917 0.512 1.275 1.298 1.457 2.66l0.053 0.459 0.055 0.532 0.047 0.422c0.172 1.361 0.485 2.09 1.248 2.644 2.275 1.65 5.534 0.309 6.87-3.349 1.516-4.152 0.174-8.514-3.484-10.789-3.675-2.284-8.899-1.306-11.373 1.987-2.075 2.763-1.82 5.28-0.215 5.816zm11.225-1.994c-0.18-0.667 0.217-1.353 0.883-1.531 0.667-0.179 1.353 0.217 1.531 0.884 0.18 0.667-0.217 1.352-0.884 1.53-0.666 0.18-1.352-0.216-1.53-0.883zm0.494 3.488c-0.179-0.666 0.217-1.352 0.884-1.53 0.667-0.18 1.352 0.216 1.531 0.883 0.179 0.667-0.217 1.353-0.884 1.531-0.667 0.179-1.352-0.217-1.53-0.884zM14.07 7.577c-0.179-0.667 0.217-1.352 0.884-1.53 0.667-0.18 1.352 0.216 1.53 0.883 0.18 0.667-0.216 1.352-0.883 1.53-0.667 0.18-1.352-0.216-1.53-0.883zm-0.028 8.998c-0.18-0.666 0.217-1.352 0.883-1.53 0.667-0.18 1.353 0.216 1.531 0.883 0.18 0.667-0.217 1.353-0.883 1.531-0.667 0.179-1.353-0.217-1.531-0.884zm-3.497-9.97c-0.179-0.666 0.217-1.352 0.883-1.53 0.667-0.18 1.353 0.217 1.532 0.883 0.178 0.667-0.218 1.353-0.884 1.531-0.667 0.179-1.353-0.217-1.531-0.884z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="20dp" android:height="20dp" android:viewportWidth="20" android:viewportHeight="20">
<path android:pathData="M10 18c4.418 0 8-3.582 8-8s-3.582-8-8-8-8 3.582-8 8 3.582 8 8 8zm0-15c0.657 0 1.407 0.59 2.022 1.908 0.217 0.466 0.406 1.002 0.559 1.592H7.419c0.153-0.59 0.342-1.126 0.56-1.592C8.592 3.59 9.342 3 10 3zM7.072 4.485C6.796 5.077 6.565 5.757 6.389 6.5H3.936c0.837-1.446 2.176-2.565 3.778-3.118-0.241 0.33-0.456 0.704-0.642 1.103zM6.192 7.5C6.068 8.288 6 9.13 6 10c0 0.87 0.067 1.712 0.193 2.5H3.46C3.163 11.724 3 10.88 3 10c0-0.88 0.163-1.724 0.46-2.5h2.733zm0.197 6c0.176 0.743 0.407 1.422 0.683 2.015 0.186 0.399 0.401 0.773 0.642 1.103-1.602-0.553-2.941-1.672-3.778-3.118H6.39zm1.03 0h5.162c-0.153 0.59-0.342 1.126-0.56 1.592C11.408 16.41 10.658 17 10 17c-0.657 0-1.407-0.59-2.022-1.908C7.761 14.626 7.572 14.09 7.42 13.5zm5.375-1H7.206C7.073 11.725 7 10.883 7 10s0.074-1.725 0.206-2.5h5.588C12.927 8.275 13 9.117 13 10s-0.073 1.725-0.206 2.5zm0.817 1h2.453c-0.837 1.446-2.176 2.565-3.778 3.118 0.241-0.33 0.456-0.704 0.642-1.103 0.276-0.593 0.507-1.272 0.683-2.015zm2.93-1h-2.734C13.933 11.712 14 10.87 14 10c0-0.87-0.067-1.712-0.193-2.5h2.733C16.837 8.276 17 9.12 17 10c0 0.88-0.163 1.724-0.46 2.5zm-4.255-9.118c1.602 0.553 2.941 1.672 3.778 3.118H13.61c-0.176-0.743-0.407-1.423-0.683-2.015-0.186-0.399-0.401-0.773-0.642-1.103z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
<path android:pathData="M12 1.999c5.524 0 10.002 4.478 10.002 10.002 0 5.523-4.478 10.001-10.002 10.001-5.524 0-10.002-4.478-10.002-10.001C1.998 6.477 6.476 1.999 12 1.999zm0 1.5c-4.695 0-8.502 3.806-8.502 8.502 0 4.695 3.807 8.501 8.502 8.501s8.502-3.806 8.502-8.501c0-4.696-3.807-8.502-8.502-8.502zm-0.003 2.5c3.312 0 5.998 2.686 5.998 5.998 0 3.313-2.686 5.998-5.998 5.998-3.313 0-5.999-2.685-5.999-5.998S8.684 6 11.997 6z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<!--~ Copyright (c) 2022. ~ Microsoft Corporation. All rights reserved.-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/ic_fluent_radio_button_24_filled" android:state_activated="true"/>
<item android:drawable="@drawable/ic_fluent_radio_button_24_filled" android:state_checked="true"/>
<item android:drawable="@drawable/ic_fluent_radio_button_24_filled" android:state_selected="true"/>
<item android:drawable="@drawable/ic_fluent_radio_button_24_regular"/>
</selector>

View File

@@ -4,7 +4,7 @@
<item android:width="14dp" android:height="14dp" android:gravity="top|right">
<shape android:shape="oval">
<stroke android:color="?android:colorPrimary" android:width="2dp"/>
<solid android:color="@color/primary_600"/>
<solid android:color="?android:colorAccent"/>
</shape>
</item>
</layer-list>

View File

@@ -0,0 +1,9 @@
<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,2C6.47,2 2,6.47 2,12C2,17.53 6.47,22 12,22C17.53,22 22,17.53 22,12C22,6.47 17.53,2 12,2ZM12,20C7.59,20 4,16.41 4,12C4,7.59 7.59,4 12,4C16.41,4 20,7.59 20,12C20,16.41 16.41,20 12,20ZM12,10.59L15.59,7L17,8.41L13.41,12L17,15.59L15.59,17L12,13.41L8.41,17L7,15.59L10.59,12L7,8.41L8.41,7L12,10.59Z"
android:fillColor="#49454F"/>
</vector>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="20dp"
android:height="20dp"
android:viewportWidth="20"
android:viewportHeight="20">
<path
android:pathData="M12.3,11.058L17.075,15.833L15.833,17.075L11.058,12.3C10.167,12.942 9.092,13.333 7.917,13.333C4.925,13.333 2.5,10.908 2.5,7.917C2.5,4.925 4.925,2.5 7.917,2.5C10.908,2.5 13.333,4.925 13.333,7.917C13.333,9.092 12.942,10.167 12.3,11.058ZM7.917,4.167C5.842,4.167 4.167,5.842 4.167,7.917C4.167,9.992 5.842,11.667 7.917,11.667C9.992,11.667 11.667,9.992 11.667,7.917C11.667,5.842 9.992,4.167 7.917,4.167Z"
android:fillColor="#49454F"/>
</vector>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/ic_fluent_checkbox_checked_24_filled" android:state_activated="true"/>
<item android:drawable="@drawable/ic_fluent_checkbox_checked_24_filled" android:state_checked="true"/>
<item android:drawable="@drawable/ic_fluent_checkbox_checked_24_filled" android:state_selected="true"/>
<item android:drawable="@drawable/ic_fluent_checkbox_unchecked_24_filled"/>
</selector>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/ic_fluent_checkbox_checked_24_regular" android:state_activated="true"/>
<item android:drawable="@drawable/ic_fluent_checkbox_checked_24_regular" android:state_checked="true"/>
<item android:drawable="@drawable/ic_fluent_checkbox_checked_24_regular" android:state_selected="true"/>
<item android:drawable="@drawable/ic_fluent_checkbox_unchecked_24_regular"/>
</selector>

View File

@@ -4,7 +4,7 @@
<item android:width="14dp" android:height="14dp" android:gravity="top|right">
<shape android:shape="oval">
<stroke android:color="?android:colorPrimary" android:width="2dp"/>
<solid android:color="@color/primary_600"/>
<solid android:color="?android:colorAccent"/>
</shape>
</item>
</layer-list>

View File

@@ -1,9 +1,34 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="110dp"
android:height="18dp"
android:viewportWidth="110"
android:viewportHeight="18">
android:width="124.78dp"
android:height="22.75dp"
android:viewportWidth="124.78"
android:viewportHeight="22.75">
<path
android:pathData="M18.406,9.999V17.878H15.287V10.231C15.287,8.618 14.609,7.8 13.254,7.8C11.755,7.8 11.004,8.771 11.004,10.69V14.876H7.903V10.69C7.903,8.771 7.151,7.8 5.653,7.8C4.297,7.8 3.619,8.618 3.619,10.231V17.878H0.5V9.999C0.5,8.389 0.91,7.109 1.733,6.162C2.581,5.215 3.693,4.73 5.072,4.73C6.668,4.73 7.877,5.344 8.676,6.572L9.453,7.876L10.23,6.572C11.029,5.344 12.238,4.73 13.834,4.73C15.214,4.73 16.325,5.215 17.174,6.162C17.997,7.109 18.406,8.389 18.406,9.999ZM29.152,13.916C29.795,13.235 30.105,12.378 30.105,11.345C30.105,10.311 29.795,9.454 29.152,8.798C28.532,8.118 27.745,7.79 26.791,7.79C25.838,7.79 25.051,8.118 24.431,8.798C23.811,9.454 23.501,10.311 23.501,11.345C23.501,12.378 23.811,13.235 24.431,13.916C25.051,14.571 25.838,14.899 26.791,14.899C27.745,14.899 28.532,14.571 29.152,13.916ZM30.105,5.042H33.181V17.647H30.105V16.16C29.176,17.395 27.888,18 26.219,18C24.622,18 23.263,17.37 22.118,16.084C20.998,14.798 20.426,13.21 20.426,11.345C20.426,9.504 20.998,7.916 22.118,6.63C23.263,5.345 24.622,4.689 26.219,4.689C27.888,4.689 29.176,5.294 30.105,6.529V5.042ZM43.528,11.118C44.434,11.798 44.888,12.756 44.864,13.966C44.864,15.252 44.41,16.26 43.481,16.966C42.551,17.647 41.431,18 40.072,18C37.616,18 35.947,16.992 35.065,15L37.735,13.412C38.093,14.496 38.879,15.05 40.072,15.05C41.168,15.05 41.717,14.697 41.717,13.966C41.717,13.437 41.001,12.958 39.547,12.58C38.999,12.429 38.546,12.277 38.188,12.151C37.688,11.95 37.258,11.723 36.901,11.445C36.019,10.765 35.566,9.857 35.566,8.698C35.566,7.462 35.995,6.479 36.853,5.773C37.735,5.042 38.808,4.689 40.095,4.689C42.146,4.689 43.648,5.571 44.625,7.361L42.003,8.874C41.621,8.017 40.978,7.588 40.095,7.588C39.166,7.588 38.713,7.941 38.713,8.622C38.713,9.151 39.428,9.63 40.882,10.008C42.003,10.26 42.885,10.639 43.528,11.118ZM53.304,8.168H50.61V13.412C50.61,14.042 50.848,14.42 51.301,14.597C51.635,14.723 52.303,14.748 53.304,14.697V17.647C51.23,17.899 49.728,17.698 48.846,17.017C47.964,16.361 47.534,15.151 47.534,13.412V8.168H45.46V5.042H47.534V2.496L50.61,1.513V5.042H53.304V8.168ZM63.103,13.84C63.723,13.185 64.033,12.353 64.033,11.344C64.033,10.336 63.723,9.504 63.103,8.849C62.483,8.193 61.721,7.866 60.791,7.866C59.861,7.866 59.098,8.193 58.478,8.849C57.882,9.529 57.572,10.361 57.572,11.344C57.572,12.328 57.882,13.16 58.478,13.84C59.098,14.496 59.861,14.823 60.791,14.823C61.721,14.823 62.483,14.496 63.103,13.84ZM56.308,16.084C55.093,14.798 54.497,13.235 54.497,11.344C54.497,9.479 55.093,7.916 56.308,6.63C57.524,5.345 59.026,4.689 60.791,4.689C62.555,4.689 64.057,5.345 65.273,6.63C66.489,7.916 67.109,9.504 67.109,11.344C67.109,13.21 66.489,14.798 65.273,16.084C64.057,17.37 62.579,18 60.791,18C59.002,18 57.524,17.37 56.308,16.084ZM77.385,13.916C78.005,13.235 78.314,12.378 78.314,11.345C78.314,10.311 78.005,9.454 77.385,8.798C76.765,8.118 75.978,7.79 75.024,7.79C74.071,7.79 73.284,8.118 72.64,8.798C72.021,9.454 71.71,10.311 71.71,11.345C71.71,12.378 72.021,13.235 72.64,13.916C73.284,14.571 74.094,14.899 75.024,14.899C75.978,14.899 76.765,14.571 77.385,13.916ZM78.314,0H81.39V17.647H78.314V16.16C77.409,17.395 76.121,18 74.452,18C72.855,18 71.472,17.37 70.328,16.084C69.207,14.798 68.635,13.21 68.635,11.345C68.635,9.504 69.207,7.916 70.328,6.63C71.472,5.345 72.855,4.689 74.452,4.689C76.121,4.689 77.409,5.294 78.314,6.529V0ZM92.191,13.84C92.811,13.185 93.12,12.353 93.12,11.344C93.12,10.336 92.811,9.504 92.191,8.849C91.571,8.193 90.808,7.866 89.878,7.866C88.948,7.866 88.185,8.193 87.565,8.849C86.969,9.529 86.659,10.361 86.659,11.344C86.659,12.328 86.969,13.16 87.565,13.84C88.185,14.496 88.948,14.823 89.878,14.823C90.808,14.823 91.571,14.496 92.191,13.84ZM85.396,16.084C84.18,14.798 83.584,13.235 83.584,11.344C83.584,9.479 84.18,7.916 85.396,6.63C86.612,5.345 88.114,4.689 89.878,4.689C91.642,4.689 93.144,5.345 94.36,6.63C95.576,7.916 96.196,9.504 96.196,11.344C96.196,13.21 95.576,14.798 94.36,16.084C93.144,17.37 91.666,18 89.878,18C88.09,18 86.612,17.37 85.396,16.084ZM109.5,9.908V17.647H106.424V10.311C106.424,9.479 106.21,8.849 105.781,8.37C105.375,7.941 104.803,7.714 104.064,7.714C102.324,7.714 101.442,8.748 101.442,10.84V17.647H98.366V5.042H101.442V6.454C102.181,5.269 103.349,4.689 104.994,4.689C106.305,4.689 107.378,5.143 108.213,6.076C109.071,7.009 109.5,8.269 109.5,9.908Z"
android:fillColor="#282C37"/>
android:pathData="m26.16,17.57q-1.82,0 -3.29,-0.84 -1.46,-0.84 -2.3,-2.3 -0.82,-1.49 -0.82,-3.34 0,-1.75 0.82,-3.24 0.82,-1.51 2.26,-2.4 1.44,-0.89 3.22,-0.89 1.9,0 3.26,0.82 1.37,0.82 2.06,2.23 0.72,1.42 0.72,3.14 0,0.55 -0.07,1.06h-9.19q0.24,1.46 1.15,2.23 0.94,0.74 2.23,0.74 1.08,0 1.85,-0.46 0.79,-0.48 1.25,-1.27l2.54,1.25q-1.87,3.26 -5.69,3.26zM29.07,9.58q-0.05,-0.58 -0.43,-1.13 -0.36,-0.58 -1.03,-0.94 -0.65,-0.38 -1.54,-0.38 -1.13,0 -1.94,0.67 -0.79,0.65 -1.15,1.78z"
android:fillColor="#282c37"/>
<path
android:pathData="m40.27,22.75q-2.33,0 -3.86,-1.06 -1.54,-1.03 -2.06,-2.5l2.95,-1.2q0.38,0.91 1.18,1.44 0.79,0.53 1.8,0.53 1.49,0 2.35,-0.91 0.89,-0.91 0.89,-2.59v-0.96h-0.19q-0.58,0.82 -1.51,1.25 -0.94,0.43 -2.14,0.43 -1.51,0 -2.83,-0.77 -1.3,-0.79 -2.09,-2.21 -0.79,-1.44 -0.79,-3.34 0,-1.9 0.79,-3.31 0.79,-1.44 2.09,-2.21 1.32,-0.79 2.83,-0.79 1.2,0 2.14,0.43 0.94,0.43 1.51,1.25h0.19v-1.3h3.02L46.54,16.34q0,1.97 -0.77,3.41 -0.77,1.44 -2.18,2.21 -1.42,0.79 -3.31,0.79zM40.32,14.33q1.32,0 2.26,-0.91 0.94,-0.94 0.94,-2.54 0,-1.66 -0.94,-2.54 -0.91,-0.91 -2.26,-0.91 -1.34,0 -2.28,0.91 -0.94,0.91 -0.94,2.54 0,1.63 0.94,2.54 0.94,0.91 2.28,0.91z"
android:fillColor="#282c37"/>
<path
android:pathData="m53.23,17.57q-1.94,0 -3.19,-1.13 -1.25,-1.15 -1.25,-3 0,-1.22 0.65,-2.16 0.65,-0.94 1.78,-1.44 1.13,-0.5 2.5,-0.5 1.9,0 3.24,0.55L56.95,9.36q0,-1.01 -0.77,-1.63 -0.74,-0.62 -2.02,-0.62 -0.86,0 -1.68,0.41 -0.79,0.38 -1.32,1.03l-2.02,-1.58q0.89,-1.15 2.23,-1.78 1.34,-0.62 2.93,-0.62 2.81,0 4.27,1.3 1.46,1.3 1.46,3.79v7.54h-3.1v-1.25h-0.19q-0.55,0.72 -1.46,1.18 -0.89,0.46 -2.06,0.46zM53.98,15.12q1.37,0 2.16,-0.86 0.82,-0.89 0.82,-2.06 -1.22,-0.58 -2.57,-0.58 -2.45,0 -2.45,1.82 0,0.74 0.53,1.22 0.53,0.46 1.51,0.46z"
android:fillColor="#282c37"/>
<path
android:pathData="m62.95,0h3.14L66.1,17.18h-3.14z"
android:fillColor="#282c37"/>
<path
android:pathData="m74.93,17.57q-1.9,0 -3.38,-0.84 -1.49,-0.86 -2.33,-2.33 -0.82,-1.49 -0.82,-3.34 0,-1.82 0.82,-3.31 0.84,-1.49 2.33,-2.33 1.49,-0.86 3.38,-0.86 1.87,0 3.36,0.86 1.49,0.84 2.3,2.33 0.84,1.49 0.84,3.31 0,1.85 -0.84,3.34 -0.82,1.46 -2.3,2.33 -1.49,0.84 -3.36,0.84zM74.93,14.66q0.91,0 1.68,-0.43 0.77,-0.43 1.22,-1.25 0.46,-0.82 0.46,-1.92 0,-1.08 -0.46,-1.9 -0.46,-0.82 -1.22,-1.25 -0.77,-0.43 -1.68,-0.43 -0.91,0 -1.68,0.43 -0.77,0.43 -1.25,1.25 -0.46,0.82 -0.46,1.9 0,1.08 0.46,1.92 0.48,0.82 1.25,1.25 0.77,0.43 1.68,0.43z"
android:fillColor="#282c37"/>
<path
android:pathData="m89.09,17.57q-1.66,0 -3.02,-0.82 -1.34,-0.84 -2.14,-2.3 -0.77,-1.49 -0.77,-3.38 0,-1.87 0.77,-3.36 0.79,-1.49 2.14,-2.3 1.37,-0.84 3.02,-0.84 1.25,0 2.21,0.55 0.96,0.53 1.46,1.32h0.19l-0.19,-1.73L92.76,0h3.12L95.88,17.18h-2.93v-1.46h-0.19q-0.48,0.79 -1.46,1.32 -0.96,0.53 -2.21,0.53zM89.62,14.66q0.89,0 1.66,-0.46 0.77,-0.46 1.22,-1.27 0.46,-0.82 0.46,-1.87 0,-1.06 -0.46,-1.87 -0.46,-0.82 -1.22,-1.25 -0.77,-0.46 -1.66,-0.46 -0.86,0 -1.63,0.46 -0.77,0.43 -1.22,1.25 -0.46,0.82 -0.46,1.87 0,1.06 0.46,1.87 0.46,0.82 1.22,1.27 0.77,0.46 1.63,0.46z"
android:fillColor="#282c37"/>
<path
android:pathData="m104.72,17.57q-1.9,0 -3.38,-0.84 -1.49,-0.86 -2.33,-2.33 -0.82,-1.49 -0.82,-3.34 0,-1.82 0.82,-3.31 0.84,-1.49 2.33,-2.33 1.49,-0.86 3.38,-0.86 1.87,0 3.36,0.86 1.49,0.84 2.3,2.33 0.84,1.49 0.84,3.31 0,1.85 -0.84,3.34 -0.82,1.46 -2.3,2.33 -1.49,0.84 -3.36,0.84zM104.72,14.66q0.91,0 1.68,-0.43 0.77,-0.43 1.22,-1.25 0.46,-0.82 0.46,-1.92 0,-1.08 -0.46,-1.9 -0.46,-0.82 -1.22,-1.25 -0.77,-0.43 -1.68,-0.43 -0.91,0 -1.68,0.43 -0.77,0.43 -1.25,1.25 -0.46,0.82 -0.46,1.9 0,1.08 0.46,1.92 0.48,0.82 1.25,1.25 0.77,0.43 1.68,0.43z"
android:fillColor="#282c37"/>
<path
android:pathData="m113.38,4.94h2.95v1.54h0.19q0.55,-0.89 1.56,-1.39 1.01,-0.53 2.18,-0.53 2.21,0 3.36,1.34 1.15,1.34 1.15,3.67v7.61h-3.14v-7.22q0,-1.18 -0.6,-1.82 -0.6,-0.65 -1.68,-0.65 -1.27,0 -2.06,0.98 -0.77,0.98 -0.77,2.47v6.24h-3.14z"
android:fillColor="#282c37"/>
<path
android:pathData="m4.53,4.54c-1.37,0 -2.47,0.48 -3.31,1.42C0.41,6.9 0,8.16 0,9.76v7.8h3.09v-7.57c0,-1.6 0.67,-2.41 2.01,-2.41 1.48,0 2.23,0.96 2.23,2.86v4.14h3.07v-4.14c0,-1.9 0.74,-2.86 2.23,-2.86 1.34,0 2.01,0.81 2.01,2.41v7.57h3.09v-7.8c0,-1.59 -0.41,-2.86 -1.22,-3.8 -0.84,-0.94 -1.94,-1.42 -3.31,-1.42 -1.58,0 -2.78,0.61 -3.57,1.82l-0.77,1.29 -0.77,-1.29C7.3,5.15 6.11,4.54 4.53,4.54Z"
android:strokeWidth="0.990258"
android:fillColor="#282c37"/>
</vector>

View File

@@ -2,7 +2,7 @@
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="oval">
<stroke android:color="@color/gray_50" android:width="4dp"/>
<stroke android:color="?colorSecondary" android:width="4dp"/>
<solid android:color="#80000000"/>
<size android:width="52dp" android:height="52dp"/>
</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="#000"/>
<corners android:radius="4dp"/>
</shape>

View File

@@ -1,7 +1,7 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="257dp"
android:height="67dp"
android:width="134dp"
android:height="34dp"
android:viewportWidth="313"
android:viewportHeight="81">
<path
@@ -20,8 +20,8 @@
</path>
<path
android:pathData="M14.81,23.2C14.81,20.72 16.77,18.72 19.2,18.72C21.62,18.72 23.58,20.73 23.58,23.2C23.58,25.67 21.62,27.68 19.2,27.68C16.77,27.68 14.81,25.67 14.81,23.2Z"
android:fillColor="#ffffff"/>
android:fillColor="#000"/>
<path
android:pathData="M80.02,27.06V47.66H72.03V27.67C72.03,23.45 70.3,21.32 66.83,21.32C63,21.32 61.07,23.87 61.07,28.87V39.82H53.14V28.87C53.14,23.84 51.24,21.32 47.38,21.32C43.92,21.32 42.18,23.45 42.18,27.67V47.65H34.21V27.06C34.21,22.86 35.25,19.51 37.35,17.03C39.53,14.54 42.37,13.29 45.89,13.29C49.97,13.29 53.07,14.9 55.11,18.11L57.11,21.52L59.1,18.11C61.14,14.91 64.23,13.29 68.32,13.29C71.84,13.29 74.69,14.55 76.86,17.03C78.96,19.51 80.01,22.83 80.01,27.06H80.02ZM107.49,37.3C109.15,35.51 109.93,33.29 109.93,30.59C109.93,27.89 109.14,25.65 107.49,23.94C105.91,22.15 103.89,21.3 101.45,21.3C99.02,21.3 97.01,22.15 95.41,23.94C93.83,25.65 93.04,27.89 93.04,30.59C93.04,33.29 93.83,35.53 95.41,37.3C97,39 99.02,39.87 101.45,39.87C103.89,39.87 105.9,39.02 107.49,37.3ZM109.93,14.12H117.8V47.06H109.93V43.18C107.55,46.41 104.26,48 99.99,48C95.71,48 92.42,46.36 89.5,43C86.64,39.64 85.18,35.48 85.18,30.61C85.18,25.74 86.65,21.65 89.5,18.29C92.43,14.93 95.92,13.23 99.99,13.23C104.06,13.23 107.55,14.81 109.93,18.02V14.14V14.12ZM144.26,29.97C146.58,31.76 147.73,34.25 147.67,37.41C147.67,40.77 146.52,43.41 144.14,45.24C141.76,47.03 138.89,47.94 135.41,47.94C129.13,47.94 124.87,45.3 122.61,40.11L129.43,35.96C130.34,38.78 132.35,40.25 135.41,40.25C138.22,40.25 139.62,39.33 139.62,37.42C139.62,36.03 137.79,34.78 134.07,33.8C132.66,33.41 131.5,33.01 130.6,32.68C129.31,32.16 128.22,31.56 127.31,30.83C125.05,29.04 123.9,26.68 123.9,23.65C123.9,20.42 124.99,17.85 127.19,16C129.45,14.09 132.19,13.18 135.48,13.18C140.73,13.18 144.56,15.48 147.07,20.16L140.37,24.1C139.4,21.86 137.74,20.74 135.48,20.74C133.11,20.74 131.95,21.65 131.95,23.44C131.95,24.83 133.78,26.08 137.5,27.06C140.37,27.72 142.63,28.7 144.26,29.97H144.27H144.26ZM169.26,22.27H162.37V35.98C162.37,37.63 162.98,38.63 164.15,39.08C165,39.4 166.71,39.47 169.27,39.34V47.05C163.98,47.71 160.14,47.17 157.88,45.41C155.62,43.7 154.53,40.53 154.53,36V22.27H149.23V14.1H154.53V7.46L162.39,4.89V14.12H169.29V22.29H169.27L169.26,22.27ZM194.34,37.1C195.92,35.4 196.71,33.22 196.71,30.58C196.71,27.94 195.92,25.78 194.34,24.05C192.74,22.35 190.79,21.48 188.42,21.48C186.04,21.48 184.09,22.33 182.49,24.05C180.97,25.84 180.18,28 180.18,30.58C180.18,33.16 180.97,35.31 182.49,37.1C184.08,38.81 186.04,39.67 188.42,39.67C190.79,39.67 192.74,38.82 194.34,37.1ZM176.96,42.96C173.85,39.6 172.32,35.52 172.32,30.58C172.32,25.63 173.85,21.62 176.96,18.26C180.07,14.9 183.91,13.19 188.42,13.19C192.92,13.19 196.77,14.9 199.87,18.26C202.97,21.62 204.57,25.77 204.57,30.58C204.57,35.39 202.97,39.6 199.87,42.96C196.76,46.32 192.98,47.96 188.42,47.96C183.85,47.96 180.06,46.32 176.96,42.96ZM230.86,37.29C232.45,35.5 233.24,33.28 233.24,30.58C233.24,27.87 232.45,25.63 230.86,23.93C229.28,22.14 227.26,21.29 224.82,21.29C222.39,21.29 220.37,22.14 218.73,23.93C217.14,25.63 216.35,27.87 216.35,30.58C216.35,33.28 217.14,35.52 218.73,37.29C220.38,38.99 222.45,39.86 224.82,39.86C227.2,39.86 229.27,39 230.86,37.29ZM233.24,0.92H241.11V47.05H233.24V43.17C230.93,46.39 227.63,47.99 223.36,47.99C219.09,47.99 215.75,46.35 212.8,42.98C209.93,39.62 208.48,35.47 208.48,30.6C208.48,25.73 209.95,21.64 212.8,18.28C215.72,14.92 219.26,13.22 223.36,13.22C227.45,13.22 230.93,14.8 233.24,18.01V0.93V0.92ZM268.74,37.07C270.32,35.36 271.12,33.18 271.12,30.54C271.12,27.9 270.32,25.74 268.74,24.01C267.15,22.31 265.21,21.45 262.82,21.45C260.43,21.45 258.5,22.3 256.9,24.01C255.37,25.8 254.58,27.96 254.58,30.54C254.58,33.12 255.37,35.28 256.9,37.07C258.48,38.77 260.44,39.64 262.82,39.64C265.2,39.64 267.14,38.78 268.74,37.07ZM251.36,42.92C248.26,39.56 246.73,35.48 246.73,30.54C246.73,25.6 248.25,21.58 251.36,18.22C254.47,14.86 258.32,13.15 262.82,13.15C267.32,13.15 271.18,14.86 274.27,18.22C277.38,21.58 278.97,25.73 278.97,30.54C278.97,35.35 277.38,39.56 274.27,42.92C271.16,46.28 267.38,47.93 262.82,47.93C258.26,47.93 254.46,46.28 251.36,42.92ZM313,26.78V47.01H305.14V27.84C305.14,25.66 304.59,24.01 303.48,22.77C302.45,21.65 300.98,21.07 299.09,21.07C294.65,21.07 292.39,23.77 292.39,29.24V47.03H284.53V14.1H292.39V17.81C294.28,14.71 297.28,13.19 301.47,13.19C304.82,13.19 307.57,14.37 309.71,16.81C311.91,19.24 313,22.54 313,26.82"
android:fillColor="#ffffff"/>
android:fillColor="#000"/>
</vector>

View File

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

View File

@@ -15,6 +15,7 @@
android:outlineProvider="background"
android:elevation="2dp">
<ImageView
android:id="@+id/icon"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_margin="16dp"

View File

@@ -12,7 +12,8 @@
<LinearLayout
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_height="wrap_content"
android:paddingVertical="12dp"
android:outlineProvider="background"
android:elevation="2dp"
android:background="@drawable/bg_poll_option_clickable"
@@ -25,7 +26,6 @@
android:layout_height="24dp"
android:layout_marginStart="12dp"
android:layout_gravity="center_vertical"
android:duplicateParentState="true"
android:tint="?colorDarkIcon"
android:src="@drawable/ic_poll_option_button"/>
@@ -48,9 +48,7 @@
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:textAppearance="@style/m3_title_medium"
android:singleLine="true"
android:ellipsize="end"
tools:text="scream into void"/>
tools:text="scream into void. like this: aaaaaaaaaaaaaaaaaaaa"/>
</LinearLayout>

View File

@@ -7,6 +7,7 @@
android:paddingBottom="12dp">
<LinearLayout
android:id="@+id/text_wrap"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
@@ -17,7 +18,8 @@
android:layout_marginTop="6dp"
android:layout_marginBottom="8dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
android:layout_height="wrap_content"
android:visibility="gone">
<View
android:id="@+id/border_top"
android:layout_width="match_parent"
@@ -47,10 +49,57 @@
android:layout_height="wrap_content"
android:paddingHorizontal="16dp"
android:textSize="16sp"
android:textAppearance="@style/m3_body_large"
tools:text="setting up my mstdn"/>
</LinearLayout>
android:textAppearance="@style/m3_body_large"/>
<org.joinmastodon.android.ui.views.AutoOrientationLinearLayout
android:id="@+id/translate_wrap"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:gravity="center_vertical">
<FrameLayout
android:id="@+id/action_btn_wrap"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="8dp"
android:clipToPadding="false">
<org.joinmastodon.android.ui.views.ProgressBarButton
android:id="@+id/translate_btn"
style="?secondaryButtonStyle"
android:background="?android:selectableItemBackground"
android:textColor="?android:textColorSecondary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingHorizontal="8dp"
tools:text="@string/translate_post"/>
<ProgressBar
android:id="@+id/translate_progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:indeterminate="true"
style="?android:progressBarStyleSmall"
android:elevation="10dp"
android:outlineProvider="none"
android:indeterminateTint="?android:textColorPrimary"
android:visibility="gone"/>
</FrameLayout>
<TextView
android:id="@+id/translate_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginVertical="4dp"
android:layout_weight="1"
android:textColor="?android:textColorSecondary"
android:textAlignment="textEnd"
tools:text="Translated using TranslateEngine" />
</org.joinmastodon.android.ui.views.AutoOrientationLinearLayout>
</LinearLayout>
<LinearLayout
android:visibility="gone"

View File

@@ -132,6 +132,7 @@
android:outlineProvider="background"
android:elevation="2dp">
<ImageView
android:id="@+id/add_poll_option_icon"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_margin="16dp"
@@ -148,6 +149,39 @@
android:textAppearance="@style/m3_label_large"
android:textColor="?android:textColorPrimary"
tools:text="Duration: 7 days"/>
<LinearLayout
android:id="@+id/poll_allow_multiple"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:layout_marginTop="8dp"
android:gravity="center_vertical"
android:layoutDirection="locale"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingTop="12dp"
android:paddingBottom="12dp"
android:background="?android:selectableItemBackground">
<CheckBox
android:id="@+id/poll_allow_multiple_checkbox"
android:clickable="false"
android:layout_width="wrap_content"
android:layout_height="24dp"
android:layout_marginEnd="24dp"
android:duplicateParentState="true"
android:importantForAccessibility="no"/>
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textSize="16sp"
android:singleLine="true"
android:text="@string/sk_poll_allow_multiple" />
</LinearLayout>
</LinearLayout>
<org.joinmastodon.android.ui.views.ComposeMediaLayout
@@ -162,6 +196,7 @@
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:layout_marginTop="8dp"
android:gravity="center_vertical"
android:layoutDirection="locale"

View File

@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<me.grishka.appkit.views.FragmentRootLinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:id="@+id/appkit_loader_root"
xmlns:android="http://schemas.android.com/apk/res/android"
android:background="?colorM3Background">
<include layout="@layout/appkit_toolbar"/>
<FrameLayout
android:id="@+id/appkit_loader_content"
android:layout_width="match_parent"
android:layout_height="0px"
android:layout_weight="1">
<include layout="@layout/loading"
android:id="@+id/loading"/>
<ViewStub android:layout="?errorViewLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/error"
android:visibility="gone"/>
<View
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/content_stub"/>
</FrameLayout>
<LinearLayout
android:id="@+id/button_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/btn_next"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="16dp"
android:minWidth="145dp"
style="@style/Widget.Mastodon.M3.Button.Filled"
android:text="@string/next" />
</LinearLayout>
</me.grishka.appkit.views.FragmentRootLinearLayout>

View File

@@ -0,0 +1,60 @@
<?xml version="1.0" encoding="utf-8"?>
<me.grishka.appkit.views.FragmentRootLinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:id="@+id/appkit_loader_root"
xmlns:android="http://schemas.android.com/apk/res/android"
android:background="?colorBackgroundLight">
<include layout="@layout/appkit_toolbar"/>
<FrameLayout
android:id="@+id/appkit_loader_content"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<include layout="@layout/loading"
android:id="@+id/loading"/>
<ViewStub android:layout="?errorViewLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/error"
android:visibility="gone"/>
<View
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/content_stub"/>
</FrameLayout>
<View
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:background="?attr/colorPollVoted"/>
<LinearLayout
android:id="@+id/button_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?colorBackgroundLight"
android:outlineProvider="bounds"
android:orientation="horizontal"
android:elevation="0dp">
<Button
style="?primaryLargeButtonStyle"
android:id="@+id/btn_next"
android:minWidth="145dp"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:layout_weight="1"
android:text="@string/next" />
</LinearLayout>
</me.grishka.appkit.views.FragmentRootLinearLayout>

View File

@@ -1,12 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<org.joinmastodon.android.ui.views.SizeListenerFrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
<org.joinmastodon.android.ui.views.SizeListenerFrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:clipToPadding="false"
android:clipChildren="false">
android:clipToPadding="false">
<View
android:id="@+id/blue_fill"
@@ -18,51 +17,60 @@
<FrameLayout
android:id="@+id/art_container"
android:layout_width="450dp"
android:layout_height="631dp"
android:layout_marginTop="40dp"
android:layout_gravity="center">
android:layout_width="360dp"
android:layout_height="640dp"
android:layout_gravity="center"
tools:ignore="rtlHardcoded">
<ImageView
android:id="@+id/art_clouds"
android:layout_width="450dp"
android:layout_height="589dp"
android:layout_gravity="bottom|center_horizontal"
android:layout_width="414dp"
android:layout_height="541dp"
android:layout_marginTop="91dp"
android:layout_gravity="top|left"
android:alpha="0.3"
android:importantForAccessibility="no"
android:src="@drawable/splash_art_layer0"/>
<ImageView
android:id="@+id/art_plane_elephant"
android:layout_width="245.64dp"
android:layout_height="72.65dp"
android:layout_marginLeft="-101.55dp"
android:layout_marginTop="238.12dp"
android:layout_gravity="left|top"
android:alpha="0.3"
android:importantForAccessibility="no"
android:src="@drawable/splash_art_layer4"/>
<ImageView
android:id="@+id/art_right_hill"
android:layout_width="218dp"
android:layout_height="255dp"
android:layout_gravity="bottom|right"
android:layout_marginBottom="156dp"
android:layout_marginRight="11dp"
android:layout_width="150.84dp"
android:layout_height="176.44dp"
android:layout_gravity="top|left"
android:layout_marginLeft="322dp"
android:layout_marginTop="310dp"
android:importantForAccessibility="no"
android:src="@drawable/splash_art_layer1"/>
<ImageView
android:id="@+id/art_left_hill"
android:layout_width="285dp"
android:layout_height="222dp"
android:layout_gravity="bottom|left"
android:layout_marginLeft="-6dp"
android:layout_marginBottom="243dp"
android:layout_width="197.2dp"
android:layout_height="153.61dp"
android:layout_gravity="top|left"
android:layout_marginTop="294dp"
android:importantForAccessibility="no"
android:src="@drawable/splash_art_layer2"/>
<ImageView
android:id="@+id/art_center_hill"
android:layout_width="457dp"
android:layout_height="397dp"
android:layout_gravity="bottom|center_horizontal"
android:layout_marginBottom="51dp"
android:layout_width="400dp"
android:layout_height="346dp"
android:layout_gravity="top|left"
android:layout_marginTop="294dp"
android:importantForAccessibility="no"
android:src="@drawable/splash_art_layer3"/>
<ImageView
android:id="@+id/art_plane_elephant"
android:layout_width="355dp"
android:layout_height="105dp"
android:layout_gravity="left|top"
android:src="@drawable/splash_art_layer4"/>
</FrameLayout>
<View
@@ -73,36 +81,65 @@
android:transformPivotY="1px"
android:background="#478E6A"/>
<org.joinmastodon.android.ui.views.SplashLogoView
android:layout_width="261dp"
android:layout_height="71dp"
android:layout_gravity="center_horizontal|top"
android:layout_marginTop="24dp"
android:scaleType="center"
android:importantForAccessibility="no"
android:src="@drawable/splash_logo"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:padding="16dp"
android:layout_height="match_parent"
android:clipToPadding="false"
android:orientation="vertical">
<Button
android:id="@+id/btn_get_started"
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
<LinearLayout
android:id="@+id/pager_dots"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="13dp"
style="@style/Widget.Mastodon.Button.Large.Primary_LightOnDark"
android:text="@string/get_started"/>
android:layout_gravity="center_horizontal"
android:layout_marginBottom="16dp"
android:orientation="horizontal">
<View
android:layout_width="8dp"
android:layout_height="8dp"
android:background="@drawable/white_circle"/>
<View
android:layout_width="8dp"
android:layout_height="8dp"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:alpha="0.3"
android:background="@drawable/white_circle"/>
<View
android:layout_width="8dp"
android:layout_height="8dp"
android:alpha="0.3"
android:background="@drawable/white_circle"/>
</LinearLayout>
<Button
android:id="@+id/btn_log_in"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/Widget.Mastodon.Button.Large.Primary_DarkOnLight"
android:background="@drawable/bg_button_green"
android:text="@string/log_in"/>
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
style="@style/Widget.Mastodon.M3.Button.Text"
android:textColor="#fff"
android:text="@string/already_have_account"/>
<Button
android:id="@+id/btn_get_started"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginBottom="16dp"
style="@style/Widget.Mastodon.M3.Button.Filled"
android:text="@string/get_started"/>
</LinearLayout>
</org.joinmastodon.android.ui.views.SizeListenerFrameLayout>

View File

@@ -0,0 +1,59 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<include layout="@layout/display_item_header" />
<TextView
style="@style/m3_headline_small"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginVertical="12dp"
android:text="@string/sk_welcome_title"
/>
<TextView
style="@style/m3_body_large"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="16dp"
android:text="@string/sk_welcome_text" />
<View
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:layout_marginTop="16dp"
android:background="?attr/colorPollVoted"/>
<EditText
android:id="@+id/search_edit"
android:layout_width="match_parent"
android:layout_height="56dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="8dp"
android:layout_marginHorizontal="16dp"
android:inputType="textFilter|textNoSuggestions"
android:singleLine="true"
android:imeOptions="actionGo"
android:drawableStart="@drawable/ic_fluent_globe_20_regular"
android:drawablePadding="12dp"
android:drawableTint="?android:textColorSecondary"
android:background="@drawable/bg_search_field"
android:paddingHorizontal="16dp"
android:elevation="0dp"
android:hint="@string/sk_example_domain"/>
<ViewStub
android:layout="?errorViewLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/error"
android:visibility="gone" />
</LinearLayout>

View File

@@ -0,0 +1,71 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="@style/m3_body_large"
android:paddingStart="56dp"
android:paddingEnd="24dp"
android:text="@string/login_subtitle"/>
<org.joinmastodon.android.ui.views.FloatingHintEditTextLayout
android:layout_width="match_parent"
android:layout_height="56dp"
android:layout_marginTop="8dp"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginBottom="8dp"
android:paddingTop="8dp"
android:paddingBottom="8dp"
app:editTextOffsetY="8dp"
android:background="@drawable/rect_4dp"
android:backgroundTint="?colorM3SurfaceVariant">
<EditText
android:id="@+id/search_edit"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@null"
android:stateListAnimator="@null"
android:textColor="?colorM3OnSurfaceVariant"
android:textColorHint="?colorM3OnSurfaceVariant"
android:inputType="textUri"
android:importantForAutofill="no"
android:paddingStart="48dp"
android:layout_marginEnd="52dp"
android:drawablePadding="16dp"
android:textAppearance="@style/m3_body_large"
android:hint="@string/server_url"/>
<ImageView
android:layout_width="44dp"
android:layout_height="20dp"
android:layout_gravity="start|center_vertical"
android:scaleType="center"
android:importantForAccessibility="no"
android:tint="?colorM3OnSurfaceVariant"
android:src="@drawable/ic_m3_search"/>
<ImageButton
android:id="@+id/search_clear"
android:layout_width="36dp"
android:layout_height="36dp"
android:layout_marginEnd="6dp"
android:background="?android:selectableItemBackgroundBorderless"
android:layout_gravity="center_vertical|end"
android:visibility="gone"
tools:visibility="visible"
android:contentDescription="@string/clear"
android:stateListAnimator="@null"
android:elevation="0dp"
android:src="@drawable/ic_m3_cancel"/>
</org.joinmastodon.android.ui.views.FloatingHintEditTextLayout>
</LinearLayout>

View File

@@ -0,0 +1,53 @@
<?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:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="16dp"
android:paddingEnd="24dp"
android:paddingTop="12dp"
android:paddingBottom="12dp"
android:background="?colorM3SurfaceVariant">
<RadioButton
android:id="@+id/radiobtn"
android:layout_width="28dp"
android:layout_height="24dp"
android:layout_marginEnd="23dp"
android:layout_marginStart="-3dp"
android:layout_alignParentTop="true"
android:layout_alignParentStart="true"
android:buttonTint="@color/m3_radiobutton_tint"
android:background="@null"
android:focusable="false"
android:clickable="false"/>
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toEndOf="@id/radiobtn"
android:layout_alignParentTop="true"
android:textAppearance="@style/m3_body_large"
android:singleLine="true"
android:ellipsize="end"
android:gravity="center_vertical"
android:textSize="16sp"
android:minHeight="24dp"
android:textColor="?colorM3OnSurface"
tools:text="mastodon.social"/>
<TextView
android:id="@+id/description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toEndOf="@id/radiobtn"
android:layout_below="@id/title"
android:layout_marginBottom="8dp"
android:textAppearance="@style/m3_body_medium"
android:textColor="?colorM3OnSurfaceVariant"
android:textSize="14sp"
android:lineSpacingExtra="4sp"
tools:text="General-purpose server run by the lead developer of Mastodon"/>
</RelativeLayout>

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="16dp"
android:paddingVertical="8dp">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bg_search_field">
<include layout="@layout/item_instance_catalog" />
</FrameLayout>
</FrameLayout>

View File

@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<org.joinmastodon.android.ui.views.AutoOrientationLinearLayout 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="48dp"
android:gravity="center_vertical"
android:layoutDirection="locale">
<ImageView
android:id="@+id/icon"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginStart="16dp"
android:layout_marginEnd="32dp"
android:importantForAccessibility="no"
android:tint="?android:textColorPrimary"
tools:src="@drawable/ic_color_theme_preference"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:gravity="center_vertical"
android:textColor="?android:textColorPrimary"
android:textSize="16sp"
android:singleLine="true"
android:ellipsize="end"
android:text="@string/sk_settings_color_picker"/>
<Button
android:id="@+id/color_picker_button"
android:layout_width="wrap_content"
android:layout_height="32dp"
android:background="@drawable/bg_inline_button"
android:elevation="0dp"
android:ellipsize="middle"
android:fontFamily="sans-serif-medium"
android:singleLine="true"
android:stateListAnimator="@null"
android:textColor="?android:textColorPrimary"
android:textSize="16sp"
tools:text="@string/sk_color_theme_pink" />
</org.joinmastodon.android.ui.views.AutoOrientationLinearLayout>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/pink_color" android:title="@string/sk_color_theme_pink"/>
<item android:id="@+id/purple_color" android:title="@string/sk_color_theme_purple"/>
<item android:id="@+id/green_color" android:title="@string/sk_color_theme_green"/>
<item android:id="@+id/blue_color" android:title="@string/sk_color_theme_blue"/>
<item android:id="@+id/brown_color" android:title="@string/sk_color_theme_brown"/>
<item android:id="@+id/yellow_color" android:title="@string/sk_color_theme_yellow"/>
</menu>

View File

@@ -4,5 +4,6 @@
android:id="@+id/follow_requests"
android:icon="@drawable/ic_follow_requests_24_badged"
android:showAsAction="always"
android:visible="false"
android:title="@string/sk_follow_requests" />
</menu>

View File

@@ -3,9 +3,7 @@
<string name="get_started">الخطوات الأولى</string>
<string name="log_in">تسجيلُ الدخول</string>
<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>

View File

@@ -3,9 +3,7 @@
<string name="get_started">Kreni</string>
<string name="log_in">Loguj se</string>
<string name="next">Dalje</string>
<string name="loading_instance">Čekamo potrebne informacije…</string>
<string name="error">Greška</string>
<string name="not_a_mastodon_instance">%s ne izgleda kao Mastodon platforma.</string>
<string name="ok">OK</string>
<string name="preparing_auth">Pripremamo autorizaciju…</string>
<string name="finishing_auth">Završavamo autorizaciju…</string>

View File

@@ -3,9 +3,7 @@
<string name="get_started">Comença</string>
<string name="log_in">Inicia sessió</string>
<string name="next">Següent</string>
<string name="loading_instance">Obtenint informació sobre la instància…</string>
<string name="error">Error</string>
<string name="not_a_mastodon_instance">Sembla que %s no és una instància de Mastodon.</string>
<string name="ok">D\'acord</string>
<string name="preparing_auth">Preparant l\'autenticació…</string>
<string name="finishing_auth">Finalitzant l\'autenticació…</string>
@@ -382,5 +380,8 @@
<string name="i_agree">Dacord</string>
<string name="empty_list">Aquesta llista està buida</string>
<string name="instance_signup_closed">Aquest servidor no accepta nous registres.</string>
<string name="text_copied">Copiat al porta-retalls</string>
<string name="add_bookmark">Marca</string>
<string name="remove_bookmark">Elimina el marcador</string>
<string name="bookmarks">Marcadors</string>
</resources>

View File

@@ -1,4 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="sk_pinned_posts">Fixat</string>
</resources>
<string name="sk_pinned_posts">Fixat</string>
<string name="sk_delete_and_redraft">Elimina i torna a escriure</string>
<string name="sk_confirm_delete_and_redraft_title">Eliminar i tornar a escriure</string>
<string name="sk_confirm_pin_post">Vols fixar aquesta publicació al teu perfil\?</string>
<string name="sk_pinning">S\'està fixant…</string>
<string name="sk_unpin_post">Deixa de fixar</string>
<string name="sk_confirm_unpin_post_title">Deixar de fixar al perfil</string>
<string name="sk_settings_show_replies">Mostra les respostes</string>
<string name="sk_settings_load_new_posts">Carrega automàticament les publicacions noves</string>
<string name="sk_federated_timeline">Federació</string>
<string name="sk_federated_timeline_info_banner">Aquestes són les publicacions més recents de gent de la teua federació.</string>
<string name="sk_update_ready">Megalodon %s s\'ha baixat i està preparat per a instal·lar-se.</string>
<string name="sk_accept_follow_request">Accepta la sol·licitud</string>
<string name="sk_disable_marquee">Desactiva el desplaçament de text a les barres de títol</string>
<string name="sk_user_post_notifications_on">Notificacions activades per a publicacions de: %s</string>
<string name="sk_app_name">Megalodon</string>
<string name="sk_pin_post">Fixa al perfil</string>
<string name="sk_confirm_pin_post_title">Fixar al perfil</string>
<string name="sk_confirm_delete_and_redraft">Segur que vols eliminar i tornar a escriure aquesta publicació\?</string>
<string name="sk_confirm_unpin_post">Segur que vols deixar de fixar aquesta publicació\?</string>
<string name="sk_unpinning">S\'està deixant de fixar…</string>
<string name="sk_image_description">Descripció de la imatge</string>
<string name="sk_visibility_unlisted">No llistat</string>
<string name="sk_settings_show_boosts">Mostra els impulsos</string>
<string name="sk_settings_show_interaction_counts">Mostra el recompte d\'interaccions</string>
<string name="sk_settings_app_version">Megalodon v. %1$s (%2$d)</string>
<string name="sk_user_post_notifications_off">Notificacions desactivades per a publicacions de: %s</string>
<string name="sk_mark_media_as_sensitive">Marca el contingut com a sensible</string>
<string name="sk_update_available">Megalodon %s està preparat per a baixar-se.</string>
<string name="sk_check_for_update">Comprova actualitzacions</string>
<string name="sk_no_update_available">No hi ha cap actualització disponible</string>
<string name="sk_list_timelines">Llistes</string>
<string name="sk_follow_requests">Sol·licituds de seguiment</string>
<string name="sk_reject_follow_request">Rebutja la sol·licitud</string>
<string name="sk_lists_with_user">Llistes amb %s</string>
<string name="sk_settings_always_reveal_content_warnings">Mostra sempre els avisos de contingut</string>
<string name="sk_settings_contribute">Contribueix a Megalodon</string>
<string name="sk_settings_show_federated_timeline">Mostra la línia de temps federada</string>
<string name="sk_notification_type_status">Publicacions</string>
<string name="sk_notify_posts">Notificacions de publicacions</string>
<string name="sk_settings_color_picker">Color de tema</string>
<string name="sk_color_theme_pink">Rosa</string>
<string name="sk_color_theme_purple">Lila</string>
<string name="sk_color_theme_green">Verd</string>
<string name="sk_color_theme_blue">Blau</string>
<string name="sk_color_theme_brown">Marró</string>
<string name="sk_color_theme_yellow">Groc</string>
</resources>

View File

@@ -3,9 +3,7 @@
<string name="get_started">Začínáme</string>
<string name="log_in">Přihlásit se</string>
<string name="next">Další</string>
<string name="loading_instance">Získávání informací o instanci…</string>
<string name="error">Chyba</string>
<string name="not_a_mastodon_instance">Zdá se, že %s není instancí Mastodonu.</string>
<string name="ok">OK</string>
<string name="preparing_auth">Příprava na autentizaci…</string>
<string name="finishing_auth">Dokončení autentizace…</string>

View File

@@ -3,9 +3,9 @@
<string name="get_started">Loslegen</string>
<string name="log_in">Anmelden</string>
<string name="next">Weiter</string>
<string name="loading_instance">Instanzinformationen werden geladen </string>
<string name="loading_instance">Server-Informationen werden abgerufen </string>
<string name="error">Fehler</string>
<string name="not_a_mastodon_instance">%s scheint keine Mastodon-Instanz zu sein.</string>
<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>
@@ -13,7 +13,7 @@
<string name="in_reply_to">Als Antwort auf %s</string>
<string name="notifications">Benachrichtigungen</string>
<string name="user_followed_you">folgt dir jetzt</string>
<string name="user_sent_follow_request">hat dir eine Folgeanfrage gesendet</string>
<string name="user_sent_follow_request">hat dir eine Follower-Anfrage gesendet</string>
<string name="user_favorited">favorisierte</string>
<string name="notification_boosted">teilte</string>
<string name="poll_ended">Abstimmung beendet</string>
@@ -47,11 +47,11 @@
<string name="button_following">Folge ich</string>
<string name="edit_profile">Profil bearbeiten</string>
<string name="mention_user">%s erwähnen</string>
<string name="share_user">%s teilen</string>
<string name="share_user">Profil %s teilen</string>
<string name="mute_user">%s stummschalten</string>
<string name="unmute_user">%s nicht mehr stummschalten</string>
<string name="block_user">%s sperren</string>
<string name="unblock_user">%s nicht mehr sperren</string>
<string name="unblock_user">%s entsperren</string>
<string name="report_user">%s melden</string>
<string name="block_domain">%s sperren</string>
<string name="unblock_domain">%s nicht mehr sperren</string>
@@ -61,10 +61,10 @@
</plurals>
<string name="profile_joined">Beigetreten</string>
<string name="done">Fertig</string>
<string name="loading">wird geladen </string>
<string name="loading">Wird geladen </string>
<string name="field_label">Beschriftung</string>
<string name="field_content">Inhalt</string>
<string name="saving">Speichern </string>
<string name="saving">wird gespeichert </string>
<string name="post_from_user">Beitrag von %s</string>
<string name="poll_option_hint">%d. Auswahl</string>
<plurals name="x_minutes">
@@ -81,20 +81,20 @@
</plurals>
<string name="compose_poll_duration">Dauer: %s</string>
<plurals name="x_seconds_left">
<item quantity="one">%d Sekunde verbleibend</item>
<item quantity="other">%d Sekunden verbleibend</item>
<item quantity="one">noch %d Sekunde</item>
<item quantity="other">noch %d Sekunden</item>
</plurals>
<plurals name="x_minutes_left">
<item quantity="one">%d Minute verbleibend</item>
<item quantity="other">%d Minuten verbleibend</item>
<item quantity="one">noch %d Minute</item>
<item quantity="other">noch %d Minuten</item>
</plurals>
<plurals name="x_hours_left">
<item quantity="one">%d Stunde verbleibend</item>
<item quantity="other">%d Stunden verbleibend</item>
<item quantity="one">noch %d Stunde</item>
<item quantity="other">noch %d Stunden</item>
</plurals>
<plurals name="x_days_left">
<item quantity="one">%d Tag verbleibend</item>
<item quantity="other">%d Tage verbleibend</item>
<item quantity="one">noch %d Tag</item>
<item quantity="other">noch %d Tage</item>
</plurals>
<plurals name="x_voters">
<item quantity="one">%,d Stimme</item>
@@ -109,12 +109,12 @@
<string name="do_unmute">Nicht mehr stummschalten</string>
<string name="confirm_block_title">Konto sperren</string>
<string name="confirm_block_domain_title">Domain sperren</string>
<string name="confirm_block">Bestätigen, um %s zu blockieren</string>
<string name="do_block">Ja, blockieren</string>
<string name="confirm_block">Bestätigen, um %s zu sperren</string>
<string name="do_block">Sperren</string>
<string name="confirm_unblock_title">Konto nicht mehr sperren</string>
<string name="confirm_unblock_domain_title">Domain nicht mehr blockieren</string>
<string name="confirm_unblock">Bestätigen, um %s nicht mehr zu blockieren</string>
<string name="do_unblock">Nicht mehr blockieren</string>
<string name="confirm_unblock">Bestätigen, um Sperre von %s aufzuheben</string>
<string name="do_unblock">Sperre aufheben</string>
<string name="button_muted">Stummgeschaltet</string>
<string name="button_blocked">Blockiert</string>
<string name="action_vote">Abstimmen</string>
@@ -147,63 +147,63 @@
<string name="report_choose_reason_account">Was stimmt mit %s nicht?</string>
<string name="report_choose_reason_subtitle">Bitte das Bestmögliche auswählen</string>
<string name="report_reason_personal">Das gefällt mir nicht</string>
<string name="report_reason_personal_subtitle">Den Inhalt kann man nicht allen zumuten</string>
<string name="report_reason_personal_subtitle">Das ist nichts, was du sehen möchtest</string>
<string name="report_reason_spam">Das ist Spam</string>
<string name="report_reason_spam_subtitle">Bösartige Links, vorgetäuschtes Verhalten oder wiederholtes Antworten</string>
<string name="report_reason_violation">Es verstößt gegen Serverregeln</string>
<string name="report_reason_violation_subtitle">Du weißt, welche Regeln verletzt werden</string>
<string name="report_reason_violation_subtitle">Du bist dir bewusst, dass dies gegen die Serverregeln verstößt</string>
<string name="report_reason_other">Es ist etwas anderes</string>
<string name="report_reason_other_subtitle">Das Problem passt nicht in eine der Kategorien</string>
<string name="report_choose_rule">Welche Regeln werden verletzt?</string>
<string name="report_reason_other_subtitle">Der Vorfall passt zu keiner dieser Kategorien</string>
<string name="report_choose_rule">Gegen welche Regeln wird verstoßen?</string>
<string name="report_choose_rule_subtitle">Alles Zutreffende auswählen</string>
<string name="report_choose_posts">Gibt es Beiträge, die diesen Bericht unterstützen?</string>
<string name="report_choose_posts">Gibt es Beiträge, die diese Meldung untermauern?</string>
<string name="report_choose_posts_subtitle">Alles Zutreffende auswählen</string>
<string name="report_comment_title">Gibt es etwas anderes, was wir wissen sollten?</string>
<string name="report_comment_hint">Zusätzliche Kommentare</string>
<string name="sending_report">Bericht wird gesendet</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="report_sent_title">Vielen Dank für die Meldung, wir werden uns damit befassen.</string>
<string name="report_sent_subtitle">Während wir dies überprüfen, kannst du gegen %s vorgehen.</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>
<string name="unfollow">Entfolgen</string>
<string name="mute_user_explain">Du wirst die eigenen und geteilten Beiträge des Kontos nicht mehr sehen können. Dass du das Profil stummgeschaltet hast, erfährt die Person nicht.</string>
<string name="block_user_explain">Du wirst die Beiträge von diesem Konto nicht sehen. Das Konto wird nicht in der Lage sein, deine Beiträge zu sehen oder dir zu folgen. Die Person hinter dem Konto wird wissen, dass du das Konto blockiert hast.</string>
<string name="report_personal_title">Du willst das nicht mehr sehen?</string>
<string name="report_personal_subtitle">Wenn du etwas auf Mastodon nicht sehen willst, kannst du den Nutzer aus deiner Erfahrung streichen.</string>
<string name="block_user_explain">Dir wird es nicht länger möglich sein, die Beiträge dieses Konto zu sehen. Das blockierte Profil wird nicht mehr in der Lage sein, deine Beiträge zu sehen oder dir zu folgen. Die Person hinter dem Konto wird mitbekommen, dass du ihr Konto gesperrt hast.</string>
<string name="report_personal_title">Möchtest du das nicht mehr sehen?</string>
<string name="report_personal_subtitle">Wenn du etwas auf Mastodon siehst, das dir nicht gefällt, kannst du die Person aus deinem Umfeld entfernen.</string>
<string name="back">Zurück</string>
<string name="instance_catalog_title">Mastodon wird von Benutzer*innen auf verschiedenen Servern gestaltet.</string>
<string name="instance_catalog_subtitle">Wähle einen Server basierend auf deinen Interessen oder deiner Region oder einfach einen allgemeinen. Du kannst trotzdem mit jedem interagieren, egal auf welchem Server.</string>
<string name="search_communities">Server suchen oder Adresse eingeben</string>
<string name="instance_rules_title">Einige Grundregeln</string>
<string name="instance_rules_subtitle">Nimm dir eine Minute Zeit, und gehe kurz alle Regeln von %s durch.</string>
<string name="search_communities">Server suchen oder Link eingeben</string>
<string name="instance_rules_title">Serverregeln</string>
<string name="instance_rules_subtitle">Nimm dir eine Minute Zeit und lies die Serverregeln von %s durch.</string>
<string name="signup_title">Okay, lass uns mit %s anfangen</string>
<string name="edit_photo">bearbeiten</string>
<string name="display_name">Anzeigename</string>
<string name="username">Kontoname</string>
<string name="username">Profilname</string>
<string name="email">E-Mail</string>
<string name="password">Passwort</string>
<string name="password_note">Verwende Großbuchstaben, Sonderzeichen und Zahlen, um deine Passwortstärke zu erhöhen.</string>
<string name="category_academia">Bildung</string>
<string name="category_activism">Aktivismus</string>
<string name="category_activism">Bürgerbeteiligung</string>
<string name="category_all">Alle</string>
<string name="category_art">Kunst</string>
<string name="category_food">Essen</string>
<string name="category_furry">Flausch</string>
<string name="category_furry">Furries</string>
<string name="category_games">Spiele</string>
<string name="category_general">Allgemein</string>
<string name="category_journalism">Journalismus</string>
<string name="category_lgbt">LGBT</string>
<string name="category_music">Musik</string>
<string name="category_regional">Regional</string>
<string name="category_tech">Technologie</string>
<string name="category_tech">Technik</string>
<string name="confirm_email_title">Eine letzte Sache noch</string>
<string name="confirm_email_subtitle">Schaue kurz in dein E-Mail-Postfach und tippe den Link an, den wir dir gesendet haben.</string>
<string name="resend">Erneut senden</string>
<string name="confirm_email_subtitle">Tippe auf den Link, den wir dir per E-Mail geschickt haben, um dein Konto zu verifizieren.</string>
<string name="resend">Erneut abschicken</string>
<string name="open_email_app">E-Mail-App öffnen</string>
<string name="resent_email">Bestätigungs-E-Mail gesendet</string>
<string name="compose_hint">Eintippen oder einfügen, was dir am Herzen liegt</string>
<string name="content_warning">Inhaltwarnung</string>
<string name="resent_email">Bestätigung per E-Mail zugeschickt</string>
<string name="compose_hint">Was gibt\'s Neues? Was geht dir durch den Kopf? Was liegt dir am Herzen?</string>
<string name="content_warning">Inhaltswarnung</string>
<string name="add_image_description">Bildbeschreibung hinzufügen </string>
<string name="retry_upload">Hochladen erneut versuchen</string>
<string name="retry_upload">Erneut hochladen</string>
<string name="edit_image">Bild bearbeiten</string>
<string name="save">Speichern</string>
<string name="add_alt_text">Bildbeschreibung hinzufügen</string>
@@ -212,9 +212,9 @@
<string name="visibility_public">Öffentlich</string>
<string name="visibility_followers_only">Nur Follower</string>
<string name="visibility_private">Nur erwähnte Profile</string>
<string name="search_all">Alle</string>
<string name="search_people">Personen</string>
<string name="recent_searches">Letzte Suchanfragen</string>
<string name="search_all">Alles</string>
<string name="search_people">Profile</string>
<string name="recent_searches">Frühere Suchen</string>
<string name="step_x_of_n">Schritt %1$d von %2$d</string>
<string name="skip">Überspringen</string>
<string name="notification_type_follow">Neue Follower</string>
@@ -228,9 +228,9 @@
<item quantity="one">Du kannst nicht mehr als %d Mediendatei anhängen</item>
<item quantity="other">Du kannst nicht mehr als %d Mediendateien anhängen</item>
</plurals>
<string name="media_attachment_unsupported_type">Datei %s wird nicht unterstützt</string>
<string name="media_attachment_too_big">Datei %1$s übersteigt die Größengrenze von %2$s MB</string>
<string name="settings_theme">Design</string>
<string name="media_attachment_unsupported_type">Dateityp von %s wird nicht unterstützt</string>
<string name="media_attachment_too_big">Datei %1$s überschreitet die maximale Größe von %2$s MB</string>
<string name="settings_theme">Erscheinungsbild</string>
<string name="theme_auto">Systembedingt</string>
<string name="theme_light">Hell</string>
<string name="theme_dark">Dunkel</string>
@@ -254,15 +254,15 @@
<string name="settings_tos">Nutzungsbedingungen</string>
<string name="settings_privacy_policy">Datenschutzbestimmungen</string>
<string name="settings_spicy">Gefährliches</string>
<string name="settings_clear_cache">Medienpuffer leeren</string>
<string name="settings_clear_cache">Medien-Cache leeren</string>
<string name="settings_app_version">Mastodon für Android v%1$s (%2$d)</string>
<string name="media_cache_cleared">Medienpuffer geleert</string>
<string name="media_cache_cleared">Medien-Cache geleert</string>
<string name="confirm_log_out">Bist du dir sicher, dass du dich abmelden möchtest?</string>
<string name="sensitive_content">Inhaltswarnung</string>
<string name="sensitive_content_explain">Autor*in hat den Inhalt mit einer Inhaltswarnung versehen. Tippen zum Anzeigen.</string>
<string name="media_hidden">Tippen zum Anzeigen</string>
<string name="avatar_description">Das Profil von %s öffnen</string>
<string name="more_options">Mehr Optionen</string>
<string name="avatar_description">Profil von %s aufrufen</string>
<string name="more_options">Weitere Einstellungen</string>
<string name="reveal_content">Inhalt anzeigen</string>
<string name="hide_content">Inhalt ausblenden</string>
<string name="new_post">Neuer Beitrag</string>
@@ -287,12 +287,12 @@
<string name="signup_reason">Weshalb möchtest du beitreten?</string>
<string name="signup_reason_note">Das erleichtert uns die Prüfung deiner Anmeldung.</string>
<string name="clear">Leeren</string>
<string name="profile_header">Kopfbild</string>
<string name="profile_header">Titelbild</string>
<string name="profile_picture">Profilbild</string>
<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äts, um diese Datei zu speichern.</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="open_settings">Einstellungen öffnen</string>
<string name="error_saving_file">Fehler beim Speichern der Datei</string>
<string name="file_saved">Datei gespeichert</string>
@@ -302,7 +302,7 @@
<string name="trending_posts_info_banner">Dies sind Beiträge, die auf deinem Mastodon-Server gerade angesagt sind.</string>
<string name="trending_hashtags_info_banner">Diese Hashtags sind auf deinem Mastodon-Server gerade angesagt.</string>
<string name="trending_links_info_banner">Diese journalistischen Nachrichten werden auf deinem Mastodon-Server gerade am häufigsten geteilt.</string>
<string name="local_timeline_info_banner">Das sind die neuesten Beiträge von Personen, die denselben Mastodon-Server benutzen.</string>
<string name="local_timeline_info_banner">Das sind die neuesten Beiträge von Personen, die denselben Mastodon-Server wie du benutzen.</string>
<string name="dismiss">Verwerfen</string>
<string name="see_new_posts">Neue Beiträge anzeigen</string>
<string name="load_missing_posts">Weitere Beiträge laden</string>
@@ -334,7 +334,7 @@
<string name="post_info_reblogs">Geteilte Beiträge</string>
<string name="post_info_favorites">Favoriten</string>
<string name="edit_history">Verlauf bearbeiten</string>
<string name="last_edit_at_x">Letzte Bearbeitung: %s</string>
<string name="last_edit_at_x">Zuletzt bearbeitet: %s</string>
<string name="time_just_now">gerade jetzt</string>
<plurals name="x_seconds_ago">
<item quantity="one">vor %d Sekunde</item>
@@ -347,9 +347,9 @@
<string name="edited_timestamp">bearbeitet %s</string>
<string name="edit_original_post">Ursprünglicher Beitrag</string>
<string name="edit_text_edited">Text bearbeitet</string>
<string name="edit_spoiler_added">Inhaltwarnung hinzugefügt</string>
<string name="edit_spoiler_edited">Inhaltwarnung bearbeitet</string>
<string name="edit_spoiler_removed">Inhaltwarnung entfernt</string>
<string name="edit_spoiler_added">Inhaltswarnung hinzugefügt</string>
<string name="edit_spoiler_edited">Inhaltswarnung bearbeitet</string>
<string name="edit_spoiler_removed">Inhaltswarnung entfernt</string>
<string name="edit_poll_added">Umfrage hinzugefügt</string>
<string name="edit_poll_edited">Umfrage bearbeitet</string>
<string name="edit_poll_removed">Umfrage entfernt</string>
@@ -363,13 +363,13 @@
<string name="discard_changes">Änderungen verwerfen?</string>
<string name="upload_failed">Hochladen fehlgeschlagen</string>
<string name="file_size_bytes">%d Bytes</string>
<string name="file_size_kb">%.2f KB</string>
<string name="file_size_kb">%.2f kB</string>
<string name="file_size_mb">%.2f MB</string>
<string name="file_size_gb">%.2f GB</string>
<string name="file_upload_progress">%1$s von %2$s</string>
<string name="file_upload_time_remaining">%s verbleibend</string>
<string name="upload_error_connection_lost">Dein Gerät hat gerade keinen Zugang zum Internet</string>
<string name="upload_processing">wird verarbeitet </string>
<string name="upload_processing">Wird verarbeitet </string>
<!-- %s is version like 1.2.3 -->
<string name="update_available">Mastodon für Android %s ist zum Herunterladen bereit.</string>
<!-- %s is version like 1.2.3 -->
@@ -377,10 +377,17 @@
<!-- %s is file size -->
<string name="download_update">(%s) herunterladen</string>
<string name="install_update">Installieren</string>
<string name="privacy_policy_title">Mastodon und Ihre Privatsphäre</string>
<string name="privacy_policy_subtitle">Obwohl die Mastodon-App keine Daten sammelt, kann der Server, über den Sie sich anmelden, eine andere Richtlinie haben. Nehmen Sie sich eine Minute Zeit, um die Mastodon-Datenschutzrichtlinien und die Datenschutzrichtlinien Ihres Servers zu lesen und zu akzeptieren.</string>
<string name="privacy_policy_title">Mastodon und der Schutz deiner Daten</string>
<string name="privacy_policy_subtitle">Obwohl die Mastodon-App keinerlei Daten sammelt, könnte der Server, über den du dich registriert hast, eine abweichende Datenschutzerklärung haben. Nimm dir einen Moment Zeit, um die Datenschutzbestimmungen sowohl der App als auch deiner Mastodon-Instanz durchzulesen und sie zu akzeptieren.</string>
<string name="i_agree">Ich stimme zu</string>
<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 setzen</string>
<string name="remove_bookmark">Lesezeichen entfernen</string>
<string name="bookmarks">Lesezeichen</string>
<string name="your_favorites">Deine Favoriten</string>
<string name="login_title">Willkommen zurück</string>
<string name="login_subtitle">Melde dich mit dem Server an, auf dem du dein Konto erstellt hast.</string>
<string name="server_url">Serveradresse</string>
</resources>

View File

@@ -1,41 +1,62 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="sk_app_name">Megalodon</string>
<string name="sk_pinned_posts">Angeheftet</string>
<string name="sk_delete_and_redraft">Löschen und neu erstellen</string>
<string name="sk_confirm_delete_and_redraft_title">Beitrag löschen und neu erstellen</string>
<string name="sk_confirm_delete_and_redraft">Bist du dir sicher, dass du diesen Beitrag löschen und neu erstellen möchtest?</string>
<string name="sk_pin_post">An Profil anheften</string>
<string name="sk_confirm_pin_post_title">Beitrag an Profil anheften</string>
<string name="sk_confirm_pin_post">Möchtest du den Beitrag an dein Profil anheften?</string>
<string name="sk_pinning">Wird angeheftet…</string>
<string name="sk_unpin_post">Von Profil lösen</string>
<string name="sk_confirm_unpin_post_title">Angehefteten Beitrag von Profil lösen</string>
<string name="sk_confirm_unpin_post">Bist du dir sicher, dass du den angehefteten Beitrag von deinem Profil lösen möchtest?</string>
<string name="sk_unpinning">Wird vom Profil gelöst…</string>
<string name="sk_image_description">Bildbeschreibung</string>
<string name="sk_visibility_unlisted">Nicht gelistet</string>
<string name="sk_settings_show_replies">Antworten anzeigen</string>
<string name="sk_settings_show_boosts">Geteilte Beiträge anzeigen</string>
<string name="sk_settings_load_new_posts">Automatisch neue Beiträge laden</string>
<string name="sk_settings_show_interaction_counts">Interaktions-Anzahlen anzeigen</string>
<string name="sk_settings_app_version">Megalodon v%1$s (%2$d)</string>
<string name="sk_mark_media_as_sensitive">Medien als sensibel markieren</string>
<string name="sk_user_post_notifications_on">Benachrichtigungen über Beiträge von %s aktiviert</string>
<string name="sk_user_post_notifications_off">Benachrichtigungen über Beiträge von %s deaktiviert</string>
<string name="sk_federated_timeline">Föderation</string>
<string name="sk_federated_timeline_info_banner">Das sind die neuesten Beiträge von Personen, die in der Föderation deines Servers sind.</string>
<string name="sk_update_available">Megalodon %s ist zum Herunterladen bereit.</string>
<string name="sk_update_ready">Megalodon %s wurde heruntergeladen und kann jetzt installiert werden.</string>
<string name="sk_check_for_update">Auf Update prüfen</string>
<string name="sk_no_update_available">Kein Update verfügbar</string>
<string name="sk_list_timelines">Listen</string>
<string name="sk_follow_requests">Folgeanfragen</string>
<string name="sk_accept_follow_request">Folgeanfrage akzeptieren</string>
<string name="sk_reject_follow_request">Folgeanfrage ablehnen</string>
<string name="sk_lists_with_user">Listen mit %s</string>
<string name="sk_settings_always_reveal_content_warnings">Inhaltswarnungen immer ausklappen</string>
<string name="sk_disable_marquee">Laufschrift in Titelleisten deaktivieren</string>
<string name="sk_settings_contribute">Zu Megalodon beitragen</string>
<string name="sk_settings_show_federated_timeline">Föderierte Timeline anzeigen</string>
</resources>
<string name="sk_app_name">Megalodon</string>
<string name="sk_pinned_posts">Angeheftet</string>
<string name="sk_delete_and_redraft">Löschen und neu erstellen</string>
<string name="sk_confirm_delete_and_redraft_title">Beitrag löschen und neu erstellen</string>
<string name="sk_confirm_delete_and_redraft">Bist du dir sicher, dass du diesen Beitrag löschen und neu erstellen möchtest?</string>
<string name="sk_pin_post">An Profil anheften</string>
<string name="sk_confirm_pin_post_title">Beitrag an Profil anheften</string>
<string name="sk_confirm_pin_post">Möchtest du den Beitrag an dein Profil anheften?</string>
<string name="sk_pinning">Wird angeheftet…</string>
<string name="sk_unpin_post">Von Profil lösen</string>
<string name="sk_confirm_unpin_post_title">Angehefteten Beitrag von Profil lösen</string>
<string name="sk_confirm_unpin_post">Bist du dir sicher, dass du den angehefteten Beitrag von deinem Profil lösen möchtest?</string>
<string name="sk_unpinning">Wird vom Profil gelöst…</string>
<string name="sk_image_description">Bildbeschreibung</string>
<string name="sk_visibility_unlisted">Nicht gelistet</string>
<string name="sk_settings_show_replies">Antworten anzeigen</string>
<string name="sk_settings_show_boosts">Geteilte Beiträge anzeigen</string>
<string name="sk_settings_load_new_posts">Automatisch neue Beiträge laden</string>
<string name="sk_settings_show_interaction_counts">Interaktions-Anzahlen anzeigen</string>
<string name="sk_settings_app_version">Megalodon v%1$s (%2$d)</string>
<string name="sk_mark_media_as_sensitive">Medien als sensibel markieren</string>
<string name="sk_user_post_notifications_on">Benachrichtigungen über Beiträge von %s aktiviert</string>
<string name="sk_user_post_notifications_off">Benachrichtigungen über Beiträge von %s deaktiviert</string>
<string name="sk_federated_timeline">Föderation</string>
<string name="sk_federated_timeline_info_banner">Das sind die neuesten Beiträge von Personen, die in der Föderation deines Servers sind.</string>
<string name="sk_update_available">Megalodon %s ist zum Herunterladen bereit.</string>
<string name="sk_update_ready">Megalodon %s wurde heruntergeladen und kann jetzt installiert werden.</string>
<string name="sk_check_for_update">Auf Update prüfen</string>
<string name="sk_no_update_available">Kein Update verfügbar</string>
<string name="sk_list_timelines">Listen</string>
<string name="sk_follow_requests">Folgeanfragen</string>
<string name="sk_accept_follow_request">Folgeanfrage akzeptieren</string>
<string name="sk_reject_follow_request">Folgeanfrage ablehnen</string>
<string name="sk_lists_with_user">Listen mit %s</string>
<string name="sk_settings_always_reveal_content_warnings">Inhaltswarnungen immer ausklappen</string>
<string name="sk_disable_marquee">Laufschrift in Titelleisten deaktivieren</string>
<string name="sk_settings_contribute">Zu Megalodon beitragen</string>
<string name="sk_settings_show_federated_timeline">Föderierte Timeline anzeigen</string>
<string name="sk_notify_posts">Beitrags-Benachrichtigungen</string>
<string name="sk_settings_color_picker">Farbschema</string>
<string name="sk_color_theme_pink">Pink</string>
<string name="sk_color_theme_purple">Violett</string>
<string name="sk_color_theme_green">Grün</string>
<string name="sk_color_theme_brown">Braun</string>
<string name="sk_color_theme_yellow">Gelb</string>
<string name="sk_notification_type_status">Beiträge</string>
<string name="sk_color_theme_blue">Blau</string>
<string name="sk_poll_allow_multiple">Mehrfachantworten erlauben</string>
<string name="sk_translated_using">Übersetzt mit %s</string>
<string name="sk_post_language">Sprache: %s</string>
<string name="sk_language_name">%s (%s)</string>
<string name="sk_confirm_clear_recent_languages">Sicher, dass du die Liste der zuletzt verwendeten Sprachen leeren willst\?</string>
<string name="sk_translate_post">Übersetzen</string>
<string name="sk_translate_show_original">Original anzeigen</string>
<string name="sk_available_languages">Verfügbare Sprachen</string>
<string name="sk_clear_recent_languages">Zuletzt verwendete Sprachen leeren</string>
<string name="sk_welcome_title">Willkommen!</string>
<string name="sk_example_domain">beispiel.social</string>
<string name="sk_welcome_text">Der Hai sagt Hi! Um anzufangen, bitte gib den Domain-Namen deiner Heim-Instanz unten ein.</string>
</resources>

View File

@@ -3,9 +3,7 @@
<string name="get_started">Empezar</string>
<string name="log_in">Iniciar sesión</string>
<string name="next">Siguiente</string>
<string name="loading_instance">Obteniendo información de la instancia…</string>
<string name="error">Error</string>
<string name="not_a_mastodon_instance">%s no parece ser una instancia de Mastodon.</string>
<string name="ok">Aceptar</string>
<string name="preparing_auth">Preparando para autenticación…</string>
<string name="finishing_auth">Terminando autenticación…</string>

View File

@@ -2,28 +2,28 @@
<resources>
<string name="sk_pinned_posts">Anclado</string>
<string name="sk_delete_and_redraft">Eliminar y editar</string>
<string name="sk_confirm_delete_and_redraft_title">Eliminar y editar post</string>
<string name="sk_confirm_delete_and_redraft">Seguro que quiere eliminar y volver a editar este post\?</string>
<string name="sk_pin_post">Fijar en perfil</string>
<string name="sk_confirm_pin_post_title">Fijar post en perfil</string>
<string name="sk_confirm_pin_post">Desea fijar el post en su perfil\?</string>
<string name="sk_pinning">Fijando post</string>
<string name="sk_unpin_post">Quitar del perfil</string>
<string name="sk_confirm_unpin_post_title">Quitar post del perfil</string>
<string name="sk_confirm_unpin_post">Está seguro que quiere quitar el post\?</string>
<string name="sk_confirm_delete_and_redraft_title">Eliminar y editar publicación</string>
<string name="sk_confirm_delete_and_redraft">¿Confirma que quiere eliminar y volver a editar esta publicación\?</string>
<string name="sk_pin_post">Anclar en perfil</string>
<string name="sk_confirm_pin_post_title">Anclar publicación en perfil</string>
<string name="sk_confirm_pin_post">¿Quiere anclar la publicación en su perfil\?</string>
<string name="sk_pinning">Anclando publicación</string>
<string name="sk_unpin_post">Desanclar del perfil</string>
<string name="sk_confirm_unpin_post_title">Desanclar publicación del perfil</string>
<string name="sk_confirm_unpin_post">¿Confirma que quiere desanclar esta publicación\?</string>
<string name="sk_app_name">Megalodon</string>
<string name="sk_unpinning">Quitando post</string>
<string name="sk_unpinning">Desanclando publicación</string>
<string name="sk_image_description">Descripción de la imagen</string>
<string name="sk_visibility_unlisted">Sin listar</string>
<string name="sk_visibility_unlisted">Descatalogada</string>
<string name="sk_settings_show_replies">Mostrar respuestas</string>
<string name="sk_settings_show_boosts">Mostrar boosts</string>
<string name="sk_settings_load_new_posts">Cargar nuevos posts automáticamente</string>
<string name="sk_settings_show_interaction_counts">Mostrar contadores de interacciones</string>
<string name="sk_mark_media_as_sensitive">Marcar medio como sensible</string>
<string name="sk_settings_show_boosts">Mostrar impulsos</string>
<string name="sk_settings_load_new_posts">Cargar publicaciones nuevas automáticamente</string>
<string name="sk_settings_show_interaction_counts">Mostrar recuentos de interacciones</string>
<string name="sk_mark_media_as_sensitive">Marcar medio como delicado</string>
<string name="sk_user_post_notifications_on">Activadas las notificaciones de posts para %s</string>
<string name="sk_user_post_notifications_off">Desactivadas las notificaciones de posts para %s</string>
<string name="sk_federated_timeline">Federación</string>
<string name="sk_federated_timeline_info_banner">Estos son los posts más recientes de las personas de tu federación.</string>
<string name="sk_federated_timeline_info_banner">Estas son las publicaciones más recientes de las personas de su federación.</string>
<string name="sk_update_available">Megalodon %s está listo para descargar.</string>
<string name="sk_update_ready">Megalodon %s se ha descargado y está listo para instalarse.</string>
<string name="sk_check_for_update">Buscar actualizaciones</string>
@@ -36,6 +36,24 @@
<string name="sk_settings_always_reveal_content_warnings">Mostrar siempre advertencias de contenido</string>
<string name="sk_disable_marquee">Desactivar desplazamiento de texto en barras del título</string>
<string name="sk_settings_contribute">Contribuir a Megalodon</string>
<string name="sk_settings_show_federated_timeline">Mostrar el timeline federado</string>
<string name="sk_settings_show_federated_timeline">Mostrar cronología federada</string>
<string name="sk_settings_app_version">Megalodon v%1$s (%2$d)</string>
<string name="sk_notification_type_status">Publicaciones</string>
<string name="sk_notify_posts">Publicar notificaciones</string>
<string name="sk_settings_color_picker">Colores para los temas</string>
<string name="sk_color_theme_pink">Rosa</string>
<string name="sk_color_theme_purple">Violeta</string>
<string name="sk_color_theme_green">Verde</string>
<string name="sk_color_theme_blue">Azul</string>
<string name="sk_color_theme_brown">Marrón</string>
<string name="sk_color_theme_yellow">Amarillo</string>
<string name="sk_poll_allow_multiple">Permitir respuesta múltiple</string>
<string name="sk_translate_post">Traducir</string>
<string name="sk_translate_show_original">Mostrar original</string>
<string name="sk_translated_using">Traducido mediante %s</string>
<string name="sk_post_language">Idioma: %s</string>
<string name="sk_available_languages">Idiomas disponibles</string>
<string name="sk_language_name">%s (%s)</string>
<string name="sk_confirm_clear_recent_languages">¿Confirma que quiere vaciar sus idiomas usados recientemente\?</string>
<string name="sk_clear_recent_languages">Vaciar idiomas usados recientemente</string>
</resources>

View File

@@ -3,13 +3,11 @@
<string name="get_started">Nola hasi</string>
<string name="log_in">Hasi saioa</string>
<string name="next">Hurrengoa</string>
<string name="loading_instance">Instantziaren informazioa eskuratzen…</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 zure bidalketa bultzatu du</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="user_followed_you">jarraitu zaitu</string>
@@ -96,6 +94,10 @@
<item quantity="one">%d egun falta da</item>
<item quantity="other">%d egun falta dira</item>
</plurals>
<plurals name="x_voters">
<item quantity="one">Boto-emaile %,d</item>
<item quantity="other">%,d boto-emaile</item>
</plurals>
<string name="poll_closed">Itxita</string>
<string name="confirm_mute_title">Mututu kontua</string>
<string name="confirm_mute">Berretsi %s mututzea</string>
@@ -134,6 +136,10 @@
<item quantity="one">Pertsona %d hizketan</item>
<item quantity="other">%d pertsona hizketan</item>
</plurals>
<plurals name="discussed_x_times">
<item quantity="one">%d aldiz eztabaidatua</item>
<item quantity="other">%d aldiz eztabaidatua</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>
@@ -166,6 +172,8 @@
<string name="instance_catalog_subtitle">Aukeratu zerbitzari bat zure interesen, eskualdearen edo helburuen arabera. Pertsona guztiekin konektatu ahal izango duzu, zerbitzaria zein den kontuan hartu gabe.</string>
<string name="search_communities">Bilatu zerbitzariak edo idatzi URL-a</string>
<string name="instance_rules_title">Oinarrizko arau batzuk</string>
<string name="instance_rules_subtitle">Hartu minutu bat %s-en administratzaileek ezarri eta aplikatutako arauak berrikusteko.</string>
<string name="signup_title">%s zerbitzariko kontua prestatuko dizugu</string>
<string name="edit_photo">editatu</string>
<string name="display_name">pantaila-izena</string>
<string name="username">erabiltzaile-izena</string>
@@ -214,6 +222,12 @@
<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 bisuala</string>
<string name="theme_auto">Automatikoa</string>
<string name="theme_light">Argia</string>
@@ -297,6 +311,22 @@
<string name="current_account">Oraingo kontua</string>
<string name="log_out_account">Itxi saioa %s</string>
<!-- translators: %,d is a valid placeholder, it formats the number with locale-dependent grouping separators -->
<plurals name="x_followers">
<item quantity="one">Jarraitzaile %,d</item>
<item quantity="other">%,d jarraitzaile</item>
</plurals>
<plurals name="x_following">
<item quantity="one">%,d jarraitzen</item>
<item quantity="other">%,d jarraitzen</item>
</plurals>
<plurals name="x_favorites">
<item quantity="one">Gogoko bat</item>
<item quantity="other">%,d gogoko</item>
</plurals>
<plurals name="x_reblogs">
<item quantity="one">Bultzada %,d</item>
<item quantity="other">%,d bultzada</item>
</plurals>
<string name="timestamp_via_app">%1$s %2$s -en bidez</string>
<string name="time_now">orain</string>
<string name="post_info_reblogs">Bultzadak</string>
@@ -304,6 +334,14 @@
<string name="edit_history">Editatu historia</string>
<string name="last_edit_at_x">Azken edizioa %s</string>
<string name="time_just_now">oraintxe</string>
<plurals name="x_seconds_ago">
<item quantity="one">Duela segundo %d</item>
<item quantity="other">Duela %d segundo</item>
</plurals>
<plurals name="x_minutes_ago">
<item quantity="one">Duela minutu %d</item>
<item quantity="other">Duela %d minutu</item>
</plurals>
<string name="edited_timestamp">editatua %s</string>
<string name="edit_original_post">Jatorrizko bidalketa</string>
<string name="edit_text_edited">Editatutako testua</string>
@@ -326,12 +364,14 @@
<string name="file_size_kb">%.2f KB</string>
<string name="file_size_mb">%.2f MB</string>
<string name="file_size_gb">%.2f GB</string>
<string name="file_upload_progress">%2$s-tik %1$s</string>
<string name="file_upload_time_remaining">%s geratzen da</string>
<string name="upload_error_connection_lost">Zure gailuak interneterako konexioa galdu du</string>
<string name="upload_processing">Prozesatzen…</string>
<!-- %s is version like 1.2.3 -->
<string name="update_available">Androiderako Mastodon %s prest dago jeisteko.</string>
<!-- %s is version like 1.2.3 -->
<string name="update_ready">%s Androiderako Mastodon deskargatu da eta instalatzeko prest dago.</string>
<!-- %s is file size -->
<string name="download_update">(%s) deskargatu</string>
<string name="install_update">Instalatu</string>
@@ -340,4 +380,9 @@
<string name="i_agree">Ados nago</string>
<string name="empty_list">Zerrenda hau hutsik dago</string>
<string name="instance_signup_closed">Zerbitzari honek ez ditu izen-emate berriak onartzen.</string>
<string name="text_copied">Arbelean kopiatuta</string>
<string name="add_bookmark">Laster-marka</string>
<string name="remove_bookmark">Kendu laster-marka</string>
<string name="bookmarks">Laster-markak</string>
<string name="your_favorites">Zure gogokoak</string>
</resources>

View File

@@ -0,0 +1,165 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="get_started">Magsimula</string>
<string name="log_in">Mag-log in</string>
<string name="next">Sunod</string>
<string name="error">Nag-Error</string>
<string name="ok">OK</string>
<string name="preparing_auth">Paghahanda para sa pagpapatunay…</string>
<string name="finishing_auth">Natapos na ang pagpapatunay…</string>
<string name="user_boosted">%s reblogged</string>
<string name="in_reply_to">Sa sumagot sa %s</string>
<string name="notifications">Mga abiso</string>
<string name="user_followed_you">sinundan ka</string>
<string name="user_sent_follow_request">may nagpadala sa iyo ng follow request</string>
<string name="user_favorited">paborito ang iyong post</string>
<string name="notification_boosted">na reblogged ang iyong post</string>
<string name="poll_ended">natapos na ang poll</string>
<string name="time_seconds">%ds</string>
<string name="time_minutes">%dm</string>
<string name="time_hours">%dh</string>
<string name="time_days">%dd</string>
<string name="share_toot_title">Ibahagi</string>
<string name="settings">Mga setting</string>
<string name="publish">I-publish</string>
<string name="discard_draft">Ipagliban ang draft?</string>
<string name="discard">Ipagliban</string>
<string name="cancel">Kanselahin</string>
<plurals name="followers">
<item quantity="one">tagasunod</item>
<item quantity="other">mga tagasunod</item>
</plurals>
<plurals name="following">
<item quantity="one">sumusunod</item>
<item quantity="other">sumusunod</item>
</plurals>
<plurals name="posts">
<item quantity="one">post</item>
<item quantity="other">mga post</item>
</plurals>
<string name="posts">Mga Post</string>
<string name="posts_and_replies">Mga post at Mga tugon</string>
<string name="media">Medya</string>
<string name="profile_about">Tungkol</string>
<string name="button_follow">Sundan</string>
<string name="button_following">Sumusunod</string>
<string name="edit_profile">I-edit ang profile</string>
<string name="mention_user">Binanggit %s</string>
<string name="share_user">Ibahagi %s</string>
<string name="mute_user">I-mute %s</string>
<string name="unmute_user">I-unmute %s</string>
<string name="block_user">Na-Block %s</string>
<string name="unblock_user">Na-Unblock %s</string>
<string name="report_user">Na-report %s</string>
<string name="block_domain">Na-Block %s</string>
<string name="unblock_domain">Na-Unblock %s</string>
<plurals name="x_posts">
<item quantity="one">%,d post</item>
<item quantity="other">%,d mga post</item>
</plurals>
<string name="profile_joined">Sumali</string>
<string name="done">Tapos na</string>
<string name="loading">Naglo-load…</string>
<string name="field_label">Label</string>
<string name="field_content">Nilalaman</string>
<string name="saving">Pag-save…</string>
<string name="post_from_user">Post galing %s</string>
<string name="poll_option_hint">Pagpipilian %d</string>
<plurals name="x_minutes">
<item quantity="one">%d minuto</item>
<item quantity="other">%d minuto</item>
</plurals>
<plurals name="x_hours">
<item quantity="one">%d oras</item>
<item quantity="other">%d oras</item>
</plurals>
<plurals name="x_days">
<item quantity="one">%d araw</item>
<item quantity="other">%d araw</item>
</plurals>
<string name="compose_poll_duration">Tagal: %s</string>
<plurals name="x_seconds_left">
<item quantity="one">%d segundong natitira</item>
<item quantity="other">%d segundong natitira</item>
</plurals>
<plurals name="x_minutes_left">
<item quantity="one">%d minutong natitira</item>
<item quantity="other">%d minutong natitira</item>
</plurals>
<plurals name="x_hours_left">
<item quantity="one">%d natitirang oras</item>
<item quantity="other">%d natitirang oras</item>
</plurals>
<plurals name="x_days_left">
<item quantity="one">%d natitirang araw</item>
<item quantity="other">%d natitirang araw</item>
</plurals>
<plurals name="x_voters">
<item quantity="one">%, d botante</item>
<item quantity="other">%,d botante</item>
</plurals>
<string name="poll_closed">Sarado</string>
<string name="confirm_mute_title">I-Mute Ang Account</string>
<string name="confirm_mute">Kumpirmahin ang pag-mute %s</string>
<string name="do_mute">I-Mute</string>
<string name="confirm_unmute_title">I-Unmute Ang Account</string>
<string name="confirm_unmute">Kumpirmahin ang pag-unmute %s</string>
<string name="do_unmute">I-unmute</string>
<string name="confirm_block_title">I-Block Ang Account</string>
<string name="confirm_block_domain_title">I-Block Ang Domain</string>
<string name="confirm_block">Kumpirmahin ang pag-block %s</string>
<string name="do_block">Block</string>
<string name="confirm_unblock_title">I-Unblock ang Account</string>
<string name="confirm_unblock_domain_title">I-Unblock ang Domain</string>
<string name="confirm_unblock">Kumpirmahin ang pag-unblock%s</string>
<string name="do_unblock">Unblock</string>
<string name="button_muted">Naka-mute</string>
<string name="button_blocked">Na-block</string>
<string name="action_vote">Bumoto</string>
<string name="tap_to_reveal">I-Tap para ipakita</string>
<string name="delete">Tanggalin</string>
<string name="confirm_delete_title">Burahin ang Post</string>
<string name="confirm_delete">Sigurado ka bang gusto mong burahin ang post na ito?</string>
<string name="notification_channel_audio_player">Pag-playback ng Audio</string>
<string name="play">I-play</string>
<string name="log_out">Mag-Sign out</string>
<string name="add_account">Magdagdag ng account</string>
<string name="search_hint">Maghanap</string>
<string name="hashtags">Mga hashtag</string>
<string name="news">Balita</string>
<string name="for_you">Para sa\'yo</string>
<string name="all_notifications">Lahat</string>
<string name="mentions">Mga binangit</string>
<plurals name="x_people_talking">
<item quantity="one">%d tao ay nagsasalita</item>
<item quantity="other">%d tao ay nagsasalita</item>
</plurals>
<plurals name="discussed_x_times">
<item quantity="one">Tinalakay %d oras</item>
<item quantity="other">Tinalakay %d oras</item>
</plurals>
<string name="report_title">Na-report %s</string>
<string name="report_choose_reason">Ano ang mali sa post na ito?</string>
<string name="report_choose_reason_account">Ano ang mali sa %s?</string>
<string name="report_choose_reason_subtitle">Piliin ang pinakamahusay na tugma</string>
<string name="report_reason_personal">Hindi ko gusto ito</string>
<string name="report_reason_personal_subtitle">Hindi ito isang bagay na nais mong makita</string>
<string name="report_reason_spam">Ito ay spam</string>
<string name="report_reason_spam_subtitle">Nakakahamak na mga link, pekeng pakikipag-ugnayan, o paulit-ulit na mga tugon</string>
<string name="report_reason_violation">Lumalabag ito sa mga patakaran ng server</string>
<string name="report_reason_violation_subtitle">Alam mo na nilalabag nito ang mga tiyak na patakaran</string>
<string name="report_reason_other">Ito may iba pa</string>
<string name="report_reason_other_subtitle">Ang isyu ay hindi umaangkop sa iba pang mga kategorya</string>
<string name="report_choose_rule">Aling mga patakaran ang nilabag?</string>
<string name="report_choose_rule_subtitle">Piliin ang lahat na-iapply</string>
<string name="report_choose_posts">Mayroon bang anumang mga post na nai-back up ang ulat na ito?</string>
<string name="report_choose_posts_subtitle">Piliin ang lahat na aaply</string>
<string name="report_comment_title">Mayroon pa bang dapat nating malaman?</string>
<string name="report_comment_hint">Mga Karagdagang Komento</string>
<string name="report_sent_title">Salamat sa pag-uulat, titingnan namin ito.</string>
<string name="report_sent_subtitle">Habang sinusuri namin ito, maaari kang gumawa ng aksyon laban sa %s.</string>
<!-- translators: %,d is a valid placeholder, it formats the number with locale-dependent grouping separators -->
<!-- %s is version like 1.2.3 -->
<!-- %s is version like 1.2.3 -->
<!-- %s is file size -->
</resources>

View File

@@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
</resources>

View File

@@ -3,9 +3,9 @@
<string name="get_started">Premiers pas</string>
<string name="log_in">Se connecter</string>
<string name="next">Suivant</string>
<string name="loading_instance">Chargement des informations de linstance</string>
<string name="loading_instance">Récupération des informations serveur</string>
<string name="error">Erreur</string>
<string name="not_a_mastodon_instance">%s ne semble pas être une instance Mastodon.</string>
<string name="not_a_mastodon_instance">%s ne semble pas être un serveur Mastodon.</string>
<string name="ok">OK</string>
<string name="preparing_auth">Préparation à lauthentification…</string>
<string name="finishing_auth">Fin de lauthentification…</string>
@@ -387,4 +387,7 @@
<string name="remove_bookmark">Retirer des favoris</string>
<string name="bookmarks">Favoris</string>
<string name="your_favorites">Vos favoris</string>
<string name="login_title">Content de vous revoir</string>
<string name="login_subtitle">Connectez-vous avec le serveur sur lequel vous avez créé votre compte.</string>
<string name="server_url">URL du serveur</string>
</resources>

View File

@@ -1,24 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="sk_pinned_posts">Épinglé</string>
<string name="sk_confirm_delete_and_redraft_title">Supprimer et reformuler le message</string>
<string name="sk_confirm_delete_and_redraft">Voulez-vous vraiment supprimer et reformuler ce message \?</string>
<string name="sk_confirm_delete_and_redraft_title">Supprimer et rééditer le message</string>
<string name="sk_confirm_delete_and_redraft">Voulez-vous vraiment supprimer et rééditer ce message \?</string>
<string name="sk_confirm_pin_post">Voulez-vous épingler ce message à votre profil \?</string>
<string name="sk_pinning">Épinglage de la publication</string>
<string name="sk_pinning">Épinglage du message</string>
<string name="sk_unpin_post">Détacher du profil</string>
<string name="sk_confirm_unpin_post_title">Détacher la publication du profil</string>
<string name="sk_unpinning">Détachement de la publication</string>
<string name="sk_confirm_unpin_post_title">Détacher le message du profil</string>
<string name="sk_unpinning">Détachement du message</string>
<string name="sk_image_description">Description de l\'image</string>
<string name="sk_visibility_unlisted">Non répertorié</string>
<string name="sk_settings_show_replies">Afficher les réponses</string>
<string name="sk_settings_show_boosts">Afficher les boosts</string>
<string name="sk_settings_load_new_posts">Charger automatiquement les nouvelles publications</string>
<string name="sk_settings_load_new_posts">Charger automatiquement les nouveaux messages</string>
<string name="sk_settings_app_version">Megalodon v%1$s (%2$d)</string>
<string name="sk_mark_media_as_sensitive">Marquer le média comme sensible</string>
<string name="sk_user_post_notifications_on">Notifications de publication activées pour %s</string>
<string name="sk_user_post_notifications_off">Désactivation des notifications de publication pour %s</string>
<string name="sk_update_available">Megalodon %s est prêt à être téléchargé.</string>
<string name="sk_update_ready">Megalodon %s est téléchargé et prêt à être installé.</string>
<string name="sk_user_post_notifications_on">Notifications de message activées pour %s</string>
<string name="sk_user_post_notifications_off">Désactivation des notifications de message pour %s</string>
<string name="sk_update_available">Megalodon %s est prête à être téléchargée.</string>
<string name="sk_update_ready">Megalodon %s est téléchargée et prête à être installée.</string>
<string name="sk_check_for_update">Vérifier les mises à jour</string>
<string name="sk_no_update_available">Pas de mise a jour disponible</string>
<string name="sk_list_timelines">Listes</string>
@@ -31,11 +31,29 @@
<string name="sk_settings_contribute">Contribuez à Megalodon</string>
<string name="sk_settings_show_federated_timeline">Afficher la timeline fédérée</string>
<string name="sk_app_name">Megalodon</string>
<string name="sk_delete_and_redraft">Supprimer et reformuler</string>
<string name="sk_delete_and_redraft">Supprimer et rééditer</string>
<string name="sk_pin_post">Épingler au profil</string>
<string name="sk_confirm_pin_post_title">Épingler la publication au profil</string>
<string name="sk_confirm_unpin_post">Êtes-vous sûr de vouloir détacher cette publication \?</string>
<string name="sk_confirm_pin_post_title">Épingler le message au profil</string>
<string name="sk_confirm_unpin_post">Êtes-vous sûr de vouloir détacher ce message \?</string>
<string name="sk_settings_show_interaction_counts">Afficher le nombre d\'interactions</string>
<string name="sk_federated_timeline">Fédération</string>
<string name="sk_federated_timeline_info_banner">Ce sont les publications les plus récentes des membres de votre fédération.</string>
<string name="sk_federated_timeline_info_banner">Ce sont les messages les plus récents des membres de votre fédération.</string>
<string name="sk_notification_type_status">Messages</string>
<string name="sk_notify_posts">Notifications des messages</string>
<string name="sk_color_theme_pink">Rose</string>
<string name="sk_color_theme_purple">Violet</string>
<string name="sk_color_theme_green">Vert</string>
<string name="sk_color_theme_blue">Bleu</string>
<string name="sk_color_theme_brown">Marron</string>
<string name="sk_color_theme_yellow">Jaune</string>
<string name="sk_settings_color_picker">Couleur d\'accentuation</string>
<string name="sk_poll_allow_multiple">Autoriser plusieurs choix</string>
<string name="sk_translate_post">Traduire</string>
<string name="sk_translate_show_original">Afficher l\'original</string>
<string name="sk_translated_using">Traduit en utilisant %s</string>
<string name="sk_post_language">Langue : %s</string>
<string name="sk_available_languages">Langues disponibles</string>
<string name="sk_language_name">%s (%s)</string>
<string name="sk_confirm_clear_recent_languages">Êtes-vous sûr de vouloir effacer vos langues récemment utilisées \?</string>
<string name="sk_clear_recent_languages">Effacer les langues récemment utilisées</string>
</resources>

View File

@@ -3,9 +3,7 @@
<string name="get_started">Dèan toiseach-tòiseachaidh</string>
<string name="log_in">Clàraich a-steach</string>
<string name="next">Air adhart</string>
<string name="loading_instance">A faighinn fiosrachadh an ionstans…</string>
<string name="error">Mearachd</string>
<string name="not_a_mastodon_instance">Chan eil coltas ionstans Mhastodon air %s.</string>
<string name="ok">Ceart ma-thà</string>
<string name="preparing_auth">Ag ullachadh an dearbhaidh…</string>
<string name="finishing_auth">A crìochnachadh an dearbhaidh…</string>

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