Compare commits

..

286 Commits

Author SHA1 Message Date
LucasGGamerM
f69ae5e816 refactor(PreviewlessMediaItems): make FileStatusDisplayItem be used only for files 2023-09-17 08:41:32 -03:00
LucasGGamerM
91c7fc64bd refactor(PreviewlessMediaItems): remove unused class 2023-09-17 08:37:07 -03:00
LucasGGamerM
91211b7720 refactor: do some final adjustments to PreviewlessMediaGridStatusDisplayItem layout 2023-09-17 08:34:29 -03:00
LucasGGamerM
51ef732d50 fix: fix crashes on PreviewlessMediaGridStatusDisplayItem 2023-09-16 16:22:00 -03:00
LucasGGamerM
31a7aa9d40 refactor: start of refactor of previewless media status display item
Its crashing a lot, and has a lot to be done
2023-09-16 16:14:48 -03:00
LucasGGamerM
ed6aa0725e Revert "fix: use better colors for true black mode"
This reverts commit 69272e891e.
2023-09-13 08:26:02 -03:00
LucasGGamerM
6fd9047a8e fix: use consistent coloring on boost icon 2023-09-13 07:57:19 -03:00
LucasGGamerM
48d7635a56 fix: unscrew the brown theme 2023-09-13 07:54:34 -03:00
LucasGGamerM
9515edea4b feat: make images scrollable in hidden preview mode 2023-09-13 07:43:00 -03:00
LucasGGamerM
652c5e1d16 fix: unscrew the black & white theme 2023-09-12 15:14:08 -03:00
LucasGGamerM
e1d9cc4add fix: unscrew the NORD theme 2023-09-12 15:11:33 -03:00
LucasGGamerM
d51cd2d497 fix: finally get the red theme right
THIS WAS PAINFUL. Please go fund me at github.com/sponsors/LucasGGamerM
2023-09-12 15:03:13 -03:00
LucasGGamerM
39d1b69014 fix: finally get the yellow theme right 2023-09-12 13:04:02 -03:00
LucasGGamerM
74a9e49190 fix: finally get the brown theme right 2023-09-12 11:27:43 -03:00
LucasGGamerM
b0763473c7 feat: rework colors blue and green. Brown WIP 2023-09-12 11:09:46 -03:00
LucasGGamerM
4e5bbe6f9b refactor(themes): start theme refactoring 2023-09-12 08:17:23 -03:00
LucasGGamerM
1ba30a4d28 Merge remote-tracking branch 'megalodon_main/main'
# Conflicts:
#	mastodon/build.gradle
#	mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/FooterStatusDisplayItem.java
#	mastodon/src/main/res/values-ar/strings_sk.xml
#	mastodon/src/main/res/values-fr-rFR/strings_sk.xml
#	mastodon/src/main/res/values-ja-rJP/strings_sk.xml
#	metadata/ar/short_description.txt
2023-09-11 16:47:24 -03:00
sk
380e4ff77e remove unused member 2023-09-09 01:48:49 +02:00
sk
58f0c07357 determine next display item using items list
closes sk22#815
2023-09-09 01:27:32 +02:00
sk
77dee59b9c fix string and tweak banner margin
closes sk22#812
2023-09-09 01:24:36 +02:00
sk
646f83ff0a boop verison 2023-09-08 22:19:41 +02:00
sk
fdf0414698 fix missing banner icons and wrong strings
closes sk22#805
2023-09-08 22:05:06 +02:00
sk
cc699a3f5e fix spacing stuff in report fragment 2023-09-08 21:58:08 +02:00
LucasGGamerM
12eaa8d5f1 fix: fix window insets on ScheduledStatusListFragment
cc: @sk22
2023-09-08 21:49:29 +02:00
sk22
70680e39c6 Translated using Weblate (German)
Currently translated at 100.0% (384 of 384 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/de/
2023-09-08 19:46:45 +00:00
sk
8bd8f90d58 remove unused strings 2023-09-08 21:44:31 +02:00
edxkl
54b53a266e Translated using Weblate (Portuguese (Brazil))
Currently translated at 93.2% (362 of 388 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/pt_BR/
2023-09-08 19:40:59 +00:00
sk22
66921e3b5a Translated using Weblate (German)
Currently translated at 97.1% (377 of 388 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/de/
2023-09-08 19:40:59 +00:00
sk22
9d7af3964b Translated using Weblate (English)
Currently translated at 100.0% (388 of 388 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/en/
2023-09-08 19:40:59 +00:00
ihor_ck
ec73687e9b Translated using Weblate (Ukrainian)
Currently translated at 100.0% (382 of 382 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/uk/
2023-09-08 19:17:03 +00:00
Choukajohn
c8af800b88 Translated using Weblate (French)
Currently translated at 100.0% (382 of 382 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/fr/
2023-09-08 19:17:02 +00:00
gallegonovato
e74ac5da56 Translated using Weblate (Spanish)
Currently translated at 99.4% (380 of 382 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/es/
2023-09-08 19:17:02 +00:00
sk
efa003a9a5 implement bidirectional missing posts gap 2023-09-08 21:16:42 +02:00
butterflyoffire
de5165434d Translated using Weblate (Arabic)
Currently translated at 16.6% (3 of 18 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/ar/
2023-09-08 15:43:13 +00:00
sk
be648cc5ab Merge remote-tracking branch 'upstream/l10n_master' 2023-09-08 17:42:47 +02:00
sk
90f1f464dc reset strings.xml to upstream 2023-09-08 17:41:30 +02:00
sk
734aa52816 Merge remote-tracking branch 'weblate/main' 2023-09-08 17:37:31 +02:00
sk
068d42175e Merge remote-tracking branch 'upstream/master' 2023-09-08 17:37:00 +02:00
sk
2314871246 temporary fix for pre-release users 2023-09-08 17:36:17 +02:00
butterflyoffire
e4f13c900b Translated using Weblate (Arabic)
Currently translated at 67.5% (250 of 370 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/ar/
2023-09-07 19:23:10 +00:00
ppnplus
4ddfa483d4 Translated using Weblate (Thai)
Currently translated at 23.5% (87 of 370 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/th/
2023-09-07 19:23:10 +00:00
butterflyoffire
20f41ce7c9 Translated using Weblate (Kabyle)
Currently translated at 0.5% (2 of 370 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/kab/
2023-09-07 19:23:10 +00:00
Arkxv
c8df9e085e Translated using Weblate (Japanese)
Currently translated at 78.3% (290 of 370 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/ja/
2023-09-07 19:23:10 +00:00
GunChleoc
0238aa4375 Translated using Weblate (Gaelic)
Currently translated at 84.3% (312 of 370 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/gd/
2023-09-07 19:23:09 +00:00
butterflyoffire
79d7873790 Translated using Weblate (French)
Currently translated at 100.0% (370 of 370 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/fr/
2023-09-07 19:23:09 +00:00
kallekn
996842489d Translated using Weblate (Finnish)
Currently translated at 100.0% (370 of 370 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/fi/
2023-09-07 19:23:09 +00:00
Andrewblasco
23c2c2b5e7 Translated using Weblate (Spanish)
Currently translated at 100.0% (370 of 370 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/es/
2023-09-07 19:23:08 +00:00
sk
9dd694ce2e Merge branch 'merge/load-more-direction' 2023-09-07 18:32:21 +02:00
FineFindus
f51f2a1197 fix: use random account id in notification endpoint (#806)
Closes https://github.com/sk22/megalodon/issues/803. This was due to registering with the actual accounId, whilst saving a random one. Once receiving a notification, the id could not be found.
2023-09-07 18:28:32 +02:00
LucasGGamerM
3020cab243 fix: fixes crashes when currentQuery is null and suicide prevention dialog is enabled (#807)
cc: @FineFindus
2023-09-07 10:03:22 +02:00
Eugen Rochko
be73c9e81c New translations strings.xml (Basque) 2023-09-07 02:28:42 +02:00
Eugen Rochko
1c2183bf1a New translations strings.xml (Slovenian) 2023-09-06 22:29:03 +02:00
LucasGGamerM
da54c93af1 Merge branch 'fix/notification-endpoint' 2023-09-06 14:34:27 -03:00
LucasGGamerM
777891ebe9 fix: fixes crashes when currentQuery is null and suicide prevention dialog is enabled
cc: @FineFindus
2023-09-06 11:24:50 -03:00
Andrewblasco
9836538562 Translated using Weblate (Spanish)
Currently translated at 100.0% (83 of 83 strings)

Translation: Moshidon/values
Translate-URL: https://translate.codeberg.org/projects/moshidon/values/es/
2023-09-06 12:12:24 +00:00
GunChleoc
4a443d4826 Translated using Weblate (Gaelic)
Currently translated at 100.0% (83 of 83 strings)

Translation: Moshidon/values
Translate-URL: https://translate.codeberg.org/projects/moshidon/values/gd/
2023-09-06 12:07:38 +00:00
butterflyoffire
1c93d1fa67 Translated using Weblate (French)
Currently translated at 90.3% (75 of 83 strings)

Translation: Moshidon/values
Translate-URL: https://translate.codeberg.org/projects/moshidon/values/fr/
2023-09-06 12:07:38 +00:00
LucasGGamerM
0246059556 Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (83 of 83 strings)

Translation: Moshidon/values
Translate-URL: https://translate.codeberg.org/projects/moshidon/values/pt_BR/
2023-09-06 12:07:38 +00:00
butterflyoffire
46382bd8b0 Translated using Weblate (Arabic (Algeria))
Currently translated at 62.1% (51 of 82 strings)

Translation: Moshidon/values
Translate-URL: https://translate.codeberg.org/projects/moshidon/values/ar_DZ/
2023-09-06 12:07:38 +00:00
LucasGGamerM
090d2658dd feat: also hide link card image previews according to showMediaPreview setting 2023-09-06 09:06:14 -03:00
LucasGGamerM
e47fa4f8db Merge pull request #255
fix: update timeline when showMediaPreview changes
2023-09-06 09:00:16 -03:00
FineFindus
8338fff4e5 fix: use random account id in notification endpoint
Closes https://github.com/sk22/megalodon/issues/803. This was due to registering with the actual accounId, whilst saving a random one. Once receiving a notification, the id could not be found.
2023-09-06 09:44:03 +02:00
FineFindus
b2d09c2ab6 fix: update timeline when showMediaPreview changes 2023-09-06 09:05:51 +02:00
LucasGGamerM
0aa4493e5c Merge remote-tracking branch 'mastodon/master' 2023-09-05 20:51:12 -03:00
LucasGGamerM
6e2a5437e8 fix: apply correct showMediaPreview setting on threads and notifications 2023-09-05 20:46:56 -03:00
LucasGGamerM
ae61007d22 fix: use correct polarization in FLAG_NO_MEDIA_PREVIEW in StatusListFragment
Wtf is wrong with this
2023-09-05 20:29:57 -03:00
LucasGGamerM
20f3c7c9db fix: use correct polarization in FLAG_NO_MEDIA_PREVIEW 2023-09-05 19:52:59 -03:00
Gregory K
1789d90dc3 Merge pull request #685 from LucasGGamerM/mastodon-android
fix(editing-alt-text): fix small oversight on editing existing attachments without alt text
2023-09-06 01:42:41 +03:00
LucasGGamerM
57306ff7fe fix(editing-alt-text): fix small oversight on editing existing attachments without alt text
This makes the implementation hopefully bug free
2023-09-05 19:37:12 -03:00
LucasGGamerM
bbdd1c4aa9 fix(editing-alt-text): fix small oversight on editing existing attachments without alt text
This makes the implementation hopefully bug free
2023-09-05 19:00:16 -03:00
LucasGGamerM
81d13537e3 fix: fixes incorrect icon coloring on compose fragment relocated publish button 2023-09-05 15:19:46 -03:00
LucasGGamerM
b6c951c026 feat: add indicatives/hints of what type of media is on an media item without preview 2023-09-05 14:51:32 -03:00
LucasGGamerM
860cdb05e2 feat: add no alt text and sensitive media warning to images without preview 2023-09-05 14:25:20 -03:00
LucasGGamerM
e239600af6 feat: full implementation of not showing media preview in timelines 2023-09-05 11:56:56 -03:00
LucasGGamerM
d7bef67550 feat: initial steps for only show media preview setting
It contains settings, flags and settings. But no implementation just yet
2023-09-05 11:24:35 -03:00
LucasGGamerM
ad86a00fa7 fix: make changing showPostDividers setting restart the activity 2023-09-05 10:37:17 -03:00
LucasGGamerM
da13926269 Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (82 of 82 strings)

Translation: Moshidon/values
Translate-URL: https://translate.codeberg.org/projects/moshidon/values/pt_BR/
2023-09-05 12:21:52 +00:00
LucasGGamerM
9da0173215 Translated using Weblate (Portuguese (Brazil))
Currently translated at 93.9% (77 of 82 strings)

Translation: Moshidon/values
Translate-URL: https://translate.codeberg.org/projects/moshidon/values/pt_BR/
2023-09-05 12:16:25 +00:00
LucasGGamerM
cfeda06bf9 fix: make repo and donate links non-translatable 2023-09-05 09:16:07 -03:00
butterflyoffire
ca60ac6722 Translated using Weblate (Arabic (Algeria))
Currently translated at 2.9% (1 of 34 strings)

Translation: Moshidon/metadata
Translate-URL: https://translate.codeberg.org/projects/moshidon/metadata/ar_DZ/
2023-09-05 12:09:40 +00:00
butterflyoffire
80f70bd530 Translated using Weblate (Arabic (Algeria))
Currently translated at 44.0% (164 of 372 strings)

Translation: Moshidon/values_sk
Translate-URL: https://translate.codeberg.org/projects/moshidon/values_sk/ar_DZ/
2023-09-05 12:09:40 +00:00
butterflyoffire
3982fa4835 Translated using Weblate (Arabic)
Currently translated at 0.0% (0 of 34 strings)

Translation: Moshidon/metadata
Translate-URL: https://translate.codeberg.org/projects/moshidon/metadata/ar/
2023-09-05 12:09:40 +00:00
butterflyoffire
31e720690c Translated using Weblate (French)
Currently translated at 100.0% (372 of 372 strings)

Translation: Moshidon/values_sk
Translate-URL: https://translate.codeberg.org/projects/moshidon/values_sk/fr/
2023-09-05 12:09:40 +00:00
butterflyoffire
560c75c669 Translated using Weblate (Arabic (Algeria))
Currently translated at 13.4% (11 of 82 strings)

Translation: Moshidon/values
Translate-URL: https://translate.codeberg.org/projects/moshidon/values/ar_DZ/
2023-09-05 12:09:40 +00:00
butterflyoffire
fb441b21e0 Translated using Weblate (Burmese)
Currently translated at 2.9% (1 of 34 strings)

Translation: Moshidon/metadata
Translate-URL: https://translate.codeberg.org/projects/moshidon/metadata/my/
2023-09-05 12:09:40 +00:00
butterflyoffire
e2f957f985 Translated using Weblate (Chinese (Traditional))
Currently translated at 5.8% (2 of 34 strings)

Translation: Moshidon/metadata
Translate-URL: https://translate.codeberg.org/projects/moshidon/metadata/zh_Hant/
2023-09-05 12:09:40 +00:00
butterflyoffire
a4f97a134b Translated using Weblate (Croatian)
Currently translated at 5.8% (2 of 34 strings)

Translation: Moshidon/metadata
Translate-URL: https://translate.codeberg.org/projects/moshidon/metadata/hr/
2023-09-05 12:09:40 +00:00
butterflyoffire
18ae9efad3 Translated using Weblate (French)
Currently translated at 2.9% (1 of 34 strings)

Translation: Moshidon/metadata
Translate-URL: https://translate.codeberg.org/projects/moshidon/metadata/fr/
2023-09-05 12:09:39 +00:00
butterflyoffire
8232047c55 Translated using Weblate (French)
Currently translated at 90.2% (74 of 82 strings)

Translation: Moshidon/values
Translate-URL: https://translate.codeberg.org/projects/moshidon/values/fr/
2023-09-05 12:09:39 +00:00
LucasGGamerM
fb3b9513f2 fix: fix window insets on ScheduledStatusListFragment
cc: @sk22
2023-09-05 08:21:06 -03:00
LucasGGamerM
2ee4b03fe8 refactor: remove DomainDisplay.java 2023-09-05 07:23:07 -03:00
LucasGGamerM
61b20eea05 Merge remote-tracking branch 'megalodon_main/main'
# Conflicts:
#	mastodon/src/main/java/org/joinmastodon/android/GlobalUserPreferences.java
#	mastodon/src/main/java/org/joinmastodon/android/model/Account.java
2023-09-05 07:12:27 -03:00
sk
2aba90f353 add media indicator for spoiler
closes sk22#598
2023-09-05 00:18:47 +02:00
sk
5065c7e7e2 add dummy to add spacing
closes sk22#782
2023-09-04 23:42:00 +02:00
sk
a10e661b21 fix wrong info banner color
closes sk22#790
2023-09-04 23:37:34 +02:00
sk
4975bde76f fix tab layout horizontal paddings 2023-09-04 23:33:35 +02:00
FineFindus
ebbd56e3bc feat(search): show suicide help dialog (#767)
* feat(search): show suicide help dialog

* change wording, add helpline url, change behavior

---------

Co-authored-by: sk <sk22@mailbox.org>
2023-09-04 23:07:24 +02:00
Jacoco
454ec6b4c0 Fix errors when some entries were missing when retrieving account information (#757)
* Handle null avatar

* Handle empty account
2023-09-04 22:24:17 +02:00
FineFindus
10a8b195b1 fix: move tabbar padding to tabbar items (#792)
* feat: increase padding without labels

* fix: move tabbar padding to individual items
2023-09-04 22:19:47 +02:00
sk
6f273df060 use single-line linear layout 2023-09-04 19:54:07 +02:00
sk
7cfade62d3 fix click listener target 2023-09-04 19:53:33 +02:00
LucasGGamerM
0e58b00086 fix: make use of correct coloring in filled button backgrounds (again) 2023-09-04 12:13:55 -03:00
LucasGGamerM
9f18844148 fix: make use of correct coloring in filled button backgrounds 2023-09-04 11:18:36 -03:00
LucasGGamerM
d854da54fe fix: make purple be on top of pink
Me no likey pink >:C
2023-09-04 10:39:37 -03:00
LucasGGamerM
33f4dd4716 fix: compose fragment bottom drafts button being not centered 2023-09-04 10:31:01 -03:00
LucasGGamerM
93ca187f1c fix: use bigger bot icon on header display item 2023-09-04 07:56:17 -03:00
LucasGGamerM
6fd59a19e8 fix: remove duplicate variable assigning 2023-09-04 07:46:42 -03:00
LucasGGamerM
531ce568c0 Merge remote-tracking branch 'megalodon_main/main'
# Conflicts:
#	mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java
#	mastodon/src/main/res/drawable/ic_fluent_bot_20_filled.xml
#	mastodon/src/main/res/layout/display_item_header.xml
2023-09-04 07:42:25 -03:00
LucasGGamerM
c49b25454e refactor(HomeFragment): refactor long click action for cleaner code 2023-09-04 07:35:48 -03:00
LucasGGamerM
d39d5c2602 Merge pull request #252
feat: allow doubleTapToSwipe when doubleTapToSearch is disabled
2023-09-04 07:19:54 -03:00
starstuff
0a338ad607 Translated using Weblate (Swedish)
Currently translated at 41.6% (154 of 370 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/sv/
2023-09-04 08:05:19 +00:00
Arkxv
0cb3e1863e Translated using Weblate (Japanese)
Currently translated at 5.4% (20 of 370 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/ja/
2023-09-04 08:05:19 +00:00
kallekn
209081f1f0 Translated using Weblate (Finnish)
Currently translated at 0.8% (3 of 370 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/fi/
2023-09-04 08:05:19 +00:00
Eugen Rochko
f0eb6573f4 New translations strings.xml (Portuguese, Brazilian) 2023-09-04 08:10:24 +02:00
Eugen Rochko
e7f5dd3357 New translations strings.xml (Bengali) 2023-09-04 07:10:06 +02:00
Eugen Rochko
8101bb9ea1 New translations strings.xml (Portuguese, Brazilian) 2023-09-04 07:10:05 +02:00
butterflyoffire
54d48253d5 Translated using Weblate (Arabic)
Currently translated at 12.4% (46 of 370 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/ar/
2023-09-04 00:53:06 +00:00
ca
3373b2bb04 Translated using Weblate (Catalan)
Currently translated at 100.0% (18 of 18 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/ca/
2023-09-04 00:53:06 +00:00
ca
a7e23aa228 Translated using Weblate (Catalan)
Currently translated at 100.0% (370 of 370 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/ca/
2023-09-04 00:53:06 +00:00
FineFindus
a2d0738a68 fix: only call openSearch once without doubleTapToSearch 2023-09-03 21:47:45 +02:00
FineFindus
8cd55fc365 feat: display icon for bots (#793)
* feat(profile): display bot icon for bots

* feat(status): display bot icon

* use 16sp size and 4sp spacing

* don't add lock/bot icon as image spans

---------

Co-authored-by: sk <sk22@mailbox.org>
2023-09-03 21:34:22 +02:00
LucasGGamerM
924d6a9dd9 Merge remote-tracking branch 'weblate/master'
# Conflicts:
#	mastodon/src/main/res/values-es-rES/strings_sk.xml
#	mastodon/src/main/res/values-fr-rFR/strings_sk.xml
2023-09-03 16:34:13 -03:00
butterflyoffire
816a307bc0 Translated using Weblate (Arabic (Algeria))
Currently translated at 18.3% (68 of 370 strings)

Translation: Moshidon/values_sk
Translate-URL: https://translate.codeberg.org/projects/moshidon/values_sk/ar_DZ/
2023-09-03 19:31:20 +00:00
butterflyoffire
a947a6ea26 Translated using Weblate (Arabic)
Currently translated at 18.9% (70 of 370 strings)

Translation: Moshidon/values_sk
Translate-URL: https://translate.codeberg.org/projects/moshidon/values_sk/ar/
2023-09-03 19:31:20 +00:00
LucasGGamerM
f14df2bb0f fix: allows for editing timeline badges again. fixes sk22#800 (#804) 2023-09-03 20:49:29 +02:00
LucasGGamerM
b5123ff865 feat: readd recent emoji support 2023-09-03 15:32:35 -03:00
FineFindus
ca4630e6e1 feat: always allow longtap to search 2023-09-03 16:00:11 +02:00
FineFindus
a8a1c7824d feat: allow doubleTapToSwipe in discoverfragment 2023-09-03 15:59:19 +02:00
FineFindus
0725c2a741 feat: add setting to disable double tap to search 2023-09-03 15:47:11 +02:00
LucasGGamerM
c3f3a9be7e feat: update edit note confirmation button design (again) 2023-09-03 09:23:33 -03:00
Eugen Rochko
228fdc8ffe New translations strings.xml (Swedish) 2023-09-03 11:26:37 +02:00
butterflyoffire
da2a69b4a1 Translated using Weblate (Arabic)
Currently translated at 48.7% (39 of 80 strings)

Translation: Moshidon/values
Translate-URL: https://translate.codeberg.org/projects/moshidon/values/ar/
2023-09-03 09:20:46 +00:00
butterflyoffire
822a6a3653 Translated using Weblate (French)
Currently translated at 99.7% (369 of 370 strings)

Translation: Moshidon/values_sk
Translate-URL: https://translate.codeberg.org/projects/moshidon/values_sk/fr/
2023-09-03 09:20:46 +00:00
butterflyoffire
410f694863 Translated using Weblate (Arabic)
Currently translated at 18.3% (68 of 370 strings)

Translation: Moshidon/values_sk
Translate-URL: https://translate.codeberg.org/projects/moshidon/values_sk/ar/
2023-09-03 09:20:46 +00:00
butterflyoffire
57b2596d9f Translated using Weblate (French)
Currently translated at 83.7% (67 of 80 strings)

Translation: Moshidon/values
Translate-URL: https://translate.codeberg.org/projects/moshidon/values/fr/
2023-09-03 09:20:45 +00:00
ca
caebce897b Translated using Weblate (Catalan)
Currently translated at 35.0% (28 of 80 strings)

Translation: Moshidon/values
Translate-URL: https://translate.codeberg.org/projects/moshidon/values/ca/
2023-09-03 09:20:45 +00:00
LucasGGamerM
677fb4e4da fix: readds bot icon to timelines
This was accidentally removed during the great merge, but its back again
2023-09-02 21:02:11 -03:00
LucasGGamerM
ce51fc3c0d fix: allows for editing timeline badges again. fixes sk22#800 2023-09-02 20:40:55 -03:00
LucasGGamerM
0dcade767b Merge remote-tracking branch 'megalodon_main/main' 2023-09-02 20:26:22 -03:00
LucasGGamerM
f4f4cbe78b fix: fix incorrect m3_overlay alpha values 2023-09-02 20:06:56 -03:00
alextecplayz
ebb6897582 Translated using Weblate (Romanian)
Currently translated at 100.0% (370 of 370 strings)

Translation: Moshidon/values_sk
Translate-URL: https://translate.codeberg.org/projects/moshidon/values_sk/ro/
2023-09-01 22:53:06 +00:00
gallegonovato
9720a133ba Translated using Weblate (Spanish)
Currently translated at 100.0% (370 of 370 strings)

Translation: Moshidon/values_sk
Translate-URL: https://translate.codeberg.org/projects/moshidon/values_sk/es/
2023-09-01 22:53:06 +00:00
alextecplayz
29c36e90ab Translated using Weblate (Romanian)
Currently translated at 100.0% (80 of 80 strings)

Translation: Moshidon/values
Translate-URL: https://translate.codeberg.org/projects/moshidon/values/ro/
2023-09-01 22:53:05 +00:00
gallegonovato
1b822676a8 Translated using Weblate (Spanish)
Currently translated at 100.0% (80 of 80 strings)

Translation: Moshidon/values
Translate-URL: https://translate.codeberg.org/projects/moshidon/values/es/
2023-09-01 22:53:05 +00:00
Angelo Suzuki
2d24e50ff2 Add a setting for "Load missing posts behavior"
This will make sure that items that are filtered out don't show up on the interface.
Fixes sk22#311
2023-09-01 21:51:14 +02:00
Jacoco
53369eb2d4 Fix unreliable Preferences from Account Source (#798)
* Create empty Preferences object on error

* Update prefs from account when preferences fails
2023-09-01 21:28:02 +02:00
LucasGGamerM
aeb0aa65a8 Merge remote-tracking branch 'megalodon_main/main'
# Conflicts:
#	mastodon/src/main/java/org/joinmastodon/android/api/session/AccountLocalPreferences.java
2023-09-01 14:11:19 -03:00
Eugen Rochko
e9df125cde New translations strings.xml (Swedish) 2023-09-01 16:10:32 +02:00
sk
807010893a support static url for emoji reacts and keyboard 2023-09-01 14:38:55 +02:00
sk
ea01b14ffb more options for showing emoji reactions
closes sk22#796
re: sk22#788
2023-09-01 14:23:38 +02:00
arnav
3fd9dc1dcd Translated using Weblate (Hindi)
Currently translated at 4.3% (16 of 370 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/hi/
2023-08-31 23:18:52 +00:00
sk
02a4a77885 Merge remote-tracking branch 'weblate/main' 2023-09-01 01:18:30 +02:00
sk
651090a504 Merge remote-tracking branch 'upstream/master' 2023-09-01 01:17:18 +02:00
sk
203254c9f4 fix crash in onEmojiReactionsChanged 2023-09-01 01:16:52 +02:00
LucasGGamerM
74db111a4f Merge remote-tracking branch 'megalodon_main/main'
# Conflicts:
#	mastodon/src/main/java/org/joinmastodon/android/api/session/AccountLocalPreferences.java
#	mastodon/src/main/java/org/joinmastodon/android/fragments/StatusListFragment.java
2023-08-31 14:36:59 -03:00
Gregory K
16ef577a7a Merge pull request #678 from LucasGGamerM/mastodon-android
fix: fix alt texts not being able to be edited
2023-08-31 20:31:39 +03:00
LucasGGamerM
734b3bced6 fix: fix alt texts not being able to be edited
fixes #70 cc: @sk22
2023-08-31 14:18:32 -03:00
sk
e26c641dc7 fix drafts being all on the same-ish day 2023-08-31 18:07:43 +02:00
sk
9295cf4e9c fix emoji reaction spacing not updating
closes sk22#784
2023-08-31 17:24:29 +02:00
sk
dd9237e9ca enable emojiReactionsInTimelines by default 2023-08-31 17:20:44 +02:00
sk
ea81c1fad6 Merge remote-tracking branch 'upstream/master' 2023-08-31 17:09:18 +02:00
sk
d334703c65 adhere to showEmojiReactionsInLists setting
closes sk22#788
2023-08-31 17:04:22 +02:00
LucasGGamerM
befa5a9f6d fix: fix alt texts not being able to be edited
fixes #70 cc: @sk22
2023-08-31 11:43:39 -03:00
Gregory K
5f6f3c94c9 Merge pull request #677 from tinsukE/gap-local-filter
When loading gap posts, apply filters before building display items.
2023-08-31 15:06:07 +03:00
Angelo Suzuki
09ba42a974 When loading gap posts, apply filters before building display items.
This will make sure that items that are filtered out don't show up on the interface.
Fixes #675
2023-08-31 14:01:58 +02:00
Oliebol
87a9acd860 Translated using Weblate (Dutch)
Currently translated at 96.2% (77 of 80 strings)

Translation: Moshidon/values
Translate-URL: https://translate.codeberg.org/projects/moshidon/values/nl/
2023-08-31 03:53:05 +00:00
ihor_ck
b1e43d6f97 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (370 of 370 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/uk/
2023-08-30 18:04:05 +00:00
ppnplus
ea92a61d13 Translated using Weblate (Thai)
Currently translated at 20.5% (76 of 370 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/th/
2023-08-30 18:04:05 +00:00
Linerly
7fda69a6aa Translated using Weblate (Indonesian)
Currently translated at 100.0% (370 of 370 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/id/
2023-08-30 18:04:05 +00:00
gallegonovato
cf8b9ac649 Translated using Weblate (Spanish)
Currently translated at 100.0% (370 of 370 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/es/
2023-08-30 18:04:05 +00:00
LucasGGamerM
d871d2cad8 fix: fix broken Ask before unfollowing option 2023-08-30 11:36:17 -03:00
LucasGGamerM
323018daa9 fix: fix crash when there is an error on status posting 2023-08-30 07:50:32 -03:00
ppnplus
3c122b005d Translated using Weblate (Thai)
Currently translated at 3.7% (14 of 370 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/th/
2023-08-29 22:37:15 +00:00
edxkl
f9dc6105f4 Translated using Weblate (Portuguese (Brazil))
Currently translated at 97.5% (361 of 370 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/pt_BR/
2023-08-29 22:37:15 +00:00
Oliebol
d7fe3c80e6 Translated using Weblate (Dutch)
Currently translated at 82.9% (307 of 370 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/nl/
2023-08-29 22:37:15 +00:00
Choukajohn
e996deea0b Translated using Weblate (French)
Currently translated at 100.0% (370 of 370 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/fr/
2023-08-29 22:37:14 +00:00
LucasGGamerM
67d89f5799 fix: fix less prominent post dividers
cc: @sk22
2023-08-29 19:12:47 -03:00
LucasGGamerM
485d25f9f4 fix: undo wrong string rename
AAAAAAAAAAAAAAAAAAA
2023-08-29 10:44:18 -03:00
LucasGGamerM
c4b7262e89 Merge remote-tracking branch 'megalodon_main/main'
# Conflicts:
#	mastodon/src/main/java/org/joinmastodon/android/fragments/settings/SettingsAboutAppFragment.java
2023-08-29 10:41:09 -03:00
LucasGGamerM
7191e5a96a feat: use better account log off alert 2023-08-29 10:33:08 -03:00
LucasGGamerM
0edaffee59 fix: fix Warning item title being cut off 2023-08-29 09:05:33 -03:00
Weblate
c859d35c93 Added translation using Weblate (Norwegian Nynorsk) 2023-08-29 11:47:51 +00:00
LucasGGamerM
17a59b5db4 fix: fix hide with a warning posts without alt text 2023-08-29 08:20:11 -03:00
S1m
580ae15af6 Fix unifiedpush (#785)
* [UnifiedPush] Register the new endpoint

* [UnifiedPush] Get account linked to instance
2023-08-29 12:22:12 +02:00
Eugen Rochko
d76e823489 New translations strings.xml (Italian) 2023-08-29 11:52:38 +02:00
LucasGGamerM
7c3b4128de feat: use better color background for extended footer 2023-08-28 21:25:35 -03:00
LucasGGamerM
da5929c092 feat: improvements on the black and white theme 2023-08-28 21:14:20 -03:00
LucasGGamerM
77fca4d763 fix: fix bottom bar labels ellipsizing wrongly on devices with a smaller screen 2023-08-28 21:03:00 -03:00
sk
e66078e52e fix poll options outline provider
closes sk22#702
2023-08-29 00:53:02 +02:00
FineFindus
f84356f5d0 fix: display 'Lists with' with username (#780) 2023-08-29 00:19:34 +02:00
sk
1fa5a8436b highlight verified links again!
closes sk22#713
2023-08-29 00:18:12 +02:00
sk
2a471ffa96 fix broken "edit avatar" overlay.. again!!
closes sk22#727
closes sk22#738
2023-08-28 23:48:18 +02:00
sk
1ee5e1ab3a fix cut-off settings item titles
closes sk22#723
2023-08-28 23:42:34 +02:00
sk
4d90cad034 Merge remote-tracking branch 'weblate/main' 2023-08-28 23:35:18 +02:00
sk
45615b1fc5 Merge remote-tracking branch 'upstream/l10n_master' 2023-08-28 23:35:04 +02:00
sk
9fd0e7fea4 return if fcm device token is empty
closes sk22#779
2023-08-28 23:33:28 +02:00
sk
696016bd8f use hasSpoiler method 2023-08-28 23:29:18 +02:00
sk
cc46e09853 fix issue with visibility button
closes sk22#740
2023-08-28 23:24:56 +02:00
sk
83e84836b5 fix slightly wrong margins
closes sk22#778
2023-08-28 22:47:43 +02:00
sk
14bb544344 replace donation link 2023-08-28 22:35:17 +02:00
sk
ceec18ff5e fix divider not being full-width 2023-08-28 22:33:19 +02:00
sk
d36ad43700 fix reporting crashing the app 2023-08-28 22:26:53 +02:00
LucasGGamerM
d93bcd9241 feat: use ellipsize for settings items
IDK why It can't just continue on the next line :(
2023-08-28 17:23:00 -03:00
LucasGGamerM
cfad3144f8 fix: fix the nord theme (again) 2023-08-28 16:13:43 -03:00
LucasGGamerM
69272e891e fix: use better colors for true black mode
cc: @sk22
2023-08-28 15:57:33 -03:00
LucasGGamerM
576030f262 fix: fixes inconsistent coloring on turned-off switches
cc: @sk22
2023-08-28 15:54:18 -03:00
sk
fb39f74ba5 fix bottom padding for above emoji reactions 2023-08-28 20:22:16 +02:00
LucasGGamerM
272afca0fb fix: fixes inconsistent coloring on bottom tabbar 2023-08-28 15:15:02 -03:00
sk
9bfc73d6ee fix wrong bottom padding
hopefully, lol
2023-08-28 20:04:52 +02:00
LucasGGamerM
d0e3da67a4 fix: fix wrong favorite selected coloring on nord theme 2023-08-28 14:04:13 -03:00
LucasGGamerM
1ecb9820c0 refactor: change bookmark_selected to colorBookmark 2023-08-28 12:18:25 -03:00
EndermanCo
efb72eace9 Translated using Weblate (Persian)
Currently translated at 50.0% (9 of 18 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/fa/
2023-08-28 14:53:05 +00:00
edxkl
84b15f4a49 Translated using Weblate (Portuguese (Brazil))
Currently translated at 97.5% (360 of 369 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/pt_BR/
2023-08-28 14:53:05 +00:00
ca
2c793fd83b Translated using Weblate (Catalan)
Currently translated at 100.0% (369 of 369 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/ca/
2023-08-28 14:53:05 +00:00
LucasGGamerM
8810ac52f6 Merge pull request #245
fix: small nord theme improvements
2023-08-28 11:38:30 -03:00
LucasGGamerM
9442de7e5a Merge pull request #246
fix: allow long version names
2023-08-28 11:32:37 -03:00
LucasGGamerM
4f40ecd112 Merge pull request #247
fix: display 'Lists with' with username
2023-08-28 11:31:03 -03:00
LucasGGamerM
5456f4fbea refactor: tidy things up with the show posts with missing alt text
IT DOESNT WORK YET GODDAMNIT
2023-08-28 11:30:08 -03:00
LucasGGamerM
b294c998ae Merge branch 'feature/hide_alttext'
# Conflicts:
#	mastodon/src/androidTest/java/org/joinmastodon/android/utils/StatusFilterPredicateTest.java
#	mastodon/src/main/java/org/joinmastodon/android/fragments/settings/TimeLineFragment.java
#	mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/WarningFilteredStatusDisplayItem.java
#	mastodon/src/main/java/org/joinmastodon/android/utils/StatusFilterPredicate.java
#	mastodon/src/main/res/values-de-rDE/strings_mo.xml
#	mastodon/src/main/res/values/strings_mo.xml
2023-08-28 10:15:18 -03:00
FineFindus
b7b7ef4faf fix: display 'Lists with' with username 2023-08-28 14:43:28 +02:00
FineFindus
6a736ef387 fix: allow long version names 2023-08-28 14:32:31 +02:00
FineFindus
2a7d7d2e45 fix: use single color for favorites 2023-08-28 14:08:38 +02:00
LucasGGamerM
bdf822d6a5 fix: fix haptic feedback setting not being saved 2023-08-28 09:04:15 -03:00
LucasGGamerM
a9cd67a64e fix: fix wrong coloring on the follow suggestions fragment bottom bar 2023-08-28 08:44:01 -03:00
LucasGGamerM
a3ee20f06e feat: increase roundness of the select instance button background
Yes, this is the 3rd time I do this
2023-08-28 08:33:52 -03:00
FineFindus
2158a1abae fix(theme/nord): use #2E3440 as background color 2023-08-28 13:29:32 +02:00
LucasGGamerM
52955b6c42 fix: fix wrong tap-to-reveal string 2023-08-28 08:27:42 -03:00
LucasGGamerM
8524e57ecd fix inconsistent coloring on alert dialog items
cc: @sk22
2023-08-27 20:57:29 -03:00
LucasGGamerM
639ecba380 feat: increase roundness of the Popup menus 2023-08-27 20:46:13 -03:00
LucasGGamerM
ed6961016e feat: increase roundness of the logon instance input field (again) 2023-08-27 20:41:41 -03:00
LucasGGamerM
5d1d6e4bd7 fix: fix crash when long clicking search from the search tab 2023-08-27 20:15:53 -03:00
LucasGGamerM
e87f47228f fix: readd only translate when opened settings (off by default) 2023-08-27 19:22:35 -03:00
LucasGGamerM
8e038b204c fix: readd showDividers setting 2023-08-27 19:16:42 -03:00
Eugen Rochko
900b204bb0 New translations strings.xml (Portuguese, Brazilian) 2023-08-27 16:51:00 +02:00
Eugen Rochko
17d679901a New translations strings.xml (Japanese) 2023-08-27 15:42:52 +02:00
Eugen Rochko
d8036779f8 New translations strings.xml (Japanese) 2023-08-27 14:46:15 +02:00
0que
3a35674ea2 Translated using Weblate (Russian)
Currently translated at 77.7% (14 of 18 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/ru/
2023-08-27 05:53:05 +00:00
0que
3235cf1c4f Translated using Weblate (Russian)
Currently translated at 89.7% (331 of 369 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/ru/
2023-08-27 05:53:04 +00:00
Eugen Rochko
3f6bda28b3 New translations strings.xml (Vietnamese) 2023-08-26 17:43:48 +02:00
Eugen Rochko
c0b4f4dd79 New translations strings.xml (Vietnamese) 2023-08-26 16:06:27 +02:00
starstuff
2a913e26e7 Translated using Weblate (Swedish)
Currently translated at 41.4% (153 of 369 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/sv/
2023-08-25 14:53:06 +00:00
edxkl
f60375cd5f Translated using Weblate (Portuguese (Brazil))
Currently translated at 97.0% (358 of 369 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/pt_BR/
2023-08-25 14:53:06 +00:00
poesty
5920270899 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (369 of 369 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/zh_Hans/
2023-08-25 14:53:06 +00:00
Eugen Rochko
f36aee44c6 New translations strings.xml (Scottish Gaelic) 2023-08-25 09:54:23 +02:00
Eugen Rochko
cd24526a9d New translations strings.xml (Swedish) 2023-08-24 17:48:37 +02:00
Eugen Rochko
a345ac1390 New translations strings.xml (Icelandic) 2023-08-24 17:48:36 +02:00
Eugen Rochko
3d987b8e1d New translations strings.xml (Thai) 2023-08-23 21:39:07 +02:00
Eugen Rochko
57043912e0 New translations strings.xml (Chinese Traditional) 2023-08-23 19:16:40 +02:00
Eugen Rochko
00aef5ea6b New translations full_description.txt (Arabic) 2023-08-23 13:02:31 +02:00
Eugen Rochko
369b69668c New translations strings.xml (Arabic) 2023-08-23 13:02:29 +02:00
Eugen Rochko
65245f4560 New translations strings.xml (Arabic) 2023-08-23 09:39:50 +02:00
Eugen Rochko
4d4fdc97d4 New translations strings.xml (French) 2023-08-23 09:39:49 +02:00
Eugen Rochko
c96577891c New translations strings.xml (Scottish Gaelic) 2023-08-23 07:00:18 +02:00
Eugen Rochko
f48b2fc9cb New translations strings.xml (Thai) 2023-08-23 07:00:13 +02:00
Eugen Rochko
2fca2580ed New translations strings.xml (Bengali) 2023-08-23 07:00:12 +02:00
Eugen Rochko
7adc1da361 New translations strings.xml (Persian) 2023-08-23 07:00:11 +02:00
Eugen Rochko
6dc24dde43 New translations strings.xml (Indonesian) 2023-08-23 07:00:10 +02:00
Eugen Rochko
4929e0e6ec New translations strings.xml (Portuguese, Brazilian) 2023-08-23 07:00:09 +02:00
Eugen Rochko
16a8b8ed71 New translations strings.xml (Icelandic) 2023-08-23 07:00:08 +02:00
Eugen Rochko
ad1412817e New translations strings.xml (Galician) 2023-08-23 07:00:07 +02:00
Eugen Rochko
d9e6bb3bea New translations strings.xml (Vietnamese) 2023-08-23 07:00:06 +02:00
Eugen Rochko
8970404638 New translations strings.xml (Chinese Traditional) 2023-08-23 07:00:05 +02:00
Eugen Rochko
2a2241d7f9 New translations strings.xml (Chinese Simplified) 2023-08-23 07:00:04 +02:00
Eugen Rochko
db1a47e8eb New translations strings.xml (Ukrainian) 2023-08-23 07:00:03 +02:00
Eugen Rochko
3e57061cef New translations strings.xml (Turkish) 2023-08-23 07:00:02 +02:00
Eugen Rochko
cd200f8450 New translations strings.xml (Slovenian) 2023-08-23 07:00:00 +02:00
Eugen Rochko
782013079f New translations strings.xml (Russian) 2023-08-23 06:59:59 +02:00
Eugen Rochko
e5db8acd66 New translations strings.xml (Polish) 2023-08-23 06:59:57 +02:00
Eugen Rochko
1a6a8019c8 New translations strings.xml (Norwegian) 2023-08-23 06:59:57 +02:00
Eugen Rochko
e935eef29f New translations strings.xml (Dutch) 2023-08-23 06:59:56 +02:00
Eugen Rochko
381defda51 New translations strings.xml (Japanese) 2023-08-23 06:59:54 +02:00
Eugen Rochko
02ae80c204 New translations strings.xml (Italian) 2023-08-23 06:59:53 +02:00
Eugen Rochko
82214b30e8 New translations strings.xml (Armenian) 2023-08-23 06:59:52 +02:00
Eugen Rochko
33a1f48602 New translations strings.xml (Greek) 2023-08-23 06:59:48 +02:00
Eugen Rochko
aee845e5cc New translations strings.xml (German) 2023-08-23 06:59:47 +02:00
Eugen Rochko
cd780f6006 New translations strings.xml (Danish) 2023-08-23 06:59:46 +02:00
Eugen Rochko
d4741fefa0 New translations strings.xml (Czech) 2023-08-23 06:59:46 +02:00
Eugen Rochko
7e1e8a2616 New translations strings.xml (Belarusian) 2023-08-23 06:59:44 +02:00
Eugen Rochko
d73c05cdfc New translations strings.xml (Arabic) 2023-08-23 06:59:43 +02:00
Eugen Rochko
78323023cb New translations strings.xml (Spanish) 2023-08-23 06:59:42 +02:00
Eugen Rochko
2cf084c98f New translations strings.xml (French) 2023-08-23 06:59:41 +02:00
Botiplz
009dc226d5 Update mastodon/src/main/res/values-de-rDE/strings_mo.xml
Co-authored-by: FineFindus <63370021+FineFindus@users.noreply.github.com>
2023-07-22 22:16:17 +02:00
Botiplz
94ab5835d8 Formatting strings_mo.xml 2023-07-22 19:51:54 +02:00
Botiplz
ae5d2a7ed3 Formatting 2023-07-22 19:50:58 +02:00
Botiplz
6ed7d24513 PR comments:
- StatusFilterPredicate sets clientFilters bases on settings
- Reverted changes on SettingsFragment
- Resources in strings_mo.xml
- removed duplicate comments
- conditional in onBind method
2023-07-22 19:41:01 +02:00
Botiplz
605ba441d3 Option "Show media posts with missing alt text" 2023-07-22 16:58:15 +02:00
197 changed files with 4137 additions and 1085 deletions

0
fix-metadata-markdown-lists.sh Executable file → Normal file
View File

0
gradlew vendored Executable file → Normal file
View File

View File

@@ -19,7 +19,9 @@ public class StatusFilterPredicateTest {
private static final Status
hideInHomePublic = Status.ofFake(null, "hide me, please", Instant.now()),
warnInHomePublic = Status.ofFake(null, "display me with a warning", Instant.now());
warnInHomePublic = Status.ofFake(null, "display me with a warning", Instant.now()),
noAltText = Status.ofFake(null, "display me with a warning", Instant.now()),
withAltText = Status.ofFake(null, "display me with a warning", Instant.now());
static {
hideMeFilter.phrase = "hide me";
@@ -29,6 +31,12 @@ public class StatusFilterPredicateTest {
warnMeFilter.phrase = "warning";
warnMeFilter.filterAction = WARN;
warnMeFilter.context = EnumSet.of(PUBLIC, HOME);
noAltText.mediaAttachments = Attachment.createFakeAttachments("fakeurl", new ColorDrawable());
withAltText.mediaAttachments = Attachment.createFakeAttachments("fakeurl", new ColorDrawable());
for (Attachment mediaAttachment : withAltText.mediaAttachments) {
mediaAttachment.description = "Alt Text";
}
}
@Test
@@ -78,4 +86,16 @@ public class StatusFilterPredicateTest {
assertTrue("should pass because matching filter is for hiding",
new StatusFilterPredicate(allFilters, HOME, WARN).test(hideInHomePublic));
}
@Test
public void testAltTextFilterNoPass() {
assertFalse("should not pass because of no alt text",
new StatusFilterPredicate(allFilters, HOME).test(noAltText));
}
@Test
public void testAltTextFilterPass() {
assertTrue("should pass because of alt text",
new StatusFilterPredicate(allFilters, HOME).test(withAltText));
}
}

View File

@@ -18,7 +18,6 @@ import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.model.ContentType;
import org.joinmastodon.android.model.Instance;
import org.joinmastodon.android.model.TimelineDefinition;
import org.joinmastodon.android.ui.utils.ColorPalette;
import java.lang.reflect.Type;
import java.util.ArrayList;
@@ -27,6 +26,8 @@ import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import androidx.annotation.StringRes;
public class GlobalUserPreferences{
private static final String TAG="GlobalUserPreferences";
@@ -62,11 +63,13 @@ public class GlobalUserPreferences{
public static boolean showNavigationLabels;
public static boolean displayPronounsInTimelines, displayPronounsInThreads, displayPronounsInUserListings;
public static boolean overlayMedia;
public static boolean showSuicideHelp;
// MOSHIDON
public static boolean showDividers;
public static boolean relocatePublishButton;
public static boolean defaultToUnlistedReplies;
public static boolean doubleTapToSearch;
public static boolean doubleTapToSwipe;
public static boolean confirmBeforeReblog;
public static boolean hapticFeedback;
@@ -74,6 +77,8 @@ public class GlobalUserPreferences{
public static boolean swapBookmarkWithBoostAction;
public static boolean loadRemoteAccountFollowers;
public static boolean mentionRebloggerAutomatically;
public static boolean showPostsWithoutAlt;
public static boolean showMediaPreview;
public static SharedPreferences getPrefs(){
return MastodonApp.context.getSharedPreferences("global", Context.MODE_PRIVATE);
@@ -133,6 +138,7 @@ public class GlobalUserPreferences{
displayPronounsInThreads=prefs.getBoolean("displayPronounsInThreads", true);
displayPronounsInUserListings=prefs.getBoolean("displayPronounsInUserListings", true);
overlayMedia=prefs.getBoolean("overlayMedia", false);
showSuicideHelp=prefs.getBoolean("showSuicideHelp", true);
// MOSHIDON
uniformNotificationIcon=prefs.getBoolean("uniformNotificationIcon", false);
@@ -140,6 +146,7 @@ public class GlobalUserPreferences{
relocatePublishButton=prefs.getBoolean("relocatePublishButton", true);
compactReblogReplyLine=prefs.getBoolean("compactReblogReplyLine", true);
defaultToUnlistedReplies=prefs.getBoolean("defaultToUnlistedReplies", false);
doubleTapToSearch =prefs.getBoolean("doubleTapToSearch", true);
doubleTapToSwipe =prefs.getBoolean("doubleTapToSwipe", true);
replyLineAboveHeader=prefs.getBoolean("replyLineAboveHeader", true);
confirmBeforeReblog=prefs.getBoolean("confirmBeforeReblog", false);
@@ -147,6 +154,9 @@ public class GlobalUserPreferences{
swapBookmarkWithBoostAction=prefs.getBoolean("swapBookmarkWithBoostAction", false);
loadRemoteAccountFollowers=prefs.getBoolean("loadRemoteAccountFollowers", true);
mentionRebloggerAutomatically=prefs.getBoolean("mentionRebloggerAutomatically", false);
showPostsWithoutAlt=prefs.getBoolean("showPostsWithoutAlt", true);
showMediaPreview=prefs.getBoolean("showMediaPreview", true);
theme=ThemePreference.values()[prefs.getInt("theme", 0)];
@@ -211,19 +221,25 @@ public class GlobalUserPreferences{
.putBoolean("displayPronounsInThreads", displayPronounsInThreads)
.putBoolean("displayPronounsInUserListings", displayPronounsInUserListings)
.putBoolean("overlayMedia", overlayMedia)
.putBoolean("showSuicideHelp", showSuicideHelp)
// MOSHIDON
.putBoolean("defaultToUnlistedReplies", defaultToUnlistedReplies)
.putBoolean("doubleTapToSearch", doubleTapToSearch)
.putBoolean("doubleTapToSwipe", doubleTapToSwipe)
.putBoolean("compactReblogReplyLine", compactReblogReplyLine)
.putBoolean("replyLineAboveHeader", replyLineAboveHeader)
.putBoolean("confirmBeforeReblog", confirmBeforeReblog)
.putBoolean("swapBookmarkWithBoostAction", swapBookmarkWithBoostAction)
.putBoolean("loadRemoteAccountFollowers", loadRemoteAccountFollowers)
.putBoolean("hapticFeedback", hapticFeedback)
.putBoolean("mentionRebloggerAutomatically", mentionRebloggerAutomatically)
.putBoolean("showDividers", showDividers)
.putBoolean("relocatePublishButton", relocatePublishButton)
.putBoolean("enableDeleteNotifications", enableDeleteNotifications)
.putBoolean("showPostsWithoutAlt", showPostsWithoutAlt)
.putBoolean("showMediaPreview", showMediaPreview)
.putInt("theme", theme.ordinal())
.apply();
@@ -281,8 +297,8 @@ public class GlobalUserPreferences{
public enum ColorPreference{
MATERIAL3,
PINK,
PURPLE,
PINK,
GREEN,
BLUE,
BROWN,

View File

@@ -340,7 +340,7 @@ public class PushNotificationReceiver extends BroadcastReceiver{
req.visibility = preferences.postingDefaultVisibility;
req.inReplyToId = notification.status.id;
if (!notification.status.spoilerText.isEmpty() &&
if (notification.status.hasSpoiler() &&
(GlobalUserPreferences.prefixReplies == ALWAYS
|| (GlobalUserPreferences.prefixReplies == TO_OTHERS && !ownID.equals(notification.status.account.id)))
&& !notification.status.spoilerText.startsWith("re: ")) {

View File

@@ -27,9 +27,9 @@ public class UnifiedPushNotificationReceiver extends MessagingReceiver{
public void onNewEndpoint(@NotNull Context context, @NotNull String endpoint, @NotNull String instance) {
// Called when a new endpoint be used for sending push messages
Log.d(TAG, "onNewEndpoint: New Endpoint " + endpoint + " for "+ instance);
AccountSession account = AccountSessionManager.getInstance().getLastActiveAccount();
AccountSession account = AccountSessionManager.getInstance().tryGetAccount(instance);
if (account != null)
account.getPushSubscriptionManager().registerAccountForPush(null);
account.getPushSubscriptionManager().registerAccountForPush(null, endpoint);
}
@Override
@@ -37,7 +37,7 @@ public class UnifiedPushNotificationReceiver extends MessagingReceiver{
// called when the registration is not possible, eg. no network
Log.d(TAG, "onRegistrationFailed: " + instance);
//re-register for gcm
AccountSession account = AccountSessionManager.getInstance().getLastActiveAccount();
AccountSession account = AccountSessionManager.getInstance().tryGetAccount(instance);
if (account != null)
account.getPushSubscriptionManager().registerAccountForPush(null);
}
@@ -47,7 +47,7 @@ public class UnifiedPushNotificationReceiver extends MessagingReceiver{
// called when this application is unregistered from receiving push messages
Log.d(TAG, "onUnregistered: " + instance);
//re-register for gcm
AccountSession account = AccountSessionManager.getInstance().getLastActiveAccount();
AccountSession account = AccountSessionManager.getInstance().tryGetAccount(instance);
if (account != null)
account.getPushSubscriptionManager().registerAccountForPush(null);
}
@@ -55,7 +55,10 @@ public class UnifiedPushNotificationReceiver extends MessagingReceiver{
@Override
public void onMessage(@NotNull Context context, @NotNull byte[] message, @NotNull String instance) {
// Called when a new message is received. The message contains the full POST body of the push message
AccountSession account = AccountSessionManager.getInstance().getAccount(instance);
AccountSession account = AccountSessionManager.getInstance().tryGetAccount(instance);
if (account == null)
return;
//this is stupid
// Mastodon stores the info to decrypt the message in the HTTP headers, which are not accessible in UnifiedPush,

View File

@@ -97,7 +97,7 @@ public class PushSubscriptionManager{
deviceToken=getPrefs().getString("deviceToken", null);
int tokenVersion=getPrefs().getInt("version", 0);
if(!TextUtils.isEmpty(deviceToken) && tokenVersion==BuildConfig.VERSION_CODE){
registerAllAccountsForPush(false);
registerAllAccountsForPush(true); // TODO: revert this before release
return;
}
Log.i(TAG, "tryRegisterFCM: no token found or app was updated. Trying to get push token...");
@@ -125,17 +125,16 @@ public class PushSubscriptionManager{
// this function is used for registering push notifications using FCM
// to avoid NonFreeNet in F-Droid, this registration is disabled in it
// see https://github.com/LucasGGamerM/moshidon/issues/206 for more context
if(BuildConfig.BUILD_TYPE.equals("fdroidRelease"))
if(BuildConfig.BUILD_TYPE.equals("fdroidRelease") || TextUtils.isEmpty(deviceToken)){
Log.d(TAG, "Skipping registering for FCM push notifications");
return;
}
if(TextUtils.isEmpty(deviceToken))
throw new IllegalStateException("No device push token available");
String endpoint = "https://app.joinmastodon.org/relay-to/fcm/"+deviceToken+"/"+accountID;
String endpoint = "https://app.joinmastodon.org/relay-to/fcm/"+deviceToken+"/";
registerAccountForPush(subscription, endpoint);
}
public void registerAccountForPush(PushSubscription subscription, String endpoint){
MastodonAPIController.runInBackground(()->{
Log.d(TAG, "registerAccountForPush: started for "+accountID);
String encodedPublicKey, encodedAuthKey, pushAccountID;
@@ -164,7 +163,13 @@ public class PushSubscriptionManager{
Log.e(TAG, "registerAccountForPush: error generating encryption key", e);
return;
}
new RegisterForPushNotifications(endpoint,
//work-around for adding the randomAccountId
String newEndpoint = endpoint;
if (endpoint.startsWith("https://app.joinmastodon.org/relay-to/fcm/"))
newEndpoint += pushAccountID;
new RegisterForPushNotifications(newEndpoint,
encodedPublicKey,
encodedAuthKey,
subscription==null ? PushSubscription.Alerts.ofAll() : subscription.alerts,

View File

@@ -11,13 +11,11 @@ import java.util.ArrayList;
import java.util.List;
public class CreateStatus extends MastodonAPIRequest<Status>{
public static final Instant DRAFTS_AFTER_INSTANT = Instant.ofEpochMilli(253370764799999L) /* end of 9998 */;
private static final float draftFactor = 31536000000f /* one year */ / 253370764799999f /* end of 9998 */;
public static long EPOCH_OF_THE_YEAR_FIVE_THOUSAND=95617584000000L;
public static final Instant DRAFTS_AFTER_INSTANT=Instant.ofEpochMilli(EPOCH_OF_THE_YEAR_FIVE_THOUSAND - 1) /* end of 4999 */;
public static Instant getDraftInstant() {
// returns an instant between 9999-01-01 00:00:00 and 9999-12-31 23:59:59
// yes, this is a weird implementation for something that hardly matters
return DRAFTS_AFTER_INSTANT.plusMillis(1 + (long) (System.currentTimeMillis() * draftFactor));
return DRAFTS_AFTER_INSTANT.plusMillis(System.currentTimeMillis());
}
public CreateStatus(CreateStatus.Request req, String uuid){
@@ -36,6 +34,7 @@ public class CreateStatus extends MastodonAPIRequest<Status>{
public static class Request{
public String status;
public List<MediaAttribute> mediaAttributes;
public List<String> mediaIds;
public Poll poll;
public String inReplyToId;
@@ -55,5 +54,17 @@ public class CreateStatus extends MastodonAPIRequest<Status>{
public boolean multiple;
public boolean hideTotals;
}
public static class MediaAttribute{
public String id;
public String description;
public String focus;
public MediaAttribute(String id, String description, String focus){
this.id=id;
this.description=description;
this.focus=focus;
}
}
}
}

View File

@@ -42,7 +42,7 @@ public class AccountLocalPreferences{
public boolean keepOnlyLatestNotification;
public boolean emojiReactionsEnabled;
public boolean showEmojiReactionsInLists;
public ShowEmojiReactions showEmojiReactions;
private final static Type recentLanguagesType = new TypeToken<ArrayList<String>>() {}.getType();
private final static Type timelinesType = new TypeToken<ArrayList<TimelineDefinition>>() {}.getType();
@@ -73,7 +73,7 @@ public class AccountLocalPreferences{
timelineReplyVisibility=prefs.getString("timelineReplyVisibility", null);
keepOnlyLatestNotification=prefs.getBoolean("keepOnlyLatestNotification", false);
emojiReactionsEnabled=prefs.getBoolean("emojiReactionsEnabled", session.getInstance().isPresent() && session.getInstance().get().isAkkoma());
showEmojiReactionsInLists=prefs.getBoolean("showEmojiReactionsInLists", false);
showEmojiReactions=ShowEmojiReactions.valueOf(prefs.getString("showEmojiReactions", ShowEmojiReactions.HIDE_EMPTY.name()));
// MOSHIDON
recentEmojis=fromJson(prefs.getString("recentEmojis", "{}"), recentEmojisType, new HashMap<>());
@@ -109,10 +109,16 @@ public class AccountLocalPreferences{
.putString("timelineReplyVisibility", timelineReplyVisibility)
.putBoolean("keepOnlyLatestNotification", keepOnlyLatestNotification)
.putBoolean("emojiReactionsEnabled", emojiReactionsEnabled)
.putBoolean("showEmojiReactionsInLists", showEmojiReactionsInLists)
.putString("showEmojiReactions", showEmojiReactions.name())
// MOSHIDON
.putString("recentEmojis", gson.toJson(recentEmojis))
.apply();
}
public enum ShowEmojiReactions{
HIDE_EMPTY,
ONLY_OPENED,
ALWAYS
}
}

View File

@@ -146,6 +146,9 @@ public class AccountSession{
@Override
public void onError(ErrorResponse error){
Log.w(TAG, "Failed to load preferences for account "+getID()+": "+error);
if (preferences==null)
preferences=new Preferences();
preferencesFromAccountSource(self);
}
})
.exec(getID());
@@ -310,4 +313,10 @@ public class AccountSession{
.authority(getInstance().map(i -> i.normalizedUri).orElse(domain))
.build();
}
public String getDefaultAvatarUrl() {
return getInstance()
.map(instance->"https://"+domain+(instance.isAkkoma() ? "/images/avi.png" : "/avatars/original/missing.png"))
.orElse("");
}
}

View File

@@ -0,0 +1,19 @@
package org.joinmastodon.android.events;
import androidx.recyclerview.widget.RecyclerView;
import org.joinmastodon.android.model.EmojiReaction;
import java.util.List;
public class EmojiReactionsUpdatedEvent{
public final String id;
public final List<EmojiReaction> reactions;
public final boolean updateTextPadding;
public RecyclerView.ViewHolder viewHolder;
public EmojiReactionsUpdatedEvent(String id, List<EmojiReaction> reactions, boolean updateTextPadding, RecyclerView.ViewHolder viewHolder){
this.id=id;
this.reactions=reactions;
this.updateTextPadding=updateTextPadding;
this.viewHolder=viewHolder;
}
}

View File

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

View File

@@ -44,6 +44,7 @@ import org.joinmastodon.android.ui.displayitems.HeaderStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.MediaGridStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.PollFooterStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.PollOptionStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.PreviewlessMediaGridStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.SpoilerStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.TextStatusDisplayItem;
@@ -51,6 +52,7 @@ import org.joinmastodon.android.ui.displayitems.WarningFilteredStatusDisplayItem
import org.joinmastodon.android.ui.photoviewer.PhotoViewer;
import org.joinmastodon.android.ui.photoviewer.PhotoViewerHost;
import org.joinmastodon.android.ui.utils.MediaAttachmentViewController;
import org.joinmastodon.android.ui.utils.PreviewlessMediaAttachmentViewController;
import org.joinmastodon.android.ui.utils.UiUtils;
import org.joinmastodon.android.utils.ProvidesAssistContent;
import org.joinmastodon.android.utils.TypedObjectPool;
@@ -90,6 +92,8 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
protected HashMap<String, Relationship> relationships=new HashMap<>();
protected Rect tmpRect=new Rect();
protected TypedObjectPool<MediaGridStatusDisplayItem.GridItemType, MediaAttachmentViewController> attachmentViewsPool=new TypedObjectPool<>(this::makeNewMediaAttachmentView);
protected TypedObjectPool<MediaGridStatusDisplayItem.GridItemType, PreviewlessMediaAttachmentViewController> previewlessAttachmentViewsPool=new TypedObjectPool<>(this::makeNewPreviewlessMediaAttachmentView);
protected boolean currentlyScrolling;
public BaseStatusListFragment(){
@@ -280,6 +284,79 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
});
}
public void openPreviewlessMediaPhotoViewer(String parentID, Status _status, int attachmentIndex, PreviewlessMediaGridStatusDisplayItem.Holder gridHolder){
final Status status=_status.getContentStatus();
currentPhotoViewer=new PhotoViewer(getActivity(), status.mediaAttachments, attachmentIndex, new PhotoViewer.Listener(){
private PreviewlessMediaAttachmentViewController transitioningHolder;
@Override
public void setPhotoViewVisibility(int index, boolean visible){
}
@Override
public boolean startPhotoViewTransition(int index, @NonNull Rect outRect, @NonNull int[] outCornerRadius){
PreviewlessMediaAttachmentViewController holder=findPhotoViewHolder(index);
if(holder!=null && list!=null){
transitioningHolder=holder;
View view=transitioningHolder.inner;
int[] pos={0, 0};
view.getLocationOnScreen(pos);
outRect.set(pos[0], pos[1], pos[0]+view.getWidth(), pos[1]+view.getHeight());
list.setClipChildren(false);
gridHolder.setClipChildren(false);
transitioningHolder.view.setElevation(1f);
return true;
}
return false;
}
@Override
public void setTransitioningViewTransform(float translateX, float translateY, float scale){
View view=transitioningHolder.inner;
view.setTranslationX(translateX);
view.setTranslationY(translateY);
view.setScaleX(scale);
view.setScaleY(scale);
}
@Override
public void endPhotoViewTransition(){
View view=transitioningHolder.inner;
view.setTranslationX(0f);
view.setTranslationY(0f);
view.setScaleX(1f);
view.setScaleY(1f);
transitioningHolder.view.setElevation(0f);
if(list!=null)
list.setClipChildren(true);
gridHolder.setClipChildren(true);
transitioningHolder=null;
}
@Nullable
@Override
public Drawable getPhotoViewCurrentDrawable(int index){
return null;
}
@Override
public void photoViewerDismissed(){
currentPhotoViewer=null;
}
@Override
public void onRequestPermissions(String[] permissions){
requestPermissions(permissions, PhotoViewer.PERMISSION_REQUEST);
}
private PreviewlessMediaAttachmentViewController findPhotoViewHolder(int index){
return gridHolder.getViewController(index);
}
});
}
@Override
public @Nullable View getFab() {
if (getParentFragment() instanceof HasFab l) return l.getFab();
@@ -622,7 +699,7 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
}
}
public void onGapClick(GapStatusDisplayItem.Holder item){}
public void onGapClick(GapStatusDisplayItem.Holder item, boolean downwards){}
public void onWarningClick(WarningFilteredStatusDisplayItem.Holder warning){
int startPos = warning.getAbsoluteAdapterPosition();
@@ -768,10 +845,18 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
return new MediaAttachmentViewController(getActivity(), type);
}
private PreviewlessMediaAttachmentViewController makeNewPreviewlessMediaAttachmentView(MediaGridStatusDisplayItem.GridItemType type){
return new PreviewlessMediaAttachmentViewController(getActivity(), type);
}
public TypedObjectPool<MediaGridStatusDisplayItem.GridItemType, MediaAttachmentViewController> getAttachmentViewsPool(){
return attachmentViewsPool;
}
public TypedObjectPool<MediaGridStatusDisplayItem.GridItemType, PreviewlessMediaAttachmentViewController> getPreviewlessAttachmentViewsPool(){
return previewlessAttachmentViewsPool;
}
@Override
public void onProvideAssistContent(AssistContent assistContent) {
assistContent.setWebUri(getWebUri(getSession().getInstanceUri().buildUpon()));
@@ -846,7 +931,7 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
private Paint dividerPaint=new Paint();
{
dividerPaint.setColor(UiUtils.getThemeColor(getActivity(), GlobalUserPreferences.showDividers ? R.attr.colorM3OutlineVariant : R.attr.colorM3Surface));
dividerPaint.setColor(UiUtils.getThemeColor(getActivity(), GlobalUserPreferences.showDividers ? R.attr.colorM3Outline : R.attr.colorM3Surface));
dividerPaint.setStyle(Paint.Style.STROKE);
dividerPaint.setStrokeWidth(V.dp(0.5f));
}

View File

@@ -465,7 +465,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
hasSpoiler=true;
spoilerWrap.setVisibility(View.VISIBLE);
spoilerBtn.setSelected(true);
}else if(editingStatus!=null && !TextUtils.isEmpty(editingStatus.spoilerText)){
}else if(editingStatus!=null && editingStatus.hasSpoiler()){
hasSpoiler=true;
spoilerWrap.setVisibility(View.VISIBLE);
spoilerEdit.setText(getArguments().getString("sourceSpoiler", editingStatus.spoilerText));
@@ -737,7 +737,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
view.findViewById(R.id.time).setVisibility(time==null ? View.GONE : View.VISIBLE);
if(time!=null) ((TextView) view.findViewById(R.id.time)).setText(time);
if (status.spoilerText != null && !status.spoilerText.isBlank()) {
if (status.hasSpoiler()) {
TextView replyToSpoiler = view.findViewById(R.id.reply_to_spoiler);
replyToSpoiler.setVisibility(View.VISIBLE);
replyToSpoiler.setText(status.spoilerText);
@@ -1171,6 +1171,9 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
req.scheduledAt=scheduledAt;
if(!mediaViewController.isEmpty()){
req.mediaIds=mediaViewController.getAttachmentIDs();
if(editingStatus != null){
req.mediaAttributes=mediaViewController.getAttachmentAttributes();
}
}
// ask whether to publish now when editing an existing draft
if (!force && editingStatus != null && scheduledAt != null && scheduledAt.isAfter(DRAFTS_AFTER_INSTANT)) {
@@ -1291,7 +1294,13 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
wm.removeView(sendingOverlay);
sendingOverlay=null;
V.setVisibilityAnimated(sendProgress, View.GONE);
if(GlobalUserPreferences.relocatePublishButton) {
publishButtonRelocated.setEnabled(true);
} else {
publishButton.setEnabled(true);
}
if(error instanceof MastodonErrorResponse me){
new M3AlertDialogBuilder(getActivity())
.setTitle(R.string.post_failed)

View File

@@ -1,15 +0,0 @@
package org.joinmastodon.android.fragments;
import org.joinmastodon.android.api.session.AccountSession;
import org.joinmastodon.android.api.session.AccountSessionManager;
public interface DomainDisplay {
default String getDomain(){
AccountSession session = AccountSessionManager.getInstance().getLastActiveAccount();
if (session != null)
return session.domain;
else
return "";
}
}

View File

@@ -379,7 +379,7 @@ public class EditTimelinesFragment extends MastodonRecyclerFragment<TimelineDefi
mainHashtag = name;
name = null;
}
if (TextUtils.isEmpty(mainHashtag)) {
if (TextUtils.isEmpty(mainHashtag) && (item != null && item.getType() == TimelineDefinition.TimelineType.HASHTAG)) {
Toast.makeText(ctx, R.string.sk_add_timeline_tag_error_empty, Toast.LENGTH_SHORT).show();
onSave.accept(null);
return;

View File

@@ -45,8 +45,8 @@ public class FeaturedHashtagsListFragment extends BaseStatusListFragment<Hashtag
}
@Override
public void onItemClick(String id){
UiUtils.openHashtagTimeline(getActivity(), accountID, id, data.stream().filter(h -> Objects.equals(h.name, id)).findAny().map(h -> h.following).orElse(null));
public void onItemClick(String hashtag){
UiUtils.openHashtagTimeline(getActivity(), accountID, hashtag, data.stream().filter(h -> Objects.equals(h.name, hashtag)).findAny().map(h -> h.following).orElse(null));
}
@Override

View File

@@ -17,8 +17,10 @@ import android.widget.TextView;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.requests.accounts.GetAccountRelationships;
import org.joinmastodon.android.api.requests.accounts.GetFollowRequests;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.model.Account;
import org.joinmastodon.android.model.HeaderPaginationList;
import org.joinmastodon.android.model.Instance;
import org.joinmastodon.android.model.Relationship;
import org.joinmastodon.android.ui.OutlineProviders;
import org.joinmastodon.android.ui.text.HtmlParser;
@@ -357,8 +359,9 @@ public class FollowRequestsListFragment extends MastodonRecyclerFragment<FollowR
public AccountWrapper(Account account){
this.account=account;
if(!TextUtils.isEmpty(account.avatar))
avaRequest=new UrlImageLoaderRequest(account.avatar, V.dp(50), V.dp(50));
avaRequest=new UrlImageLoaderRequest(
TextUtils.isEmpty(account.avatar) ? AccountSessionManager.get(getAccountID()).getDefaultAvatarUrl() : account.avatar,
V.dp(50), V.dp(50));
if(!TextUtils.isEmpty(account.header))
coverRequest=new UrlImageLoaderRequest(account.header, 1000, 1000);
parsedBio=HtmlParser.parse(account.note, account.emojis, Collections.emptyList(), Collections.emptyList(), accountID);

View File

@@ -269,7 +269,7 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
private void onTabSelected(@IdRes int tab){
Fragment newFragment=fragmentForTab(tab);
if(tab==currentTab){
if (tab == R.id.tab_search)
if (tab == R.id.tab_search && GlobalUserPreferences.doubleTapToSearch)
discoverFragment.openSearch();
else if(newFragment instanceof ScrollableToTop scrollable)
scrollable.scrollToTop();
@@ -309,8 +309,10 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
return true;
}
if(tab==R.id.tab_search){
if(currentTab!=R.id.tab_search){
onTabSelected(R.id.tab_search);
tabBar.selectTab(R.id.tab_search);
}
discoverFragment.openSearch();
return true;
}

View File

@@ -13,25 +13,24 @@ import org.joinmastodon.android.api.requests.markers.SaveMarkers;
import org.joinmastodon.android.api.requests.timelines.GetHomeTimeline;
import org.joinmastodon.android.api.session.AccountLocalPreferences;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.events.StatusCreatedEvent;
import org.joinmastodon.android.model.CacheablePaginatedResponse;
import org.joinmastodon.android.model.FilterContext;
import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.model.TimelineMarkers;
import org.joinmastodon.android.ui.displayitems.GapStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
import org.joinmastodon.android.utils.StatusFilterPredicate;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import me.grishka.appkit.api.Callback;
import me.grishka.appkit.api.ErrorResponse;
import me.grishka.appkit.api.SimpleCallback;
import me.grishka.appkit.utils.V;
public class HomeTimelineFragment extends StatusListFragment {
private HomeTabFragment parent;
@@ -176,15 +175,23 @@ public class HomeTimelineFragment extends StatusListFragment {
}
@Override
public void onGapClick(GapStatusDisplayItem.Holder item){
public void onGapClick(GapStatusDisplayItem.Holder item, boolean downwards){
if(dataLoading)
return;
item.getItem().loading=true;
V.setVisibilityAnimated(item.progress, View.VISIBLE);
V.setVisibilityAnimated(item.text, View.GONE);
GapStatusDisplayItem gap=item.getItem();
gap.loading=true;
dataLoading=true;
currentRequest=new GetHomeTimeline(item.getItemID(), null, 20, null, getLocalPrefs().timelineReplyVisibility)
String maxID = null;
String minID = null;
if (downwards) {
maxID = item.getItemID();
} else {
int gapPos=displayItems.indexOf(gap);
StatusDisplayItem nextItem=displayItems.get(gapPos + 1);
minID=nextItem.parentID;
}
currentRequest=new GetHomeTimeline(maxID, minID, 20, null, getLocalPrefs().timelineReplyVisibility)
.setCallback(new Callback<>(){
@Override
public void onSuccess(List<Status> result){
@@ -204,6 +211,7 @@ public class HomeTimelineFragment extends StatusListFragment {
AccountSessionManager.getInstance().getAccount(accountID).getCacheController().putHomeTimeline(Collections.singletonList(gapStatus), false);
}
}else{
if(downwards) {
Set<String> idsBelowGap=new HashSet<>();
boolean belowGap=false;
int gapPostIndex=0;
@@ -229,19 +237,16 @@ public class HomeTimelineFragment extends StatusListFragment {
}else{
result=result.subList(0, endIndex);
}
AccountSessionManager.get(accountID).filterStatuses(result, FilterContext.HOME);
List<StatusDisplayItem> targetList=displayItems.subList(gapPos, gapPos+1);
targetList.clear();
List<Status> insertedPosts=data.subList(gapPostIndex+1, gapPostIndex+1);
StatusFilterPredicate filterPredicate=new StatusFilterPredicate(accountID, getFilterContext());
for(Status s:result){
if(idsBelowGap.contains(s.id))
break;
if(typeFilterPredicate(s) && filterPredicate.test(s)){
targetList.addAll(buildDisplayItems(s));
insertedPosts.add(s);
}
}
AccountSessionManager.get(accountID).filterStatuses(insertedPosts, getFilterContext());
if(targetList.isEmpty()){
// oops. We didn't add new posts, but at least we know there are none.
adapter.notifyItemRemoved(getMainAdapterOffset()+gapPos);
@@ -250,6 +255,52 @@ public class HomeTimelineFragment extends StatusListFragment {
adapter.notifyItemRangeInserted(getMainAdapterOffset()+gapPos+1, targetList.size()-1);
}
AccountSessionManager.getInstance().getAccount(accountID).getCacheController().putHomeTimeline(insertedPosts, false);
} else {
String aboveGapID = gap.parentID;
int gapPostIndex = 0;
for (;gapPostIndex<data.size();gapPostIndex++){
if (Objects.equals(aboveGapID, data.get(gapPostIndex).id)) {
break;
}
}
// find if there's an overlap between the new data and the current data
int indexOfGapInResponse = 0;
for (;indexOfGapInResponse<result.size();indexOfGapInResponse++){
if (Objects.equals(aboveGapID, result.get(indexOfGapInResponse).id)) {
break;
}
}
// there is an overlap between new and current data
List<StatusDisplayItem> targetList=displayItems.subList(gapPos, gapPos+1);
if(indexOfGapInResponse<result.size()){
result=result.subList(indexOfGapInResponse+1,result.size());
Optional<Status> gapStatus=data.stream()
.filter(s->Objects.equals(s.id, gap.parentID))
.findFirst();
if (gapStatus.isPresent()) {
gapStatus.get().hasGapAfter=false;
AccountSessionManager.getInstance().getAccount(accountID).getCacheController().putHomeTimeline(Collections.singletonList(gapStatus.get()), false);
}
targetList.clear();
} else {
gap.loading=false;
}
List<Status> insertedPosts=data.subList(gapPostIndex+1, gapPostIndex+1);
for(Status s:result){
targetList.addAll(buildDisplayItems(s));
insertedPosts.add(s);
}
AccountSessionManager.get(accountID).filterStatuses(insertedPosts, FilterContext.HOME);
if(targetList.isEmpty()){
// oops. We didn't add new posts, but at least we know there are none.
adapter.notifyItemRemoved(getMainAdapterOffset()+gapPos);
}else{
adapter.notifyItemChanged(getMainAdapterOffset()+gapPos);
adapter.notifyItemRangeInserted(getMainAdapterOffset()+gapPos+1, targetList.size()-1);
}
list.scrollToPosition(getMainAdapterOffset()+gapPos+targetList.size());
AccountSessionManager.getInstance().getAccount(accountID).getCacheController().putHomeTimeline(insertedPosts, false);
}
}
}

View File

@@ -15,6 +15,7 @@ import org.joinmastodon.android.E;
import org.joinmastodon.android.GlobalUserPreferences;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.events.EmojiReactionsUpdatedEvent;
import org.joinmastodon.android.events.PollUpdatedEvent;
import org.joinmastodon.android.events.RemoveAccountPostsEvent;
import org.joinmastodon.android.events.StatusCountersUpdatedEvent;
@@ -27,6 +28,7 @@ import org.joinmastodon.android.ui.displayitems.ExtendedFooterStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.FooterStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.NotificationHeaderStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.TextStatusDisplayItem;
import org.joinmastodon.android.ui.utils.DiscoverInfoBannerHelper;
import org.joinmastodon.android.ui.utils.InsetStatusItemDecoration;
import org.joinmastodon.android.ui.utils.UiUtils;
@@ -43,7 +45,6 @@ import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import me.grishka.appkit.api.SimpleCallback;
import me.grishka.appkit.utils.MergeRecyclerAdapter;
import me.grishka.appkit.views.FragmentRootLinearLayout;
public class NotificationsListFragment extends BaseStatusListFragment<Notification> {
private boolean onlyMentions;
@@ -95,15 +96,15 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
if (n.type == Notification.Type.FOLLOW_REQUEST) {
ArrayList<StatusDisplayItem> items = new ArrayList<>();
items.add(titleItem);
items.add(new AccountCardStatusDisplayItem(n.id, this, n.account, n));
items.add(new AccountCardStatusDisplayItem(n.id, this, accountID, n.account, n));
return items;
}
if(n.status!=null){
int flags=titleItem==null ? 0 : (StatusDisplayItem.FLAG_NO_FOOTER | StatusDisplayItem.FLAG_INSET | StatusDisplayItem.FLAG_NO_EMOJI_REACTIONS); // | StatusDisplayItem.FLAG_NO_HEADER);
if (GlobalUserPreferences.spectatorMode)
flags |= StatusDisplayItem.FLAG_NO_FOOTER;
if (!getLocalPrefs().showEmojiReactionsInLists)
flags |= StatusDisplayItem.FLAG_NO_EMOJI_REACTIONS;
if (!GlobalUserPreferences.showMediaPreview)
flags |= StatusDisplayItem.FLAG_NO_MEDIA_PREVIEW;
ArrayList<StatusDisplayItem> items=StatusDisplayItem.buildItems(this, n.status, accountID, n, knownAccounts, null, flags);
if(titleItem!=null)
items.add(0, titleItem);
@@ -248,8 +249,7 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
@Subscribe
public void onStatusCountersUpdated(StatusCountersUpdatedEvent ev){
for(Notification n:data){
if (n.status == null) continue;
if(n.status.getContentStatus().id.equals(ev.id)){
if(n.status!=null && n.status.getContentStatus().id.equals(ev.id)){
n.status.getContentStatus().update(ev);
AccountSessionManager.get(accountID).getCacheController().updateNotification(n);
for(int i=0;i<list.getChildCount();i++){
@@ -258,15 +258,36 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
footer.rebind();
}else if(holder instanceof ExtendedFooterStatusDisplayItem.Holder footer && footer.getItem().status==n.status.getContentStatus()){
footer.rebind();
}else if(holder instanceof EmojiReactionsStatusDisplayItem.Holder reactions && ev.viewHolder!=holder){
reactions.rebind();
}
}
}
}
for(Notification n:preloadedData){
if (n.status == null) continue;
if(n.status.getContentStatus().id.equals(ev.id)){
if(n.status!=null && n.status.getContentStatus().id.equals(ev.id)){
n.status.getContentStatus().update(ev);
AccountSessionManager.get(accountID).getCacheController().updateNotification(n);
}
}
}
@Subscribe
public void onEmojiReactionsChanged(EmojiReactionsUpdatedEvent ev){
for(Notification n : data){
if(n.status!=null && n.status.getContentStatus().id.equals(ev.id)){
n.status.getContentStatus().update(ev);
AccountSessionManager.get(accountID).getCacheController().updateNotification(n);
for(int i=0; i<list.getChildCount(); i++){
RecyclerView.ViewHolder holder=list.getChildViewHolder(list.getChildAt(i));
if(holder instanceof EmojiReactionsStatusDisplayItem.Holder reactions && reactions.getItem().status==n.status.getContentStatus() && ev.viewHolder!=holder){
reactions.rebind();
}else if(holder instanceof TextStatusDisplayItem.Holder text && text.getItem().parentID.equals(n.getID())){
text.rebind();
}
}
}
}
for(Notification n : preloadedData){
if(n.status!=null && n.status.getContentStatus().id.equals(ev.id)){
n.status.getContentStatus().update(ev);
AccountSessionManager.get(accountID).getCacheController().updateNotification(n);
}

View File

@@ -4,10 +4,12 @@ import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.Fragment;
import android.app.assist.AssistContent;
import android.content.Intent;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.graphics.Color;
@@ -135,6 +137,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
private CoverImageView cover;
private View avatarBorder;
private TextView name, username, bio, followersCount, followersLabel, followingCount, followingLabel;
private ImageView lockIcon, botIcon;
private ProgressBarButton actionButton, notifyButton;
private ViewPager2 pager;
private NestedRecyclerScrollView scrollView;
@@ -237,6 +240,8 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
avatarBorder=content.findViewById(R.id.avatar_border);
name=content.findViewById(R.id.name);
username=content.findViewById(R.id.username);
lockIcon=content.findViewById(R.id.lock_icon);
botIcon=content.findViewById(R.id.bot_icon);
bio=content.findViewById(R.id.bio);
followersCount=content.findViewById(R.id.followers_count);
followersLabel=content.findViewById(R.id.followers_label);
@@ -409,7 +414,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
followersBtn.setOnClickListener(this::onFollowersOrFollowingClick);
followingBtn.setOnClickListener(this::onFollowersOrFollowingClick);
username.setOnClickListener(v->{
content.findViewById(R.id.username_wrap).setOnClickListener(v->{
try {
new GetInstance()
.setCallback(new Callback<>(){
@@ -430,11 +435,11 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
.execRemote(Uri.parse(account.url).getHost());
} catch (NullPointerException ignored) {
// maybe the url was malformed?
Toast.makeText(getContext(), R.string.error, Toast.LENGTH_SHORT);
Toast.makeText(getContext(), R.string.error, Toast.LENGTH_SHORT).show();
}
});
username.setOnLongClickListener(v->{
content.findViewById(R.id.username_wrap).setOnLongClickListener(v->{
String usernameString=account.acct;
if(!usernameString.contains("@")){
usernameString+="@"+domain;
@@ -678,10 +683,14 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
}
}
@SuppressLint("SetTextI18n")
private void bindHeaderView(){
setTitle(account.displayName);
setSubtitle(getResources().getQuantityString(R.plurals.x_posts, (int)(account.statusesCount%1000), account.statusesCount));
ViewImageLoader.load(avatar, null, new UrlImageLoaderRequest(GlobalUserPreferences.playGifs ? account.avatar : account.avatarStatic, V.dp(100), V.dp(100)));
ViewImageLoader.load(avatar, null, new UrlImageLoaderRequest(
TextUtils.isEmpty(account.avatar) ? getSession().getDefaultAvatarUrl() :
GlobalUserPreferences.playGifs ? account.avatar : account.avatarStatic,
V.dp(100), V.dp(100)));
ViewImageLoader.load(cover, null, new UrlImageLoaderRequest(GlobalUserPreferences.playGifs ? account.header : account.headerStatic, 1000, 1000));
SpannableStringBuilder ssb=new SpannableStringBuilder(account.displayName);
if(AccountSessionManager.get(accountID).getLocalPreferences().customEmojiInNames)
@@ -710,36 +719,16 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
String acct = ((isSelf || account.isRemote)
? account.getFullyQualifiedName()
: account.acct);
if(account.locked){
ssb=new SpannableStringBuilder("@");
ssb.append(acct);
ssb.append(" ");
Drawable lock=username.getResources().getDrawable(R.drawable.ic_lock, getActivity().getTheme()).mutate();
lock.setBounds(0, 0, lock.getIntrinsicWidth(), lock.getIntrinsicHeight());
lock.setTint(username.getCurrentTextColor());
ssb.append(getString(R.string.manually_approves_followers), new ImageSpan(lock, ImageSpan.ALIGN_BASELINE), 0);
username.setText(ssb);
}else if(account.bot){
ssb=new SpannableStringBuilder("@");
ssb.append(account.acct);
if(isSelf){
ssb.append('@');
ssb.append(AccountSessionManager.getInstance().getAccount(accountID).domain);
}
ssb.append(" ");
Drawable botIcon=username.getResources().getDrawable(R.drawable.ic_bot, getActivity().getTheme()).mutate();
botIcon.setBounds(0, 0, botIcon.getIntrinsicWidth(), botIcon.getIntrinsicHeight());
botIcon.setTint(username.getCurrentTextColor());
ssb.append(getString(R.string.manually_approves_followers), new ImageSpan(botIcon, ImageSpan.ALIGN_BASELINE), 0);
username.setText(ssb);
}else{
// noinspection SetTextI18n
username.setText('@'+acct);
}
CharSequence parsedBio = null;
if(account.note != null){
parsedBio=HtmlParser.parse(account.note, account.emojis, Collections.emptyList(), Collections.emptyList(), accountID);
}
lockIcon.setVisibility(account.locked ? View.VISIBLE : View.GONE);
lockIcon.setImageTintList(ColorStateList.valueOf(username.getCurrentTextColor()));
botIcon.setVisibility(account.bot ? View.VISIBLE : View.GONE);
botIcon.setImageTintList(ColorStateList.valueOf(username.getCurrentTextColor()));
CharSequence parsedBio=HtmlParser.parse(account.note, account.emojis, Collections.emptyList(), Collections.emptyList(), accountID);
if(TextUtils.isEmpty(parsedBio)){
bio.setVisibility(View.GONE);
}else{
@@ -972,11 +961,12 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
notifyProgress.setIndeterminateTintList(notifyButton.getTextColors());
followsYouView.setVisibility(relationship.followedBy ? View.VISIBLE : View.GONE);
notifyButton.setSelected(relationship.notifying);
notifyButton.setContentDescription(getString(relationship.notifying ? R.string.sk_user_post_notifications_on : R.string.sk_user_post_notifications_off, '@'+account.username));
if (!isOwnProfile) {
setNote(relationship.note);
// aboutFragment.setNote(relationship.note, accountID, profileAccountID);
}
notifyButton.setContentDescription(getString(relationship.notifying ? R.string.sk_user_post_notifications_on : R.string.sk_user_post_notifications_off, '@'+account.username));
}
public ImageButton getFab() {
@@ -1169,7 +1159,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
actionButton.setText(R.string.save_changes);
pager.setVisibility(View.GONE);
tabbar.setVisibility(View.GONE);
Drawable overlay=getResources().getDrawable(R.drawable.edit_avatar_overlay).mutate();
Drawable overlay=getResources().getDrawable(R.drawable.edit_avatar_overlay, getActivity().getTheme()).mutate();
avatar.setForeground(overlay);
updateMetadataHeight();
@@ -1340,7 +1330,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
if(ava==null)
return;
int radius=V.dp(25);
currentPhotoViewer=new PhotoViewer(getActivity(), createFakeAttachments(account.avatar, ava), 0,
currentPhotoViewer=new PhotoViewer(getActivity(), createFakeAttachments(TextUtils.isEmpty(account.avatar) ? getSession().getDefaultAvatarUrl() : account.avatar, ava), 0,
new SingleImagePhotoViewerListener(avatar, avatarBorder, new int[]{radius, radius, radius, radius}, this, ()->currentPhotoViewer=null, ()->ava, null, null));
}
}
@@ -1541,16 +1531,14 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
}
}
private class AboutViewHolder extends BaseViewHolder implements ImageLoaderViewHolder {
private class AboutViewHolder extends BaseViewHolder implements ImageLoaderViewHolder{
private final TextView title;
private final LinkedTextView value;
// private final ImageView verifiedIcon;
public AboutViewHolder(){
super(R.layout.item_profile_about);
title=findViewById(R.id.title);
value=findViewById(R.id.value);
// verifiedIcon=findViewById(R.id.verified_icon);
}
@Override
@@ -1558,7 +1546,18 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
super.onBind(item);
title.setText(item.parsedName);
value.setText(item.parsedValue);
// verifiedIcon.setVisibility(item.verifiedAt!=null ? View.VISIBLE : View.GONE);
if(item.verifiedAt!=null){
int textColor=UiUtils.isDarkTheme() ? 0xFF89bb9c : 0xFF5b8e63;
value.setTextColor(textColor);
value.setLinkTextColor(textColor);
Drawable check=getResources().getDrawable(R.drawable.ic_fluent_checkmark_starburst_20_regular, getActivity().getTheme()).mutate();
check.setTint(textColor);
value.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, check, null);
}else{
value.setTextColor(UiUtils.getThemeColor(getActivity(), android.R.attr.textColorPrimary));
value.setLinkTextColor(UiUtils.getThemeColor(getActivity(), android.R.attr.colorAccent));
value.setCompoundDrawables(null, null, null, null);
}
}
@Override

View File

@@ -2,8 +2,11 @@ package org.joinmastodon.android.fragments;
import android.app.Activity;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowInsets;
import android.widget.ImageButton;
import com.squareup.otto.Subscribe;
@@ -27,6 +30,7 @@ import java.util.List;
import me.grishka.appkit.Nav;
import me.grishka.appkit.api.SimpleCallback;
import me.grishka.appkit.utils.V;
public class ScheduledStatusListFragment extends BaseStatusListFragment<ScheduledStatus> {
private String nextMaxID;
@@ -81,7 +85,10 @@ public class ScheduledStatusListFragment extends BaseStatusListFragment<Schedule
@Override
protected List<StatusDisplayItem> buildDisplayItems(ScheduledStatus s) {
return StatusDisplayItem.buildItems(this, s.toStatus(), accountID, s, knownAccounts, false, false, false, true, null);
return StatusDisplayItem.buildItems(this, s.toStatus(), accountID, s, knownAccounts, null,
StatusDisplayItem.FLAG_NO_EMOJI_REACTIONS |
StatusDisplayItem.FLAG_NO_FOOTER |
StatusDisplayItem.FLAG_NO_TRANSLATE);
}
@Override
@@ -184,6 +191,21 @@ public class ScheduledStatusListFragment extends BaseStatusListFragment<Schedule
return null;
}
@Override
public void onApplyWindowInsets(WindowInsets insets){
if(contentView!=null){
if(Build.VERSION.SDK_INT>=29 && insets.getTappableElementInsets().bottom==0){
int insetBottom=insets.getSystemWindowInsetBottom();
((ViewGroup.MarginLayoutParams) list.getLayoutParams()).bottomMargin=insetBottom;
((ViewGroup.MarginLayoutParams) fab.getLayoutParams()).bottomMargin=V.dp(16)+insetBottom;
insets=insets.inset(0, 0, 0, insetBottom);
}else{
((ViewGroup.MarginLayoutParams) fab.getLayoutParams()).bottomMargin=V.dp(16);
}
}
super.onApplyWindowInsets(insets);
}
@Override
public Uri getWebUri(Uri.Builder base) {
// TODO: adapt when frontends finally implement a scheduled posts list

View File

@@ -9,6 +9,7 @@ import org.joinmastodon.android.R;
import org.joinmastodon.android.api.requests.statuses.GetStatusEditHistory;
import org.joinmastodon.android.model.FilterContext;
import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.ui.displayitems.DummyStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.ReblogOrReplyLineStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
import org.joinmastodon.android.ui.utils.InsetStatusItemDecoration;
@@ -143,7 +144,8 @@ public class StatusEditHistoryFragment extends StatusListFragment{
}
}
String sep = getString(R.string.sk_separator);
items.add(0, new ReblogOrReplyLineStatusDisplayItem(s.id, this, action+" "+sep+" "+date, Collections.emptyList(), 0, null, null));
items.add(0, new ReblogOrReplyLineStatusDisplayItem(s.id, this, action+" "+sep+" "+date, Collections.emptyList(), 0, null, null, s));
items.add(1, new DummyStatusDisplayItem(s.id, this));
}
return items;
}

View File

@@ -1,5 +1,7 @@
package org.joinmastodon.android.fragments;
import static org.joinmastodon.android.api.session.AccountLocalPreferences.ShowEmojiReactions.ONLY_OPENED;
import android.content.res.Configuration;
import android.os.Bundle;
@@ -7,7 +9,9 @@ import com.squareup.otto.Subscribe;
import org.joinmastodon.android.E;
import org.joinmastodon.android.GlobalUserPreferences;
import org.joinmastodon.android.api.session.AccountLocalPreferences;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.events.EmojiReactionsUpdatedEvent;
import org.joinmastodon.android.MainActivity;
import org.joinmastodon.android.events.PollUpdatedEvent;
import org.joinmastodon.android.events.RemoveAccountPostsEvent;
@@ -21,6 +25,7 @@ import org.joinmastodon.android.ui.displayitems.EmojiReactionsStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.ExtendedFooterStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.FooterStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.TextStatusDisplayItem;
import org.joinmastodon.android.ui.utils.UiUtils;
import org.parceler.Parcels;
@@ -38,10 +43,13 @@ public abstract class StatusListFragment extends BaseStatusListFragment<Status>
protected List<StatusDisplayItem> buildDisplayItems(Status s){
boolean isMainThreadStatus = this instanceof ThreadFragment t && s.id.equals(t.mainStatus.id);
int flags = 0;
AccountLocalPreferences lp=getLocalPrefs();
if (GlobalUserPreferences.spectatorMode)
flags |= StatusDisplayItem.FLAG_NO_FOOTER;
if (!getLocalPrefs().showEmojiReactionsInLists)
if (!lp.emojiReactionsEnabled || lp.showEmojiReactions==ONLY_OPENED)
flags |= StatusDisplayItem.FLAG_NO_EMOJI_REACTIONS;
if(!GlobalUserPreferences.showMediaPreview)
flags |= StatusDisplayItem.FLAG_NO_MEDIA_PREVIEW;
return StatusDisplayItem.buildItems(this, s, accountID, s, knownAccounts, getFilterContext(), isMainThreadStatus ? 0 : flags);
}
@@ -237,8 +245,30 @@ public abstract class StatusListFragment extends BaseStatusListFragment<Status>
footer.rebind();
}else if(holder instanceof ExtendedFooterStatusDisplayItem.Holder footer && footer.getItem().status==s.getContentStatus()){
footer.rebind();
}else if(holder instanceof EmojiReactionsStatusDisplayItem.Holder reactions && reactions.getItem().status==s.getContentStatus() && ev.viewHolder!=holder){
}
}
}
}
for(Status s:preloadedData){
if(s.getContentStatus().id.equals(ev.id)){
s.getContentStatus().update(ev);
AccountSessionManager.get(accountID).getCacheController().updateStatus(s);
}
}
}
@Subscribe
public void onEmojiReactionsChanged(EmojiReactionsUpdatedEvent ev){
for(Status s:data){
if(s.getContentStatus().id.equals(ev.id)){
s.getContentStatus().update(ev);
AccountSessionManager.get(accountID).getCacheController().updateStatus(s);
for(int i=0;i<list.getChildCount();i++){
RecyclerView.ViewHolder holder=list.getChildViewHolder(list.getChildAt(i));
if(holder instanceof EmojiReactionsStatusDisplayItem.Holder reactions && reactions.getItem().status==s.getContentStatus() && ev.viewHolder!=holder){
reactions.rebind();
}else if(holder instanceof TextStatusDisplayItem.Holder text && text.getItem().parentID.equals(s.getID())){
text.rebind();
}
}
}

View File

@@ -16,6 +16,7 @@ import android.widget.TextView;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.requests.accounts.GetAccountRelationships;
import org.joinmastodon.android.api.requests.accounts.GetFollowSuggestions;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.fragments.IsOnTop;
import org.joinmastodon.android.fragments.MastodonRecyclerFragment;
import org.joinmastodon.android.fragments.ProfileFragment;
@@ -319,8 +320,9 @@ public class DiscoverAccountsFragment extends MastodonRecyclerFragment<DiscoverA
public AccountWrapper(Account account){
this.account=account;
if(!TextUtils.isEmpty(account.avatar))
avaRequest=new UrlImageLoaderRequest(account.avatar, V.dp(50), V.dp(50));
avaRequest=new UrlImageLoaderRequest(
TextUtils.isEmpty(account.avatar) ? AccountSessionManager.getInstance().getAccount(accountID).getDefaultAvatarUrl() : account.avatar,
V.dp(50), V.dp(50));
if(!TextUtils.isEmpty(account.header))
coverRequest=new UrlImageLoaderRequest(account.header, 1000, 1000);
parsedBio=HtmlParser.parse(account.note, account.emojis, Collections.emptyList(), Collections.emptyList(), accountID);

View File

@@ -12,6 +12,7 @@ import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.TextView;
import org.joinmastodon.android.GlobalUserPreferences;
import org.joinmastodon.android.R;
import org.joinmastodon.android.fragments.IsOnTop;
import org.joinmastodon.android.fragments.ScrollableToTop;
@@ -208,6 +209,11 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
@Override
public void scrollToTop(){
if(!searchActive){
if (((IsOnTop)getFragmentForPage(pager.getCurrentItem())).isOnTop() && GlobalUserPreferences.doubleTapToSwipe){
int nextPage=(pager.getCurrentItem()+1)%tabViews.length;
pager.setCurrentItem(nextPage, true);
return;
}
((ScrollableToTop)getFragmentForPage(pager.getCurrentItem())).scrollToTop();
}else{
searchFragment.scrollToTop();

View File

@@ -11,6 +11,7 @@ import android.widget.TextView;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.requests.trends.GetTrendingLinks;
import org.joinmastodon.android.fragments.IsOnTop;
import org.joinmastodon.android.fragments.ScrollableToTop;
import org.joinmastodon.android.model.Card;
import org.joinmastodon.android.model.viewmodel.CardViewModel;
@@ -43,7 +44,7 @@ import me.grishka.appkit.utils.SingleViewRecyclerAdapter;
import me.grishka.appkit.utils.V;
import me.grishka.appkit.views.UsableRecyclerView;
public class DiscoverNewsFragment extends BaseRecyclerFragment<CardViewModel> implements ScrollableToTop{
public class DiscoverNewsFragment extends BaseRecyclerFragment<CardViewModel> implements ScrollableToTop, IsOnTop{
private String accountID;
private DiscoverInfoBannerHelper bannerHelper;
private MergeRecyclerAdapter mergeAdapter;
@@ -111,6 +112,11 @@ public class DiscoverNewsFragment extends BaseRecyclerFragment<CardViewModel> im
smoothScrollRecyclerViewToTop(list);
}
@Override
public boolean isOnTop(){
return isRecyclerViewOnTop(list);
}
private class LinksAdapter extends UsableRecyclerView.Adapter<BaseLinkViewHolder> implements ImageLoaderRecyclerAdapter{
private final List<CardViewModel> data;

View File

@@ -70,7 +70,7 @@ public class SearchFragment extends BaseStatusListFragment<SearchResult>{
return switch(s.type){
case ACCOUNT -> Collections.singletonList(new AccountStatusDisplayItem(s.id, this, s.account));
case HASHTAG -> Collections.singletonList(new HashtagStatusDisplayItem(s.id, this, s.hashtag));
case STATUS -> StatusDisplayItem.buildItems(this, s.status, accountID, s, knownAccounts, FilterContext.PUBLIC, !getLocalPrefs().showEmojiReactionsInLists ? StatusDisplayItem.FLAG_NO_EMOJI_REACTIONS : 0);
case STATUS -> StatusDisplayItem.buildItems(this, s.status, accountID, s, knownAccounts, FilterContext.PUBLIC, 0);
};
}

View File

@@ -10,7 +10,6 @@ import android.graphics.Outline;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.text.TextUtils;
@@ -26,7 +25,7 @@ import android.widget.TextView;
import android.widget.Toast;
import android.widget.Toolbar;
import org.joinmastodon.android.MainActivity;
import org.joinmastodon.android.GlobalUserPreferences;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.requests.search.GetSearchResults;
import org.joinmastodon.android.api.session.AccountSessionManager;
@@ -38,19 +37,16 @@ import org.joinmastodon.android.model.SearchResults;
import org.joinmastodon.android.model.viewmodel.ListItem;
import org.joinmastodon.android.model.viewmodel.SearchResultViewModel;
import org.joinmastodon.android.ui.DividerItemDecoration;
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
import org.joinmastodon.android.ui.SearchViewHelper;
import org.joinmastodon.android.ui.adapters.GenericListItemsAdapter;
import org.joinmastodon.android.ui.utils.HideableSingleViewRecyclerAdapter;
import org.joinmastodon.android.ui.utils.UiUtils;
import org.joinmastodon.android.ui.viewholders.AccountViewHolder;
import org.joinmastodon.android.ui.viewholders.SimpleListItemViewHolder;
import org.parceler.Parcels;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -381,16 +377,54 @@ public class SearchQueryFragment extends MastodonRecyclerFragment<SearchResultVi
}
private void openHashtag(SearchResult res){
wrapSuicideDialog(()->{
UiUtils.openHashtagTimeline(getActivity(), accountID, res.hashtag.name, res.hashtag.following);
AccountSessionManager.getInstance().getAccount(accountID).getCacheController().putRecentSearch(res);
});
}
private boolean isInRecentMode(){
return TextUtils.isEmpty(currentQuery);
}
private void wrapSuicideDialog(Runnable r){
if(!GlobalUserPreferences.showSuicideHelp || currentQuery==null){
r.run();
return;
}
String[] terms=getContext().getString(R.string.sk_suicide_search_terms).toLowerCase().split(",");
String query=currentQuery.trim().toLowerCase();
boolean termMatches=false;
for(String term : terms){
if(query.contains(term)){
termMatches=true;
break;
}
}
if(!termMatches){
r.run();
return;
}
String url=getContext().getString(R.string.sk_suicide_helplines_url);
new M3AlertDialogBuilder(getActivity())
.setTitle(R.string.sk_search_suicide_title)
.setMessage(R.string.sk_search_suicide_message)
.setNegativeButton(R.string.sk_do_not_show_again, (dialog, which)->{
GlobalUserPreferences.showSuicideHelp = false;
GlobalUserPreferences.save();
r.run();
})
.setNeutralButton(R.string.sk_search_suicide_hotlines, (dialog, which)->UiUtils.launchWebBrowser(getContext(), url))
.setPositiveButton(R.string.ok, (dialog, which)->r.run())
.setOnDismissListener((dialog)->{})
.show();
}
private void onSearchViewEnter(){
deliverResult(currentQuery, null);
wrapSuicideDialog(()->deliverResult(currentQuery, null));
}
private void onOpenURLClick(){
@@ -398,10 +432,12 @@ public class SearchQueryFragment extends MastodonRecyclerFragment<SearchResultVi
}
private void onGoToHashtagClick(){
wrapSuicideDialog(()->{
String q=searchViewHelper.getQuery();
if(q.startsWith("#"))
q=q.substring(1);
UiUtils.openHashtagTimeline(getActivity(), accountID, q, null);
});
}
private void onGoToAccountClick(){
@@ -422,11 +458,11 @@ public class SearchQueryFragment extends MastodonRecyclerFragment<SearchResultVi
}
private void onGoToStatusSearchClick(){
deliverResult(searchViewHelper.getQuery(), SearchResult.Type.STATUS);
wrapSuicideDialog(()->deliverResult(searchViewHelper.getQuery(), SearchResult.Type.STATUS));
}
private void onGoToAccountSearchClick(){
deliverResult(searchViewHelper.getQuery(), SearchResult.Type.ACCOUNT);
wrapSuicideDialog(()->deliverResult(searchViewHelper.getQuery(), SearchResult.Type.ACCOUNT));
}
private void onClearRecentClick(){

View File

@@ -7,6 +7,7 @@ import android.widget.TextView;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.requests.trends.GetTrendingHashtags;
import org.joinmastodon.android.fragments.IsOnTop;
import org.joinmastodon.android.fragments.ScrollableToTop;
import org.joinmastodon.android.model.Hashtag;
import org.joinmastodon.android.ui.DividerItemDecoration;
@@ -23,7 +24,7 @@ import me.grishka.appkit.fragments.BaseRecyclerFragment;
import me.grishka.appkit.utils.BindableViewHolder;
import me.grishka.appkit.views.UsableRecyclerView;
public class TrendingHashtagsFragment extends BaseRecyclerFragment<Hashtag> implements ScrollableToTop{
public class TrendingHashtagsFragment extends BaseRecyclerFragment<Hashtag> implements ScrollableToTop, IsOnTop{
private String accountID;
public TrendingHashtagsFragment(){
@@ -58,6 +59,11 @@ public class TrendingHashtagsFragment extends BaseRecyclerFragment<Hashtag> impl
smoothScrollRecyclerViewToTop(list);
}
@Override
public boolean isOnTop(){
return isRecyclerViewOnTop(list);
}
private class HashtagsAdapter extends RecyclerView.Adapter<HashtagViewHolder>{
@NonNull
@Override

View File

@@ -83,7 +83,7 @@ public class ReportReasonChoiceFragment extends StatusListFragment{
reportStatus=Parcels.unwrap(getArguments().getParcelable("status"));
if(reportStatus!=null){
Status hiddenStatus=reportStatus.clone();
hiddenStatus.spoilerText=getString(R.string.post_hidden);
if(hiddenStatus.spoilerText==null) hiddenStatus.spoilerText=getString(R.string.post_hidden);
onDataLoaded(Collections.singletonList(hiddenStatus));
setTitle(R.string.report_title_post);
}else{
@@ -168,17 +168,6 @@ public class ReportReasonChoiceFragment extends StatusListFragment{
((UsableRecyclerView)list).setIncludeMarginsInItemHitbox(false);
if(reportStatus!=null){
list.addItemDecoration(new RecyclerView.ItemDecoration(){
@Override
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state){
RecyclerView.ViewHolder holder=parent.getChildViewHolder(view);
if(holder instanceof LinkCardStatusDisplayItem.Holder || holder instanceof MediaGridStatusDisplayItem.Holder){
outRect.left=V.dp(16);
outRect.right=V.dp(16);
}
}
});
list.addItemDecoration(new RecyclerView.ItemDecoration(){
private Paint paint=new Paint(Paint.ANTI_ALIAS_FLAG);
{
@@ -222,10 +211,6 @@ public class ReportReasonChoiceFragment extends StatusListFragment{
if(holder instanceof StatusDisplayItem.Holder<?>){
outRect.left=outRect.right=V.dp(16);
}
int index=holder.getAbsoluteAdapterPosition()-mergeAdapter.getPositionForAdapter(adapter);
if(index==displayItems.size()){
outRect.top=V.dp(32);
}
}
});
}
@@ -251,18 +236,6 @@ public class ReportReasonChoiceFragment extends StatusListFragment{
return null;
}
@Override
protected void onModifyItemViewHolder(BindableViewHolder<StatusDisplayItem> holder){
if((Object)holder instanceof MediaGridStatusDisplayItem.Holder h){
View layout=h.getLayout();
layout.setOutlineProvider(OutlineProviders.roundedRect(8));
layout.setClipToOutline(true);
View overlay=h.getSensitiveOverlay();
overlay.setOutlineProvider(OutlineProviders.roundedRect(8));
overlay.setClipToOutline(true);
}
}
@Override
public void putRelationship(String id, Relationship rel){
super.putRelationship(id, rel);

View File

@@ -12,10 +12,13 @@ import org.joinmastodon.android.R;
import org.joinmastodon.android.api.MastodonAPIController;
import org.joinmastodon.android.api.session.AccountSession;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.fragments.HasAccountID;
import org.joinmastodon.android.model.viewmodel.ListItem;
import org.joinmastodon.android.ui.utils.UiUtils;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import androidx.recyclerview.widget.RecyclerView;
import me.grishka.appkit.imageloader.ImageCache;
@@ -23,9 +26,11 @@ import me.grishka.appkit.utils.MergeRecyclerAdapter;
import me.grishka.appkit.utils.SingleViewRecyclerAdapter;
import me.grishka.appkit.utils.V;
public class SettingsAboutAppFragment extends BaseSettingsFragment<Void>{
public class SettingsAboutAppFragment extends BaseSettingsFragment<Void> implements HasAccountID{
private ListItem<Void> mediaCacheItem;
// MOSHIDON
private ListItem<Void> clearRecentEmojisItem;
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
@@ -36,6 +41,7 @@ public class SettingsAboutAppFragment extends BaseSettingsFragment<Void>{
new ListItem<>(R.string.mo_settings_contribute, 0, R.drawable.ic_fluent_open_24_regular, ()->UiUtils.launchWebBrowser(getActivity(), getString(R.string.mo_repo_url))),
new ListItem<>(R.string.settings_tos, 0, R.drawable.ic_fluent_open_24_regular, ()->UiUtils.launchWebBrowser(getActivity(), "https://"+s.domain+"/terms")),
new ListItem<>(R.string.settings_privacy_policy, 0, R.drawable.ic_fluent_open_24_regular, ()->UiUtils.launchWebBrowser(getActivity(), getString(R.string.privacy_policy_url)), 0, true),
clearRecentEmojisItem=new ListItem<>(R.string.mo_clear_recent_emoji, 0, this::onClearRecentEmojisClick),
mediaCacheItem=new ListItem<>(R.string.settings_clear_cache, 0, this::onClearMediaCacheClick)
));
@@ -51,7 +57,6 @@ public class SettingsAboutAppFragment extends BaseSettingsFragment<Void>{
adapter.addAdapter(super.getAdapter());
TextView versionInfo=new TextView(getActivity());
versionInfo.setSingleLine();
versionInfo.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, V.dp(32)));
versionInfo.setTextAppearance(R.style.m3_label_medium);
versionInfo.setTextColor(UiUtils.getThemeColor(getActivity(), R.attr.colorM3Outline));
@@ -73,10 +78,21 @@ public class SettingsAboutAppFragment extends BaseSettingsFragment<Void>{
});
}
private void onClearRecentEmojisClick(){
getLocalPrefs().recentEmojis=new HashMap<>();
getLocalPrefs().save();
Toast.makeText(getContext(), R.string.mo_recent_emoji_cleared, Toast.LENGTH_SHORT).show();
}
private void updateMediaCacheItem(){
long size=ImageCache.getInstance(getActivity()).getDiskCache().size();
mediaCacheItem.subtitle=UiUtils.formatFileSize(getActivity(), size, false);
mediaCacheItem.isEnabled=size>0;
rebindItem(mediaCacheItem);
}
@Override
public String getAccountID(){
return accountID;
}
}

View File

@@ -1,7 +1,6 @@
package org.joinmastodon.android.fragments.settings;
import android.os.Bundle;
import android.provider.Settings;
import androidx.annotation.StringRes;
@@ -21,6 +20,7 @@ import org.joinmastodon.android.utils.MastodonLanguage;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.IntStream;
import java.util.stream.Stream;
public class SettingsBehaviorFragment extends BaseSettingsFragment<Void> implements HasAccountID{
private ListItem<Void> languageItem;
@@ -34,7 +34,7 @@ public class SettingsBehaviorFragment extends BaseSettingsFragment<Void> impleme
private CheckableListItem<Void> forwardReportsItem, remoteLoadingItem, showBoostsItem, showRepliesItem, loadNewPostsItem, seeNewPostsBtnItem, overlayMediaItem;
// MOSHIDON
private CheckableListItem<Void> mentionRebloggerAutomaticallyItem, hapticFeedbackItem, unlistedRepliesItem;
private CheckableListItem<Void> mentionRebloggerAutomaticallyItem, hapticFeedbackItem, unlistedRepliesItem, showPostsWithoutAltItem;
@Override
public void onCreate(Bundle savedInstanceState){
@@ -50,6 +50,7 @@ public class SettingsBehaviorFragment extends BaseSettingsFragment<Void> impleme
List<ListItem<Void>> items = new ArrayList<>(List.of(
languageItem=new ListItem<>(getString(R.string.default_post_language), postLanguage!=null ? postLanguage.getDisplayName(getContext()) : null, R.drawable.ic_fluent_local_language_24_regular, this::onDefaultLanguageClick),
altTextItem=new CheckableListItem<>(R.string.settings_alt_text_reminders, 0, CheckableListItem.Style.SWITCH, GlobalUserPreferences.altTextReminders, R.drawable.ic_fluent_image_alt_text_24_regular, ()->toggleCheckableItem(altTextItem)),
showPostsWithoutAltItem=new CheckableListItem<>(R.string.mo_settings_show_posts_without_alt, R.string.mo_settings_show_posts_without_alt_summary, CheckableListItem.Style.SWITCH, GlobalUserPreferences.showPostsWithoutAlt, R.drawable.ic_fluent_eye_tracking_on_24_regular, ()->toggleCheckableItem(showPostsWithoutAltItem)),
playGifsItem=new CheckableListItem<>(R.string.settings_gif, R.string.mo_setting_play_gif_summary, CheckableListItem.Style.SWITCH, GlobalUserPreferences.playGifs, R.drawable.ic_fluent_gif_24_regular, ()->toggleCheckableItem(playGifsItem)),
unlistedRepliesItem=new CheckableListItem<>(R.string.mo_change_default_reply_visibility_to_unlisted, R.string.mo_setting_default_reply_privacy_summary, CheckableListItem.Style.SWITCH, GlobalUserPreferences.defaultToUnlistedReplies, R.drawable.ic_fluent_lock_open_24_regular, ()->toggleCheckableItem(unlistedRepliesItem)),
overlayMediaItem=new CheckableListItem<>(R.string.sk_settings_continues_playback, R.string.sk_settings_continues_playback_summary, CheckableListItem.Style.SWITCH, GlobalUserPreferences.overlayMedia, R.drawable.ic_fluent_play_circle_hint_24_regular, ()->toggleCheckableItem(overlayMediaItem)),
@@ -173,7 +174,7 @@ public class SettingsBehaviorFragment extends BaseSettingsFragment<Void> impleme
GlobalUserPreferences.overlayMedia=overlayMediaItem.checked;
GlobalUserPreferences.useCustomTabs=customTabsItem.checked;
GlobalUserPreferences.altTextReminders=altTextItem.checked;
GlobalUserPreferences.confirmUnfollow=customTabsItem.checked;
GlobalUserPreferences.confirmUnfollow=confirmUnfollowItem.checked;
GlobalUserPreferences.confirmBoost=confirmBoostItem.checked;
GlobalUserPreferences.confirmDeletePost=confirmDeleteItem.checked;
GlobalUserPreferences.forwardReportDefault=forwardReportsItem.checked;
@@ -183,6 +184,7 @@ public class SettingsBehaviorFragment extends BaseSettingsFragment<Void> impleme
GlobalUserPreferences.mentionRebloggerAutomatically=mentionRebloggerAutomaticallyItem.checked;
GlobalUserPreferences.hapticFeedback=hapticFeedbackItem.checked;
GlobalUserPreferences.defaultToUnlistedReplies=unlistedRepliesItem.checked;
GlobalUserPreferences.showPostsWithoutAlt=showPostsWithoutAltItem.checked;
GlobalUserPreferences.save();
AccountLocalPreferences lp=getLocalPrefs();
lp.showBoosts=showBoostsItem.checked;

View File

@@ -43,7 +43,7 @@ public class SettingsDisplayFragment extends BaseSettingsFragment<Void>{
private CheckableListItem<Void> pronounsInUserListingsItem, pronounsInTimelinesItem, pronounsInThreadsItem;
// MOSHIDON
private CheckableListItem<Void> enableDoubleTapToSwipeItem, relocatePublishButtonItem;
private CheckableListItem<Void> enableDoubleTapToSwipeItem, relocatePublishButtonItem, showPostDividersItem, enableDoubleTapToSearchItem, showMediaPreviewItem;
private AccountLocalPreferences lp;
@@ -62,10 +62,12 @@ public class SettingsDisplayFragment extends BaseSettingsFragment<Void>{
relocatePublishButtonItem=new CheckableListItem<>(R.string.mo_relocate_publish_button, R.string.mo_setting_relocate_publish_summary, CheckableListItem.Style.SWITCH, GlobalUserPreferences.relocatePublishButton, R.drawable.ic_fluent_arrow_autofit_down_24_regular, ()->toggleCheckableItem(relocatePublishButtonItem)),
revealCWsItem=new CheckableListItem<>(R.string.sk_settings_always_reveal_content_warnings, 0, CheckableListItem.Style.SWITCH, lp.revealCWs, R.drawable.ic_fluent_chat_warning_24_regular, ()->toggleCheckableItem(revealCWsItem)),
hideSensitiveMediaItem=new CheckableListItem<>(R.string.settings_hide_sensitive_media, 0, CheckableListItem.Style.SWITCH, lp.hideSensitiveMedia, R.drawable.ic_fluent_flag_24_regular, ()->toggleCheckableItem(hideSensitiveMediaItem)),
showMediaPreviewItem=new CheckableListItem<>(R.string.mo_show_media_preview, 0, CheckableListItem.Style.SWITCH, GlobalUserPreferences.showMediaPreview, R.drawable.ic_fluent_image_24_regular, ()->toggleCheckableItem(showMediaPreviewItem)),
interactionCountsItem=new CheckableListItem<>(R.string.settings_show_interaction_counts, R.string.mo_setting_interaction_count_summary, CheckableListItem.Style.SWITCH, lp.showInteractionCounts, R.drawable.ic_fluent_number_row_24_regular, ()->toggleCheckableItem(interactionCountsItem)),
emojiInNamesItem=new CheckableListItem<>(R.string.settings_show_emoji_in_names, 0, CheckableListItem.Style.SWITCH, lp.customEmojiInNames, R.drawable.ic_fluent_emoji_24_regular, ()->toggleCheckableItem(emojiInNamesItem)),
marqueeItem=new CheckableListItem<>(R.string.sk_settings_enable_marquee, R.string.mo_setting_marquee_summary, CheckableListItem.Style.SWITCH, GlobalUserPreferences.toolbarMarquee, R.drawable.ic_fluent_text_more_24_regular, ()->toggleCheckableItem(marqueeItem)),
reduceMotionItem=new CheckableListItem<>(R.string.sk_settings_reduce_motion, R.string.mo_setting_reduced_motion_summary, CheckableListItem.Style.SWITCH, GlobalUserPreferences.reduceMotion, R.drawable.ic_fluent_star_emphasis_24_regular, ()->toggleCheckableItem(reduceMotionItem)),
enableDoubleTapToSearchItem=new CheckableListItem<>(R.string.mo_double_tap_to_search, 0, CheckableListItem.Style.SWITCH, GlobalUserPreferences.doubleTapToSearch, R.drawable.ic_fluent_search_24_regular, ()->toggleCheckableItem(enableDoubleTapToSearchItem)),
disableSwipeItem=new CheckableListItem<>(R.string.sk_settings_tabs_disable_swipe, R.string.mo_setting_disable_swipe_summary, CheckableListItem.Style.SWITCH, GlobalUserPreferences.disableSwipe, R.drawable.ic_fluent_swipe_right_24_regular, ()->toggleCheckableItem(disableSwipeItem)),
enableDoubleTapToSwipeItem=new CheckableListItem<>(R.string.mo_double_tap_to_swipe_between_tabs, 0, CheckableListItem.Style.SWITCH, GlobalUserPreferences.doubleTapToSwipe, R.drawable.ic_fluent_double_tap_swipe_right_24_regular, ()->toggleCheckableItem(enableDoubleTapToSwipeItem)),
altIndicatorItem=new CheckableListItem<>(R.string.sk_settings_show_alt_indicator, 0, CheckableListItem.Style.SWITCH, GlobalUserPreferences.showAltIndicator, R.drawable.ic_fluent_scan_text_24_regular, ()->toggleCheckableItem(altIndicatorItem)),
@@ -74,6 +76,7 @@ public class SettingsDisplayFragment extends BaseSettingsFragment<Void>{
spectatorModeItem=new CheckableListItem<>(R.string.sk_settings_hide_interaction, 0, CheckableListItem.Style.SWITCH, GlobalUserPreferences.spectatorMode, R.drawable.ic_fluent_star_off_24_regular, ()->toggleCheckableItem(spectatorModeItem)),
hideFabItem=new CheckableListItem<>(R.string.sk_settings_hide_fab, 0, CheckableListItem.Style.SWITCH, GlobalUserPreferences.autoHideFab, R.drawable.ic_fluent_edit_24_regular, ()->toggleCheckableItem(hideFabItem)),
translateOpenedItem=new CheckableListItem<>(R.string.sk_settings_translate_only_opened, 0, CheckableListItem.Style.SWITCH, GlobalUserPreferences.translateButtonOpenedOnly, R.drawable.ic_fluent_translate_24_regular, ()->toggleCheckableItem(translateOpenedItem)),
showPostDividersItem=new CheckableListItem<>(R.string.mo_enable_dividers, 0, CheckableListItem.Style.SWITCH, GlobalUserPreferences.showDividers, R.drawable.ic_fluent_timeline_24_regular, ()->toggleCheckableItem(showPostDividersItem)),
disablePillItem=new CheckableListItem<>(R.string.sk_disable_pill_shaped_active_indicator, 0, CheckableListItem.Style.SWITCH, GlobalUserPreferences.disableM3PillActiveIndicator, R.drawable.ic_fluent_pill_24_regular, ()->toggleCheckableItem(disablePillItem)),
showNavigationLabelsItem=new CheckableListItem<>(R.string.sk_settings_show_labels_in_navigation_bar, 0, CheckableListItem.Style.SWITCH, GlobalUserPreferences.showNavigationLabels, R.drawable.ic_fluent_tag_24_regular, ()->toggleCheckableItem(showNavigationLabelsItem), true),
pronounsInTimelinesItem=new CheckableListItem<>(R.string.sk_settings_display_pronouns_in_timelines, 0, CheckableListItem.Style.CHECKBOX, GlobalUserPreferences.displayPronounsInTimelines, 0, ()->toggleCheckableItem(pronounsInTimelinesItem)),
@@ -102,7 +105,9 @@ public class SettingsDisplayFragment extends BaseSettingsFragment<Void>{
boolean restartPlease=
GlobalUserPreferences.disableM3PillActiveIndicator!=disablePillItem.checked ||
GlobalUserPreferences.showNavigationLabels!=showNavigationLabelsItem.checked;
GlobalUserPreferences.showNavigationLabels!=showNavigationLabelsItem.checked ||
GlobalUserPreferences.showMediaPreview !=showMediaPreviewItem.checked ||
GlobalUserPreferences.showDividers!=showPostDividersItem.checked;
lp.revealCWs=revealCWsItem.checked;
lp.hideSensitiveMedia=hideSensitiveMediaItem.checked;
@@ -113,6 +118,7 @@ public class SettingsDisplayFragment extends BaseSettingsFragment<Void>{
GlobalUserPreferences.relocatePublishButton=relocatePublishButtonItem.checked;
GlobalUserPreferences.reduceMotion=reduceMotionItem.checked;
GlobalUserPreferences.disableSwipe=disableSwipeItem.checked;
GlobalUserPreferences.doubleTapToSearch=enableDoubleTapToSearchItem.checked;
GlobalUserPreferences.doubleTapToSwipe=enableDoubleTapToSwipeItem.checked;
GlobalUserPreferences.showAltIndicator=altIndicatorItem.checked;
GlobalUserPreferences.showNoAltIndicator=noAltIndicatorItem.checked;
@@ -120,11 +126,13 @@ public class SettingsDisplayFragment extends BaseSettingsFragment<Void>{
GlobalUserPreferences.spectatorMode=spectatorModeItem.checked;
GlobalUserPreferences.autoHideFab=hideFabItem.checked;
GlobalUserPreferences.translateButtonOpenedOnly=translateOpenedItem.checked;
GlobalUserPreferences.showDividers=showPostDividersItem.checked;
GlobalUserPreferences.disableM3PillActiveIndicator=disablePillItem.checked;
GlobalUserPreferences.showNavigationLabels=showNavigationLabelsItem.checked;
GlobalUserPreferences.displayPronounsInTimelines=pronounsInTimelinesItem.checked;
GlobalUserPreferences.displayPronounsInThreads=pronounsInThreadsItem.checked;
GlobalUserPreferences.displayPronounsInUserListings=pronounsInUserListingsItem.checked;
GlobalUserPreferences.showMediaPreview=showMediaPreviewItem.checked;
GlobalUserPreferences.save();
if(restartPlease) restartActivityToApplyNewTheme();
else E.post(new StatusDisplaySettingsChangedEvent(accountID));

View File

@@ -2,10 +2,14 @@ package org.joinmastodon.android.fragments.settings;
import android.os.Bundle;
import androidx.annotation.StringRes;
import org.joinmastodon.android.E;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.session.AccountLocalPreferences;
import org.joinmastodon.android.api.session.AccountSession;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.events.StatusDisplaySettingsChangedEvent;
import org.joinmastodon.android.fragments.HasAccountID;
import org.joinmastodon.android.model.ContentType;
import org.joinmastodon.android.model.viewmodel.CheckableListItem;
@@ -15,12 +19,13 @@ import org.joinmastodon.android.ui.utils.UiUtils;
import java.util.Arrays;
import java.util.List;
import java.util.stream.IntStream;
import me.grishka.appkit.Nav;
public class SettingsInstanceFragment extends BaseSettingsFragment<Void> implements HasAccountID{
private CheckableListItem<Void> contentTypesItem, emojiReactionsItem, emojiReactionsInListsItem, localOnlyItem, glitchModeItem;
private ListItem<Void> defaultContentTypeItem;
private CheckableListItem<Void> contentTypesItem, emojiReactionsItem, localOnlyItem, glitchModeItem;
private ListItem<Void> defaultContentTypeItem, showEmojiReactionsItem;
private AccountLocalPreferences lp;
@Override
@@ -35,16 +40,16 @@ public class SettingsInstanceFragment extends BaseSettingsFragment<Void> impleme
new ListItem<>(R.string.sk_settings_posting, 0, R.drawable.ic_fluent_open_24_regular, ()->UiUtils.launchWebBrowser(getActivity(), "https://"+s.domain+"/settings/preferences/other")),
new ListItem<>(R.string.sk_settings_auth, 0, R.drawable.ic_fluent_open_24_regular, ()->UiUtils.launchWebBrowser(getActivity(), "https://"+s.domain+"/auth/edit"), 0, true),
contentTypesItem=new CheckableListItem<>(R.string.sk_settings_content_types, R.string.sk_settings_content_types_explanation, CheckableListItem.Style.SWITCH, lp.contentTypesEnabled, R.drawable.ic_fluent_text_edit_style_24_regular, this::onContentTypeClick),
defaultContentTypeItem=new ListItem<>(R.string.sk_settings_default_content_type, lp.defaultContentType.getName(), R.drawable.ic_fluent_text_bold_24_regular, this::onDefaultContentTypeClick),
defaultContentTypeItem=new ListItem<>(R.string.sk_settings_default_content_type, lp.defaultContentType.getName(), R.drawable.ic_fluent_text_bold_24_regular, this::onDefaultContentTypeClick, 0, true),
emojiReactionsItem=new CheckableListItem<>(R.string.sk_settings_emoji_reactions, R.string.sk_settings_emoji_reactions_explanation, CheckableListItem.Style.SWITCH, lp.emojiReactionsEnabled, R.drawable.ic_fluent_emoji_laugh_24_regular, this::onEmojiReactionsClick),
emojiReactionsInListsItem=new CheckableListItem<>(R.string.sk_settings_emoji_reactions_in_lists, R.string.sk_settings_emoji_reactions_in_lists_explanation, CheckableListItem.Style.SWITCH, lp.showEmojiReactionsInLists, R.drawable.ic_fluent_emoji_24_regular, ()->toggleCheckableItem(emojiReactionsInListsItem)),
showEmojiReactionsItem=new ListItem<>(R.string.sk_settings_show_emoji_reactions, getShowEmojiReactionsString(), R.drawable.ic_fluent_emoji_24_regular, this::onShowEmojiReactionsClick, 0, true),
localOnlyItem=new CheckableListItem<>(R.string.sk_settings_support_local_only, R.string.sk_settings_local_only_explanation, CheckableListItem.Style.SWITCH, lp.localOnlySupported, R.drawable.ic_fluent_eye_24_regular, this::onLocalOnlyClick),
glitchModeItem=new CheckableListItem<>(R.string.sk_settings_glitch_instance, R.string.sk_settings_glitch_mode_explanation, CheckableListItem.Style.SWITCH, lp.glitchInstance, R.drawable.ic_fluent_eye_24_filled, ()->toggleCheckableItem(glitchModeItem))
));
contentTypesItem.checkedChangeListener=checked->onContentTypeClick();
defaultContentTypeItem.isEnabled=contentTypesItem.checked;
emojiReactionsItem.checkedChangeListener=checked->onEmojiReactionsClick();
emojiReactionsInListsItem.isEnabled=emojiReactionsItem.checked;
showEmojiReactionsItem.isEnabled=emojiReactionsItem.checked;
localOnlyItem.checkedChangeListener=checked->onLocalOnlyClick();
glitchModeItem.isEnabled=localOnlyItem.checked;
}
@@ -57,10 +62,10 @@ public class SettingsInstanceFragment extends BaseSettingsFragment<Void> impleme
super.onHidden();
lp.contentTypesEnabled=contentTypesItem.checked;
lp.emojiReactionsEnabled=emojiReactionsItem.checked;
lp.showEmojiReactionsInLists=emojiReactionsInListsItem.checked;
lp.localOnlySupported=localOnlyItem.checked;
lp.glitchInstance=glitchModeItem.checked;
lp.save();
E.post(new StatusDisplaySettingsChangedEvent(accountID));
}
private void onServerClick(){
@@ -107,11 +112,34 @@ public class SettingsInstanceFragment extends BaseSettingsFragment<Void> impleme
.show();
}
private void onShowEmojiReactionsClick(){
int selected=lp.showEmojiReactions.ordinal();
int[] newSelected={selected};
new M3AlertDialogBuilder(getActivity())
.setTitle(R.string.sk_settings_show_emoji_reactions)
.setSingleChoiceItems((String[]) IntStream.of(R.string.sk_settings_show_emoji_reactions_hide_empty, R.string.sk_settings_show_emoji_reactions_only_opened, R.string.sk_settings_show_emoji_reactions_always).mapToObj(this::getString).toArray(String[]::new),
selected, (dlg, item)->newSelected[0]=item)
.setPositiveButton(R.string.ok, (dlg, item)->{
lp.showEmojiReactions=AccountLocalPreferences.ShowEmojiReactions.values()[newSelected[0]];
showEmojiReactionsItem.subtitleRes=getShowEmojiReactionsString();
rebindItem(showEmojiReactionsItem);
})
.setNegativeButton(R.string.cancel, null)
.show();
}
private @StringRes int getShowEmojiReactionsString(){
return switch(lp.showEmojiReactions){
case HIDE_EMPTY -> R.string.sk_settings_show_emoji_reactions_hide_empty;
case ONLY_OPENED -> R.string.sk_settings_show_emoji_reactions_only_opened;
case ALWAYS -> R.string.sk_settings_show_emoji_reactions_always;
};
}
private void onEmojiReactionsClick(){
toggleCheckableItem(emojiReactionsItem);
emojiReactionsInListsItem.checked=false;
emojiReactionsInListsItem.isEnabled=emojiReactionsItem.checked;
rebindItem(emojiReactionsInListsItem);
showEmojiReactionsItem.isEnabled=emojiReactionsItem.checked;
rebindItem(showEmojiReactionsItem);
}
private void onLocalOnlyClick(){

View File

@@ -152,6 +152,7 @@ public class SettingsMainFragment extends BaseSettingsFragment<Void>{
private void onLogOutClick(){
AccountSession session=AccountSessionManager.getInstance().getAccount(accountID);
new M3AlertDialogBuilder(getActivity())
.setTitle(R.string.log_out)
.setMessage(getString(R.string.confirm_log_out, session.getFullUsername()))
.setPositiveButton(R.string.log_out, (dialog, which)->account.logOut(getActivity(), ()->{
loggedOut=true;

View File

@@ -7,12 +7,17 @@ import androidx.annotation.Nullable;
import org.joinmastodon.android.api.ObjectValidationException;
import org.joinmastodon.android.api.RequiredField;
import org.joinmastodon.android.api.requests.instance.GetInstance;
import org.parceler.Parcel;
import java.time.Instant;
import java.time.LocalDate;
import java.util.Collections;
import java.util.List;
import me.grishka.appkit.api.Callback;
import me.grishka.appkit.api.ErrorResponse;
/**
* Represents a user of Mastodon and their associated profile.
*/
@@ -23,22 +28,22 @@ public class Account extends BaseModel implements Searchable{
/**
* The account id
*/
@RequiredField
// @RequiredField
public String id;
/**
* The username of the account, not including domain.
*/
@RequiredField
// @RequiredField
public String username;
/**
* The Webfinger account URI. Equal to username for local users, or username@domain for remote users.
*/
@RequiredField
// @RequiredField
public String acct;
/**
* The location of the user's profile page.
*/
@RequiredField
// @RequiredField
public String url;
// Display attributes
@@ -51,12 +56,12 @@ public class Account extends BaseModel implements Searchable{
/**
* The profile's bio / description.
*/
// @RequiredField
public String note;
/**
* An image icon that is shown next to statuses and in the profile.
*/
@RequiredField
// @RequiredField
public String avatar;
/**
* A static version of the avatar. Equal to avatar if its value is a static image; different if avatar is an animated GIF.
@@ -157,16 +162,26 @@ public class Account extends BaseModel implements Searchable{
if(fields!=null){
for(AccountField f:fields)
f.postprocess();
} else {
fields = Collections.emptyList();
}
if(emojis!=null){
for(Emoji e:emojis)
e.postprocess();
} else {
emojis = Collections.emptyList();
}
if(moved!=null)
moved.postprocess();
if(TextUtils.isEmpty(displayName))
displayName=username;
if(fqn == null) fqn = getFullyQualifiedName();
if(id == null) id = "";
if(username == null) username = "";
if(TextUtils.isEmpty(displayName))
displayName = !TextUtils.isEmpty(username) ? username : "";
if(acct == null) acct = "";
if(url == null) url = "";
if(note == null) note = "";
if(avatar == null) avatar = "";
}
public boolean isLocal(){
@@ -191,6 +206,8 @@ public class Account extends BaseModel implements Searchable{
}
public String getFullyQualifiedName() {
if (TextUtils.isEmpty(acct))
return "";
return fqn != null ? fqn : acct.split("@")[0] + "@" + getDomainFromURL();
}

View File

@@ -0,0 +1,19 @@
package org.joinmastodon.android.model;
import org.jsoup.internal.StringUtil;
import java.util.EnumSet;
public class AltTextFilter extends LegacyFilter {
public AltTextFilter(FilterAction filterAction, FilterContext firstContext, FilterContext... restContexts) {
this.filterAction = filterAction;
isRemote = false;
context = EnumSet.of(firstContext, restContexts);
}
@Override
public boolean matches(Status status) {
return status.getContentStatus().mediaAttachments.stream().map(attachment -> attachment.description).anyMatch(StringUtil::isBlank);
}
}

View File

@@ -41,6 +41,12 @@ public class Emoji extends BaseModel{
this.staticUrl = staticUrl;
}
public String getUrl(boolean playGifs){
String idealUrl=playGifs ? url : staticUrl;
if(idealUrl==null) return url==null ? staticUrl : url;
return idealUrl;
}
@Override
public String toString(){
return "Emoji{"+

View File

@@ -1,5 +1,6 @@
package org.joinmastodon.android.model;
import org.joinmastodon.android.GlobalUserPreferences;
import org.parceler.Parcel;
import java.util.ArrayList;
@@ -22,6 +23,12 @@ public class EmojiReaction {
public transient ImageLoaderRequest request;
public String getUrl(boolean playGifs){
String idealUrl=playGifs ? url : staticUrl;
if(idealUrl==null) return url==null ? staticUrl : url;
return idealUrl;
}
public static EmojiReaction of(Emoji info, Account me){
EmojiReaction reaction=new EmojiReaction();
reaction.me=true;

View File

@@ -15,10 +15,10 @@ import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import org.joinmastodon.android.GlobalUserPreferences;
import org.joinmastodon.android.api.ObjectValidationException;
import org.joinmastodon.android.api.RequiredField;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.events.EmojiReactionsUpdatedEvent;
import org.joinmastodon.android.events.StatusCountersUpdatedEvent;
import org.joinmastodon.android.ui.text.HtmlParser;
import org.parceler.Parcel;
@@ -120,8 +120,8 @@ public class Status extends BaseModel implements DisplayItemsParent, Searchable{
for(FilterResult fr:filtered)
fr.postprocess();
if(!TextUtils.isEmpty(spoilerText)) sensitive=true;
spoilerRevealed=TextUtils.isEmpty(spoilerText);
spoilerRevealed=!hasSpoiler();
if(!spoilerRevealed) sensitive=true;
sensitiveRevealed=!sensitive;
if(visibility.equals(StatusPrivacy.LOCAL)) localOnly=true;
if(emojiReactions!=null) reactions=emojiReactions;
@@ -181,6 +181,9 @@ public class Status extends BaseModel implements DisplayItemsParent, Searchable{
reblogged=ev.reblogged;
bookmarked=ev.bookmarked;
pinned=ev.pinned;
}
public void update(EmojiReactionsUpdatedEvent ev){
reactions=ev.reactions;
}
@@ -194,6 +197,10 @@ public class Status extends BaseModel implements DisplayItemsParent, Searchable{
return strippedText;
}
public boolean hasSpoiler(){
return !TextUtils.isEmpty(spoilerText);
}
@NonNull
@Override
public Status clone(){

View File

@@ -1,8 +1,10 @@
package org.joinmastodon.android.model.viewmodel;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import org.joinmastodon.android.GlobalUserPreferences;
import org.joinmastodon.android.api.session.AccountSession;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.model.Account;
import org.joinmastodon.android.model.AccountField;
@@ -24,9 +26,13 @@ public class AccountViewModel{
public AccountViewModel(Account account, String accountID){
this.account=account;
avaRequest=new UrlImageLoaderRequest(GlobalUserPreferences.playGifs ? account.avatar : account.avatarStatic, V.dp(50), V.dp(50));
AccountSession session = AccountSessionManager.get(accountID);
avaRequest=new UrlImageLoaderRequest(
TextUtils.isEmpty(account.avatar) ? session.getDefaultAvatarUrl() :
GlobalUserPreferences.playGifs ? account.avatar : account.avatarStatic,
V.dp(50), V.dp(50));
emojiHelper=new CustomEmojiHelper();
if(AccountSessionManager.get(accountID).getLocalPreferences().customEmojiInNames)
if(session.getLocalPreferences().customEmojiInNames)
parsedName=HtmlParser.parseCustomEmoji(account.displayName, account.emojis);
else
parsedName=account.displayName;

View File

@@ -130,7 +130,7 @@ public class AccountSwitcherSheet extends BottomSheet{
AccountSession session=AccountSessionManager.getInstance().getAccount(accountID);
new M3AlertDialogBuilder(activity)
.setTitle(R.string.log_out)
.setMessage(activity.getString(R.string.confirm_log_out))
.setMessage(activity.getString(R.string.confirm_log_out, session.getFullUsername()))
.setPositiveButton(R.string.log_out, (dialog, which) -> logOut(accountID))
.setNegativeButton(R.string.cancel, null)
.show();

File diff suppressed because one or more lines are too long

View File

@@ -37,6 +37,16 @@ public class OutlineProviders{
}
};
private final static int BUTTON_BG_HEIGHT=V.dp(40);
public static final ViewOutlineProvider M3_BUTTON=new ViewOutlineProvider(){
@Override
public void getOutline(View view, Outline outline){
int viewHeight=view.getHeight();
int top=Math.floorDiv(viewHeight - BUTTON_BG_HEIGHT, 2);
outline.setRoundRect(0, top, view.getWidth(), top + BUTTON_BG_HEIGHT, V.dp(20));
}
};
public static ViewOutlineProvider roundedRect(int dp){
ViewOutlineProvider provider=roundedRects.get(dp);
if(provider!=null)

View File

@@ -15,6 +15,7 @@ import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.fragments.BaseStatusListFragment;
import org.joinmastodon.android.model.Account;
import org.joinmastodon.android.model.Notification;
@@ -41,12 +42,13 @@ public class AccountCardStatusDisplayItem extends StatusDisplayItem{
public CustomEmojiHelper emojiHelper=new CustomEmojiHelper();
public CharSequence parsedName, parsedBio;
public AccountCardStatusDisplayItem(String parentID, BaseStatusListFragment parentFragment, Account account, Notification notification){
public AccountCardStatusDisplayItem(String parentID, BaseStatusListFragment parentFragment, String accountID, Account account, Notification notification){
super(parentID, parentFragment);
this.account=account;
this.notification=notification;
if(!TextUtils.isEmpty(account.avatar))
avaRequest=new UrlImageLoaderRequest(account.avatar, V.dp(50), V.dp(50));
avaRequest=new UrlImageLoaderRequest(
TextUtils.isEmpty(account.avatar) ? AccountSessionManager.get(accountID).getDefaultAvatarUrl() : account.avatar,
V.dp(50), V.dp(50));
if(!TextUtils.isEmpty(account.header))
coverRequest=new UrlImageLoaderRequest(account.header, 1000, 1000);
parsedBio=HtmlParser.parse(account.note, account.emojis, Collections.emptyList(), Collections.emptyList(), parentFragment.getAccountID());

View File

@@ -21,6 +21,7 @@ import androidx.recyclerview.widget.LinearSmoothScroller;
import androidx.recyclerview.widget.RecyclerView;
import org.joinmastodon.android.E;
import org.joinmastodon.android.GlobalUserPreferences;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.MastodonAPIRequest;
import org.joinmastodon.android.api.requests.announcements.AddAnnouncementReaction;
@@ -31,11 +32,10 @@ import org.joinmastodon.android.api.requests.statuses.PleromaAddStatusReaction;
import org.joinmastodon.android.api.requests.statuses.PleromaDeleteStatusReaction;
import org.joinmastodon.android.api.session.AccountSession;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.events.StatusCountersUpdatedEvent;
import org.joinmastodon.android.events.EmojiReactionsUpdatedEvent;
import org.joinmastodon.android.fragments.BaseStatusListFragment;
import org.joinmastodon.android.fragments.account_list.StatusEmojiReactionsListFragment;
import org.joinmastodon.android.model.Account;
import org.joinmastodon.android.model.Announcement;
import org.joinmastodon.android.model.Emoji;
import org.joinmastodon.android.model.EmojiReaction;
import org.joinmastodon.android.model.Status;
@@ -44,8 +44,6 @@ import org.joinmastodon.android.ui.utils.TextDrawable;
import org.joinmastodon.android.ui.utils.UiUtils;
import org.joinmastodon.android.ui.views.ProgressBarButton;
import java.util.Optional;
import me.grishka.appkit.Nav;
import me.grishka.appkit.api.Callback;
import me.grishka.appkit.api.ErrorResponse;
@@ -62,25 +60,24 @@ import me.grishka.appkit.views.UsableRecyclerView;
public class EmojiReactionsStatusDisplayItem extends StatusDisplayItem {
public final Status status;
private final Drawable placeholder;
private final boolean hideAdd, forAnnouncement;
private final boolean hideEmpty, forAnnouncement, playGifs;
private final String accountID;
private boolean hidden;
private static final float ALPHA_DISABLED=0.55f;
public EmojiReactionsStatusDisplayItem(String parentID, BaseStatusListFragment<?> parentFragment, Status status, String accountID, boolean hideAdd, boolean forAnnouncement) {
public EmojiReactionsStatusDisplayItem(String parentID, BaseStatusListFragment<?> parentFragment, Status status, String accountID, boolean hideEmpty, boolean forAnnouncement) {
super(parentID, parentFragment);
this.status=status;
this.hideAdd=hideAdd;
this.hideEmpty=hideEmpty;
this.forAnnouncement=forAnnouncement;
this.accountID=accountID;
placeholder=parentFragment.getContext().getDrawable(R.drawable.image_placeholder).mutate();
placeholder.setBounds(0, 0, V.sp(24), V.sp(24));
updateHidden();
playGifs=GlobalUserPreferences.playGifs;
}
@Override
public int getImageCount(){
return (int) status.reactions.stream().filter(r->r.url != null).count();
return (int) status.reactions.stream().filter(r->r.getUrl(playGifs)!=null).count();
}
@Override
@@ -94,11 +91,7 @@ public class EmojiReactionsStatusDisplayItem extends StatusDisplayItem {
}
public boolean isHidden(){
return hidden;
}
private void updateHidden(){
hidden=status.reactions.isEmpty() && hideAdd;
return status.reactions.isEmpty() && hideEmpty;
}
// borrowed from ProfileFragment
@@ -178,8 +171,9 @@ public class EmojiReactionsStatusDisplayItem extends StatusDisplayItem {
public void onBind(EmojiReactionsStatusDisplayItem item) {
if(emojiKeyboard != null) root.removeView(emojiKeyboard.getView());
AccountSession session=item.parentFragment.getSession();
item.status.reactions.forEach(r->
r.request=r.url != null ? new UrlImageLoaderRequest(r.url, V.sp(24), V.sp(24)) : null);
item.status.reactions.forEach(r->r.request=r.getUrl(item.playGifs)!=null
? new UrlImageLoaderRequest(r.getUrl(item.playGifs), V.sp(24), V.sp(24))
: null);
emojiKeyboard=new CustomEmojiPopupKeyboard(
(Activity) item.parentFragment.getContext(),
AccountSessionManager.getInstance().getCustomEmojis(session.domain),
@@ -187,12 +181,12 @@ public class EmojiReactionsStatusDisplayItem extends StatusDisplayItem {
emojiKeyboard.setListener(this);
space.setVisibility(View.GONE);
root.addView(emojiKeyboard.getView());
item.updateHidden();
root.setVisibility(item.hidden ? View.GONE : View.VISIBLE);
line.setVisibility(item.hidden ? View.GONE : View.VISIBLE);
boolean hidden=item.isHidden();
root.setVisibility(hidden ? View.GONE : View.VISIBLE);
line.setVisibility(hidden ? View.GONE : View.VISIBLE);
line.setPadding(
list.getPaddingLeft(),
item.hidden ? 0 : V.dp(8),
hidden ? 0 : V.dp(8),
list.getPaddingRight(),
item.forAnnouncement ? V.dp(8) : 0
);
@@ -219,6 +213,7 @@ public class EmojiReactionsStatusDisplayItem extends StatusDisplayItem {
}
private void addEmojiReaction(String emoji, Emoji info) {
int countBefore=item.status.reactions.size();
for(int i=0; i<item.status.reactions.size(); i++){
EmojiReaction r=item.status.reactions.get(i);
if(r.name.equals(emoji) && r.me){
@@ -261,7 +256,7 @@ public class EmojiReactionsStatusDisplayItem extends StatusDisplayItem {
finalExisting.add(me);
adapter.notifyItemChanged(item.status.reactions.indexOf(finalExisting));
}
E.post(new StatusCountersUpdatedEvent(item.status, adapter.parentHolder));
E.post(new EmojiReactionsUpdatedEvent(item.status.id, item.status.reactions, countBefore==0, adapter.parentHolder));
}, resetBtn).exec(item.accountID);
}
@@ -291,7 +286,7 @@ public class EmojiReactionsStatusDisplayItem extends StatusDisplayItem {
@Override
public void clearImage(int index){
if(item.status.reactions.get(index).url==null) return;
if(item.status.reactions.get(index).getUrl(item.playGifs)==null) return;
setImage(index, item.placeholder);
}
@@ -324,7 +319,7 @@ public class EmojiReactionsStatusDisplayItem extends StatusDisplayItem {
@Override
public int getImageCountForItem(int position){
return item.status.reactions.get(position).url == null ? 0 : 1;
return item.status.reactions.get(position).getUrl(item.playGifs)==null ? 0 : 1;
}
@Override
@@ -364,7 +359,7 @@ public class EmojiReactionsStatusDisplayItem extends StatusDisplayItem {
btn.setText(UiUtils.abbreviateNumber(reaction.count));
btn.setContentDescription(reaction.name);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) btn.setTooltipText(reaction.name);
if(reaction.url==null){
if(reaction.getUrl(parent.playGifs)==null){
Paint p=new Paint();
p.setTextSize(V.sp(18));
TextDrawable drawable=new TextDrawable(p, reaction.name);
@@ -392,7 +387,11 @@ public class EmojiReactionsStatusDisplayItem extends StatusDisplayItem {
break;
}
E.post(new StatusCountersUpdatedEvent(parent.status, adapter.parentHolder));
if(parent.isHidden()){
adapter.parentHolder.root.setVisibility(View.GONE);
adapter.parentHolder.line.setVisibility(View.GONE);
}
E.post(new EmojiReactionsUpdatedEvent(parent.status.id, parent.status.reactions, parent.status.reactions.isEmpty(), adapter.parentHolder));
adapter.parentHolder.imgLoader.updateImages();
}, null).exec(parent.parentFragment.getAccountID());
});
@@ -406,7 +405,7 @@ public class EmojiReactionsStatusDisplayItem extends StatusDisplayItem {
args.putString("statusID", parent.status.id);
int atSymbolIndex = emojiReaction.name.indexOf("@");
args.putString("emoji", atSymbolIndex != -1 ? emojiReaction.name.substring(0, atSymbolIndex) : emojiReaction.name);
args.putString("url", emojiReaction.url);
args.putString("url", emojiReaction.getUrl(parent.playGifs));
args.putInt("count", emojiReaction.count);
Nav.go(parent.parentFragment.getActivity(), StatusEmojiReactionsListFragment.class, args);
return true;

View File

@@ -1,7 +1,9 @@
package org.joinmastodon.android.ui.displayitems;
import android.app.Activity;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.media.Image;
import android.net.Uri;
import android.text.TextUtils;
import android.view.View;
@@ -14,10 +16,18 @@ import org.joinmastodon.android.fragments.BaseStatusListFragment;
import org.joinmastodon.android.model.Attachment;
import org.joinmastodon.android.model.Card;
import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.ui.SingleImagePhotoViewerListener;
import org.joinmastodon.android.ui.drawables.BlurhashCrossfadeDrawable;
import org.joinmastodon.android.ui.photoviewer.PhotoViewer;
import org.joinmastodon.android.ui.photoviewer.PhotoViewerHost;
import org.joinmastodon.android.ui.utils.UiUtils;
import java.util.ArrayList;
import java.util.List;
import me.grishka.appkit.Nav;
import me.grishka.appkit.imageloader.ImageLoaderViewHolder;
import me.grishka.appkit.utils.V;
public class FileStatusDisplayItem extends StatusDisplayItem{
private final Status status;
@@ -36,22 +46,31 @@ public class FileStatusDisplayItem extends StatusDisplayItem{
public static class Holder extends StatusDisplayItem.Holder<FileStatusDisplayItem>{
private final TextView title, domain;
private final ImageView icon;
private final Context context;
public Holder(Context context, ViewGroup parent){
super(context, R.layout.display_item_file, parent);
title=findViewById(R.id.title);
domain=findViewById(R.id.domain);
icon=findViewById(R.id.imageView);
this.context=context;
findViewById(R.id.inner).setOnClickListener(this::onClick);
}
@Override
public void onBind(FileStatusDisplayItem item) {
Uri url = Uri.parse(getUrl());
title.setText(item.attachment.description != null
? item.attachment.description
: url.getLastPathSegment());
title.setEllipsize(item.attachment.description != null ? TextUtils.TruncateAt.END : TextUtils.TruncateAt.MIDDLE);
domain.setText(url.getHost());
icon.setImageDrawable(context.getDrawable(R.drawable.ic_fluent_attach_24_regular));
}
private void onClick(View v) {

View File

@@ -1,5 +1,8 @@
package org.joinmastodon.android.ui.displayitems;
import static org.joinmastodon.android.ui.utils.UiUtils.opacityIn;
import static org.joinmastodon.android.ui.utils.UiUtils.opacityOut;
import android.app.Activity;
import android.app.Dialog;
import android.content.Context;

View File

@@ -8,14 +8,21 @@ import android.widget.TextView;
import org.joinmastodon.android.R;
import org.joinmastodon.android.fragments.BaseStatusListFragment;
import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.ui.drawables.SawtoothTearDrawable;
import org.joinmastodon.android.ui.utils.UiUtils;
import java.time.Instant;
import me.grishka.appkit.utils.V;
// Mind the gap!
public class GapStatusDisplayItem extends StatusDisplayItem{
public boolean loading;
private Status status;
public GapStatusDisplayItem(String parentID, BaseStatusListFragment parentFragment){
public GapStatusDisplayItem(String parentID, BaseStatusListFragment<?> parentFragment, Status status){
super(parentID, parentFragment);
this.status=status;
}
@Override
@@ -24,25 +31,53 @@ public class GapStatusDisplayItem extends StatusDisplayItem{
}
public static class Holder extends StatusDisplayItem.Holder<GapStatusDisplayItem>{
public final ProgressBar progress;
public final TextView text;
public final ProgressBar progressTop, progressBottom;
public final TextView textTop, gap, textBottom;
public final View top, bottom;
public Holder(Context context, ViewGroup parent){
super(context, R.layout.display_item_gap, parent);
progress=findViewById(R.id.progress);
text=findViewById(R.id.text);
itemView.setForeground(new SawtoothTearDrawable(context));
progressTop=findViewById(R.id.progress_top);
progressBottom=findViewById(R.id.progress_bottom);
textTop=findViewById(R.id.text_top);
textBottom=findViewById(R.id.text_bottom);
top=findViewById(R.id.top);
top.setOnClickListener(this::onViewClick);
bottom=findViewById(R.id.bottom);
bottom.setOnClickListener(this::onViewClick);
gap=findViewById(R.id.gap);
gap.setForeground(new SawtoothTearDrawable(context));
}
@Override
public void onBind(GapStatusDisplayItem item){
text.setVisibility(item.loading ? View.GONE : View.VISIBLE);
progress.setVisibility(item.loading ? View.VISIBLE : View.GONE);
if(!item.loading){
progressBottom.setVisibility(View.GONE);
progressTop.setVisibility(View.GONE);
}
top.setClickable(!item.loading);
bottom.setClickable(!item.loading);
StatusDisplayItem next=getNextVisibleDisplayItem().orElse(null);
Instant dateBelow=next instanceof HeaderStatusDisplayItem h ? h.status.createdAt
: next instanceof ReblogOrReplyLineStatusDisplayItem l ? l.status.createdAt
: null;
String text=dateBelow!=null && item.status.createdAt!=null && dateBelow.isBefore(item.status.createdAt)
? UiUtils.formatPeriodBetween(item.parentFragment.getContext(), dateBelow, item.status.createdAt)
: null;
gap.setText(text);
int p=text==null ? V.dp(6) : V.dp(20);
gap.setPadding(p, p, p, p);
}
private void onViewClick(View v){
if(item.loading) return;
boolean isTop=v==top;
(isTop ? textTop : textBottom).startAnimation(UiUtils.opacityOut);
V.setVisibilityAnimated((isTop ? progressTop : progressBottom), View.VISIBLE);
item.parentFragment.onGapClick(this, isTop);
}
@Override
public void onClick(){
item.parentFragment.onGapClick(this);
}
public void onClick(){}
}
}

View File

@@ -86,10 +86,14 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
public HeaderStatusDisplayItem(String parentID, Account user, Instant createdAt, BaseStatusListFragment parentFragment, String accountID, Status status, CharSequence extraText, Notification notification, ScheduledStatus scheduledStatus){
super(parentID, parentFragment);
user=scheduledStatus != null ? AccountSessionManager.getInstance().getAccount(accountID).self : user;
AccountSession session = AccountSessionManager.get(accountID);
user=scheduledStatus != null ? session.self : user;
this.user=user;
this.createdAt=createdAt;
avaRequest=new UrlImageLoaderRequest(GlobalUserPreferences.playGifs ? user.avatar : user.avatarStatic, V.dp(50), V.dp(50));
avaRequest=new UrlImageLoaderRequest(
TextUtils.isEmpty(user.avatar) ? session.getDefaultAvatarUrl() :
GlobalUserPreferences.playGifs ? user.avatar : user.avatarStatic,
V.dp(50), V.dp(50));
this.accountID=accountID;
parsedName=new SpannableStringBuilder(user.displayName);
this.status=status;
@@ -134,13 +138,11 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
public static class Holder extends StatusDisplayItem.Holder<HeaderStatusDisplayItem> implements ImageLoaderViewHolder{
private final TextView name, time, username, extraText, pronouns;
private final View collapseBtn, timeUsernameSeparator;
private final ImageView avatar, more, visibility, deleteNotification, unreadIndicator, markAsRead, collapseBtnIcon;
private final ImageView avatar, more, visibility, deleteNotification, unreadIndicator, markAsRead, collapseBtnIcon, botIcon;
private final PopupMenu optionsMenu;
private Relationship relationship;
private APIRequest<?> currentRelationshipRequest;
//TODO: readd
public Holder(Activity activity, ViewGroup parent){
this(activity, R.layout.display_item_header, parent);
}
@@ -150,6 +152,7 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
name=findViewById(R.id.name);
time=findViewById(R.id.time);
username=findViewById(R.id.username);
botIcon=findViewById(R.id.bot_icon);
timeUsernameSeparator=findViewById(R.id.separator);
avatar=findViewById(R.id.avatar);
more=findViewById(R.id.more);
@@ -159,7 +162,6 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
markAsRead=findViewById(R.id.mark_as_read);
collapseBtn=findViewById(R.id.collapse_btn);
collapseBtnIcon=findViewById(R.id.collapse_btn_icon);
// botIcon=findViewById(R.id.bot_icon);
extraText=findViewById(R.id.extra_text);
pronouns=findViewById(R.id.pronouns);
avatar.setOnClickListener(this::onAvaClick);
@@ -195,7 +197,7 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
}
}
boolean isPixelfed = item.parentFragment.isInstancePixelfed();
boolean textEmpty = TextUtils.isEmpty(item.status.content) && TextUtils.isEmpty(item.status.spoilerText);
boolean textEmpty = TextUtils.isEmpty(item.status.content) && !item.status.hasSpoiler();
if(!redraft && (isPixelfed || textEmpty)){
// pixelfed doesn't support /statuses/:id/source :/
if (isPixelfed) {
@@ -325,13 +327,21 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
this.time.setVisibility(time==null ? View.GONE : View.VISIBLE);
if(time!=null) this.time.setText(time);
botIcon.setVisibility(item.user.bot ? View.VISIBLE : View.GONE);
botIcon.setColorFilter(username.getCurrentTextColor());
deleteNotification.setVisibility(GlobalUserPreferences.enableDeleteNotifications && item.notification!=null && !item.inset ? View.VISIBLE : View.GONE);
if (item.hasVisibilityToggle){
boolean disabled = !item.status.sensitiveRevealed ||
(!TextUtils.isEmpty(item.status.spoilerText) &&
!item.status.spoilerRevealed);
visibility.setEnabled(!disabled);
V.setVisibilityAnimated(visibility, disabled ? View.INVISIBLE : View.VISIBLE);
boolean hidden = !item.status.sensitiveRevealed || (item.status.hasSpoiler() && !item.status.spoilerRevealed);
// doing this because V.setVisibilityAnimated ignores changes between INVISIBLE and GONE
int newVis=hidden ? View.INVISIBLE : View.VISIBLE;
if(newVis==View.INVISIBLE && visibility.getVisibility()==View.GONE)
visibility.setVisibility(newVis);
else
V.setVisibilityAnimated(visibility, newVis);
visibility.setEnabled(!hidden);
visibility.setContentDescription(item.parentFragment.getString(item.status.sensitiveRevealed ? R.string.spoiler_hide : R.string.spoiler_show));
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.O){
visibility.setTooltipText(visibility.getContentDescription());
@@ -424,6 +434,8 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
}
private void onAvaClick(View v){
if (TextUtils.isEmpty(item.user.url))
return;
if (item.announcement != null) {
UiUtils.openURL(item.parentFragment.getActivity(), item.parentFragment.getAccountID(), item.user.url);
return;

View File

@@ -27,10 +27,10 @@ public class LinkCardStatusDisplayItem extends StatusDisplayItem{
private final Status status;
private final UrlImageLoaderRequest imgRequest;
public LinkCardStatusDisplayItem(String parentID, BaseStatusListFragment parentFragment, Status status){
public LinkCardStatusDisplayItem(String parentID, BaseStatusListFragment parentFragment, Status status, boolean showImagePreview){
super(parentID, parentFragment);
this.status=status;
if(status.card.image!=null)
if(status.card.image!=null && showImagePreview)
imgRequest=new UrlImageLoaderRequest(status.card.image, 1000, 1000);
else
imgRequest=null;

View File

@@ -20,6 +20,8 @@ import android.widget.TextView;
import org.joinmastodon.android.GlobalUserPreferences;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.session.AccountSession;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.fragments.BaseStatusListFragment;
import org.joinmastodon.android.fragments.ProfileFragment;
import org.joinmastodon.android.model.Emoji;
@@ -53,7 +55,11 @@ public class NotificationHeaderStatusDisplayItem extends StatusDisplayItem{
if(notification.type==Notification.Type.POLL){
text=parentFragment.getString(R.string.poll_ended);
}else{
avaRequest=new UrlImageLoaderRequest(GlobalUserPreferences.playGifs ? notification.account.avatar : notification.account.avatarStatic, V.dp(50), V.dp(50));
AccountSession session = AccountSessionManager.get(accountID);
avaRequest=new UrlImageLoaderRequest(
TextUtils.isEmpty(notification.account.avatar) ? session.getDefaultAvatarUrl() :
GlobalUserPreferences.playGifs ? notification.account.avatar : notification.account.avatarStatic,
V.dp(50), V.dp(50));
SpannableStringBuilder parsedName=new SpannableStringBuilder(notification.account.displayName);
HtmlParser.parseCustomEmoji(parsedName, notification.account.emojis);
String str = parentFragment.getString(switch(notification.type){

View File

@@ -80,7 +80,7 @@ public class PollOptionStatusDisplayItem extends StatusDisplayItem{
progressBg=activity.getResources().getDrawable(R.drawable.bg_poll_option_voted, activity.getTheme()).mutate();
progressBgInset=activity.getResources().getDrawable(R.drawable.bg_poll_option_voted_inset, activity.getTheme()).mutate();
itemView.setOnClickListener(this::onButtonClick);
button.setOutlineProvider(OutlineProviders.roundedRect(24));
button.setOutlineProvider(OutlineProviders.M3_BUTTON);
button.setClipToOutline(true);
}

View File

@@ -0,0 +1,147 @@
package org.joinmastodon.android.ui.displayitems;
import android.app.Activity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import org.joinmastodon.android.fragments.BaseStatusListFragment;
import org.joinmastodon.android.model.Attachment;
import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.ui.OutlineProviders;
import org.joinmastodon.android.ui.PhotoLayoutHelper;
import org.joinmastodon.android.ui.utils.PreviewlessMediaAttachmentViewController;
import org.joinmastodon.android.ui.views.FrameLayoutThatOnlyMeasuresFirstChild;
import org.joinmastodon.android.utils.TypedObjectPool;
import java.util.ArrayList;
import java.util.List;
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
import me.grishka.appkit.utils.V;
public class PreviewlessMediaGridStatusDisplayItem extends StatusDisplayItem{
private static final String TAG="PreviewlessMediaGridDisplayItem";
private PhotoLayoutHelper.TiledLayoutResult tiledLayout;
private final TypedObjectPool<MediaGridStatusDisplayItem.GridItemType, PreviewlessMediaAttachmentViewController> viewPool;
private final List<Attachment> attachments;
private final ArrayList<ImageLoaderRequest> requests=new ArrayList<>();
public final Status status;
public String sensitiveTitle;
public PreviewlessMediaGridStatusDisplayItem(String parentID, BaseStatusListFragment<?> parentFragment, PhotoLayoutHelper.TiledLayoutResult tiledLayout, List<Attachment> attachments, Status status){
super(parentID, parentFragment);
this.tiledLayout=tiledLayout;
this.viewPool=parentFragment.getPreviewlessAttachmentViewsPool();
this.attachments=attachments;
this.status=status;
// for(Attachment att:attachments){
// requests.add(new UrlImageLoaderRequest(switch(att.type){
// case IMAGE -> att.url;
// case VIDEO, GIFV -> att.previewUrl == null ? att.url : att.previewUrl;
// default -> throw new IllegalStateException("Unexpected value: "+att.url);
// }, 1000, 1000));
// }
}
@Override
public Type getType(){
return Type.PREVIEWLESS_MEDIA_GRID;
}
@Override
public int getImageCount(){
return attachments.size();
}
@Override
public ImageLoaderRequest getImageRequest(int index){
return requests.get(index);
}
public static class Holder extends StatusDisplayItem.Holder<PreviewlessMediaGridStatusDisplayItem> {
private final FrameLayout wrapper;
private final LinearLayout layout;
private final View.OnClickListener clickListener=this::onViewClick;
private final ArrayList<PreviewlessMediaAttachmentViewController> controllers=new ArrayList<>();
// private final FrameLayout hideSensitiveButton;
public Holder(Activity activity, ViewGroup parent){
super(new FrameLayoutThatOnlyMeasuresFirstChild(activity));
wrapper=(FrameLayout)itemView;
layout= new LinearLayout(activity);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
params.setMargins(V.dp(16), 0, V.dp(16), 0);
layout.setLayoutParams(params);
layout.setOrientation(LinearLayout.VERTICAL);
wrapper.addView(layout);
wrapper.setClipToPadding(false);
// megalodon: no sensitive hide button because the visibility toggle looks prettier imo
// hideSensitiveButton=(FrameLayout) activity.getLayoutInflater().inflate(R.layout.alt_text_badge, overlays, false);
// ((TextView) hideSensitiveButton.findViewById(R.id.alt_button)).setText(R.string.hide);
// overlays.addView(hideSensitiveButton, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.END | Gravity.TOP));
// hideSensitiveButton.setOnClickListener(v->hideSensitive());
}
@Override
public void onBind(PreviewlessMediaGridStatusDisplayItem item){
wrapper.setPadding(0, 0, 0, 0); // item.inset ? 0 : V.dp(8));
// if(altTextAnimator!=null)
// altTextAnimator.cancel();
for(PreviewlessMediaAttachmentViewController c:controllers){
item.viewPool.reuse(c.type, c);
}
layout.removeAllViews();
controllers.clear();
int i=0;
// if (!item.attachments.isEmpty()) updateBlurhashInSensitiveOverlay();
for(Attachment att:item.attachments){
PreviewlessMediaAttachmentViewController c=item.viewPool.obtain(switch(att.type){
case IMAGE -> MediaGridStatusDisplayItem.GridItemType.PHOTO;
case VIDEO -> MediaGridStatusDisplayItem.GridItemType.VIDEO;
case GIFV -> MediaGridStatusDisplayItem.GridItemType.GIFV;
default -> throw new IllegalStateException("Unexpected value: "+att.type);
});
if(c.view.getLayoutParams()==null)
c.view.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
layout.addView(c.view);
c.view.setOnClickListener(clickListener);
c.view.setTag(i);
controllers.add(c);
c.bind(att, item.status);
i++;
}
boolean insetAndLast=item.inset && isLastDisplayItemForStatus();
wrapper.setClipToOutline(insetAndLast);
wrapper.setOutlineProvider(insetAndLast ? OutlineProviders.bottomRoundedRect(12) : null);
}
private void onViewClick(View v){
int index=(Integer)v.getTag();
item.parentFragment.openPreviewlessMediaPhotoViewer(item.parentID, item.status, index, this);
}
public PreviewlessMediaAttachmentViewController getViewController(int index){
return controllers.get(index);
}
public void setClipChildren(boolean clip){
layout.setClipChildren(clip);
wrapper.setClipChildren(clip);
}
public LinearLayout getLayout(){
return layout;
}
}
}

View File

@@ -17,6 +17,7 @@ import org.joinmastodon.android.GlobalUserPreferences;
import org.joinmastodon.android.R;
import org.joinmastodon.android.fragments.BaseStatusListFragment;
import org.joinmastodon.android.model.Emoji;
import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.model.StatusPrivacy;
import org.joinmastodon.android.ui.text.HtmlParser;
import org.joinmastodon.android.ui.utils.CustomEmojiHelper;
@@ -43,18 +44,20 @@ public class ReblogOrReplyLineStatusDisplayItem extends StatusDisplayItem{
public boolean needBottomPadding;
ReblogOrReplyLineStatusDisplayItem extra;
CharSequence fullText;
Status status;
public ReblogOrReplyLineStatusDisplayItem(String parentID, BaseStatusListFragment parentFragment, CharSequence text, List<Emoji> emojis, @DrawableRes int icon, StatusPrivacy visibility, @Nullable View.OnClickListener handleClick) {
this(parentID, parentFragment, text, emojis, icon, visibility, handleClick, text);
public ReblogOrReplyLineStatusDisplayItem(String parentID, BaseStatusListFragment parentFragment, CharSequence text, List<Emoji> emojis, @DrawableRes int icon, StatusPrivacy visibility, @Nullable View.OnClickListener handleClick, Status status) {
this(parentID, parentFragment, text, emojis, icon, visibility, handleClick, text, status);
}
public ReblogOrReplyLineStatusDisplayItem(String parentID, BaseStatusListFragment parentFragment, CharSequence text, List<Emoji> emojis, @DrawableRes int icon, StatusPrivacy visibility, @Nullable View.OnClickListener handleClick, CharSequence fullText) {
public ReblogOrReplyLineStatusDisplayItem(String parentID, BaseStatusListFragment parentFragment, CharSequence text, List<Emoji> emojis, @DrawableRes int icon, StatusPrivacy visibility, @Nullable View.OnClickListener handleClick, CharSequence fullText, Status status) {
super(parentID, parentFragment);
SpannableStringBuilder ssb=new SpannableStringBuilder(text);
HtmlParser.parseCustomEmoji(ssb, emojis);
this.text=ssb;
emojiHelper.setText(ssb);
this.icon=icon;
this.status=status;
this.handleClick=handleClick;
TypedValue outValue = new TypedValue();
context.getTheme().resolveAttribute(android.R.attr.selectableItemBackground, outValue, true);

View File

@@ -6,9 +6,9 @@ import android.graphics.drawable.LayerDrawable;
import android.text.TextUtils;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
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.Status;
@@ -29,11 +29,13 @@ public class SpoilerStatusDisplayItem extends StatusDisplayItem{
private final CharSequence parsedTitle;
private final CustomEmojiHelper emojiHelper;
private final Type type;
private final int attachmentCount;
public SpoilerStatusDisplayItem(String parentID, BaseStatusListFragment<?> parentFragment, String title, Status statusForContent, Type type){
super(parentID, parentFragment);
this.status=statusForContent;
this.type=type;
this.attachmentCount=statusForContent.mediaAttachments.size();
if(TextUtils.isEmpty(title)){
parsedTitle=HtmlParser.parseCustomEmoji(statusForContent.spoilerText, statusForContent.emojis);
emojiHelper=new CustomEmojiHelper();
@@ -62,12 +64,14 @@ public class SpoilerStatusDisplayItem extends StatusDisplayItem{
public static class Holder extends StatusDisplayItem.Holder<SpoilerStatusDisplayItem> implements ImageLoaderViewHolder{
private final TextView title, action;
private final View button;
private final ImageView mediaIcon;
public Holder(Context context, ViewGroup parent, Type type){
super(context, R.layout.display_item_spoiler, parent);
title=findViewById(R.id.spoiler_title);
action=findViewById(R.id.spoiler_action);
button=findViewById(R.id.spoiler_button);
mediaIcon=findViewById(R.id.media_icon);
button.setOutlineProvider(OutlineProviders.roundedRect(8));
button.setClipToOutline(true);
@@ -94,6 +98,10 @@ public class SpoilerStatusDisplayItem extends StatusDisplayItem{
itemView.getPaddingRight(),
item.inset ? itemView.getPaddingTop() : 0
);
mediaIcon.setVisibility(item.attachmentCount > 0 ? View.VISIBLE : View.GONE);
mediaIcon.setImageResource(item.attachmentCount > 1
? R.drawable.ic_fluent_image_multiple_24_regular
: R.drawable.ic_fluent_image_24_regular);
}
@Override

View File

@@ -1,5 +1,8 @@
package org.joinmastodon.android.ui.displayitems;
import static org.joinmastodon.android.api.session.AccountLocalPreferences.ShowEmojiReactions.ALWAYS;
import static org.joinmastodon.android.api.session.AccountLocalPreferences.ShowEmojiReactions.ONLY_OPENED;
import android.app.Activity;
import android.app.Fragment;
import android.content.Context;
@@ -12,6 +15,7 @@ import android.view.ViewGroup;
import org.joinmastodon.android.GlobalUserPreferences;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.session.AccountLocalPreferences;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.fragments.BaseStatusListFragment;
import org.joinmastodon.android.fragments.HashtagTimelineFragment;
@@ -50,7 +54,7 @@ import me.grishka.appkit.views.UsableRecyclerView;
public abstract class StatusDisplayItem{
public final String parentID;
public final BaseStatusListFragment<?> parentFragment;
public boolean inset, insetPadding=true;
public boolean inset;
public int index;
public boolean
hasDescendantNeighbor = false,
@@ -65,6 +69,7 @@ public abstract class StatusDisplayItem{
public static final int FLAG_NO_HEADER=1 << 4;
public static final int FLAG_NO_TRANSLATE=1 << 5;
public static final int FLAG_NO_EMOJI_REACTIONS=1 << 6;
public static final int FLAG_NO_MEDIA_PREVIEW=1 << 7;
public void setAncestryInfo(
boolean hasDescendantNeighbor,
@@ -111,6 +116,7 @@ public abstract class StatusDisplayItem{
case GAP -> new GapStatusDisplayItem.Holder(activity, parent);
case EXTENDED_FOOTER -> new ExtendedFooterStatusDisplayItem.Holder(activity, parent);
case MEDIA_GRID -> new MediaGridStatusDisplayItem.Holder(activity, parent);
case PREVIEWLESS_MEDIA_GRID -> new PreviewlessMediaGridStatusDisplayItem.Holder(activity, parent);
case WARNING -> new WarningFilteredStatusDisplayItem.Holder(activity, parent);
case FILE -> new FileStatusDisplayItem.Holder(activity, parent);
case SPOILER, FILTER_SPOILER -> new SpoilerStatusDisplayItem.Holder(activity, parent, type);
@@ -120,19 +126,6 @@ public abstract class StatusDisplayItem{
};
}
public static ArrayList<StatusDisplayItem> buildItems(BaseStatusListFragment<?> fragment, Status status, String accountID, DisplayItemsParent parentObject, Map<String, Account> knownAccounts, boolean inset, boolean showReactions, boolean addFooter, boolean disableTranslate, FilterContext filterContext) {
int flags=0;
if(inset)
flags|=FLAG_INSET;
if(!addFooter)
flags|=FLAG_NO_FOOTER;
if (disableTranslate)
flags|=FLAG_NO_TRANSLATE;
if (!showReactions)
flags|=FLAG_NO_EMOJI_REACTIONS;
return buildItems(fragment, status, accountID, parentObject, knownAccounts, filterContext, flags);
}
public static ReblogOrReplyLineStatusDisplayItem buildReplyLine(BaseStatusListFragment<?> fragment, Status status, String accountID, DisplayItemsParent parent, Account account, boolean threadReply) {
String parentID = parent.getID();
String text = threadReply ? fragment.getString(R.string.sk_show_thread)
@@ -144,7 +137,7 @@ public abstract class StatusDisplayItem{
: fragment.getString(R.string.in_reply_to, account.displayName);
return new ReblogOrReplyLineStatusDisplayItem(
parentID, fragment, text, account == null ? List.of() : account.emojis,
R.drawable.ic_fluent_arrow_reply_20sp_filled, null, null, fullText
R.drawable.ic_fluent_arrow_reply_20sp_filled, null, null, fullText, status
);
}
@@ -179,7 +172,7 @@ public abstract class StatusDisplayItem{
items.add(new ReblogOrReplyLineStatusDisplayItem(parentID, fragment, text, status.account.emojis, R.drawable.ic_fluent_arrow_repeat_all_20sp_filled, isOwnPost ? status.visibility : null, i->{
args.putParcelable("profileAccount", Parcels.wrap(status.account));
Nav.go(fragment.getActivity(), ProfileFragment.class, args);
}, fullText));
}, fullText, status));
} else if (!(status.tags.isEmpty() ||
fragment instanceof HashtagTimelineFragment ||
fragment instanceof ListTimelineFragment
@@ -195,7 +188,7 @@ public abstract class StatusDisplayItem{
i -> {
args.putString("hashtag", hashtag.name);
Nav.go(fragment.getActivity(), HashtagTimelineFragment.class, args);
}
}, status
)));
}
@@ -229,7 +222,7 @@ public abstract class StatusDisplayItem{
}
ArrayList<StatusDisplayItem> contentItems;
if(!TextUtils.isEmpty(statusForContent.spoilerText)){
if(statusForContent.hasSpoiler()){
if (AccountSessionManager.get(accountID).getLocalPreferences().revealCWs) statusForContent.spoilerRevealed = true;
SpoilerStatusDisplayItem spoilerItem=new SpoilerStatusDisplayItem(parentID, fragment, null, statusForContent, Type.SPOILER);
items.add(spoilerItem);
@@ -261,7 +254,7 @@ public abstract class StatusDisplayItem{
}
List<Attachment> imageAttachments=statusForContent.mediaAttachments.stream().filter(att->att.type.isImage()).collect(Collectors.toList());
if(!imageAttachments.isEmpty()){
if(!imageAttachments.isEmpty() && (flags & FLAG_NO_MEDIA_PREVIEW)==0){
int color = UiUtils.getThemeColor(fragment.getContext(), R.attr.colorM3SurfaceVariant);
for (Attachment att : imageAttachments) {
if (att.blurhashPlaceholder == null) {
@@ -276,6 +269,10 @@ public abstract class StatusDisplayItem{
statusForContent.sensitiveRevealed=true;
contentItems.add(mediaGrid);
}
if((flags & FLAG_NO_MEDIA_PREVIEW)!=0){
contentItems.add(new PreviewlessMediaGridStatusDisplayItem(parentID, fragment, null, imageAttachments, statusForContent));
}
for(Attachment att:statusForContent.mediaAttachments){
if(att.type==Attachment.Type.AUDIO){
contentItems.add(new AudioStatusDisplayItem(parentID, fragment, statusForContent, att));
@@ -288,15 +285,17 @@ public abstract class StatusDisplayItem{
buildPollItems(parentID, fragment, statusForContent.poll, contentItems, statusForContent);
}
if(statusForContent.card!=null && statusForContent.mediaAttachments.isEmpty()){
contentItems.add(new LinkCardStatusDisplayItem(parentID, fragment, statusForContent));
contentItems.add(new LinkCardStatusDisplayItem(parentID, fragment, statusForContent, (flags & FLAG_NO_MEDIA_PREVIEW)==0));
}
if(contentItems!=items && statusForContent.spoilerRevealed){
items.addAll(contentItems);
}
if((flags & FLAG_NO_EMOJI_REACTIONS)==0
&& AccountSessionManager.get(accountID).getLocalPreferences().emojiReactionsEnabled){
AccountLocalPreferences lp=fragment.getLocalPrefs();
if((flags & FLAG_NO_EMOJI_REACTIONS)==0 && lp.emojiReactionsEnabled &&
(lp.showEmojiReactions!=ONLY_OPENED || fragment instanceof ThreadFragment)){
boolean isMainStatus=fragment instanceof ThreadFragment t && t.getMainStatus().id.equals(statusForContent.id);
items.add(new EmojiReactionsStatusDisplayItem(parentID, fragment, statusForContent, accountID, !isMainStatus, false));
boolean showAddButton=lp.showEmojiReactions==ALWAYS || isMainStatus;
items.add(new EmojiReactionsStatusDisplayItem(parentID, fragment, statusForContent, accountID, !showAddButton, false));
}
FooterStatusDisplayItem footer=null;
if((flags & FLAG_NO_FOOTER)==0){
@@ -304,12 +303,12 @@ public abstract class StatusDisplayItem{
footer.hideCounts=hideCounts;
items.add(footer);
if(status.hasGapAfter && !(fragment instanceof ThreadFragment))
items.add(new GapStatusDisplayItem(parentID, fragment));
items.add(new GapStatusDisplayItem(parentID, fragment, status));
}
int i=1;
boolean inset=(flags & FLAG_INSET)!=0;
// add inset dummy so last content item doesn't clip out of inset bounds
if(inset || footer==null){
if((inset || footer==null) && (flags & FLAG_CHECKABLE)==0){
items.add(new DummyStatusDisplayItem(parentID, fragment));
// in case we ever need the dummy to display a margin for the media grid again:
// (i forgot why we apparently don't need this anymore)
@@ -363,6 +362,7 @@ public abstract class StatusDisplayItem{
GAP,
EXTENDED_FOOTER,
MEDIA_GRID,
PREVIEWLESS_MEDIA_GRID,
WARNING,
FILE,
SPOILER,
@@ -401,24 +401,17 @@ public abstract class StatusDisplayItem{
}
return Optional.empty();
}
// int nextNextPos=getAbsoluteAdapterPosition() + 2;
// if(next.map(n->n instanceof EmojiReactionsStatusDisplayItem e && e.isHidden()).orElse(false)){
// List<StatusDisplayItem> displayItems=item.parentFragment.getDisplayItems();
// return displayItems.size() > nextNextPos
// ? Optional.of(displayItems.get(nextNextPos))
// : Optional.empty();
// }else{
// return next;
// }
public Optional<StatusDisplayItem> getNextDisplayItem(){
return getDisplayItemOffset(1);
}
public Optional<StatusDisplayItem> getDisplayItemOffset(int offset){
int nextPos=getAbsoluteAdapterPosition() + offset;
List<StatusDisplayItem> displayItems=item.parentFragment.getDisplayItems();
return displayItems.size() > nextPos
? Optional.of(displayItems.get(nextPos))
int thisPos=displayItems.indexOf(item);
int offsetPos=thisPos + offset;
return displayItems.size() > offsetPos && thisPos >= 0 && offsetPos >= 0
? Optional.of(displayItems.get(offsetPos))
: Optional.empty();
}

View File

@@ -143,7 +143,8 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
!item.status.visibility.isLessVisibleThan(StatusPrivacy.UNLISTED) &&
item.status.language != null &&
// todo: compare to mastodon locale instead (how do i query that?!)
!item.status.language.equalsIgnoreCase(Locale.getDefault().getLanguage())));
!item.status.language.equalsIgnoreCase(Locale.getDefault().getLanguage())))
&& (!GlobalUserPreferences.translateButtonOpenedOnly || item.textSelectable);
translateWrap.setVisibility(translateVisible ? View.VISIBLE : View.GONE);
translateButton.setText(item.translationShown ? R.string.sk_translate_show_original : R.string.sk_translate_post);
translateInfo.setText(item.translationShown ? itemView.getResources().getString(R.string.sk_translated_using, bottomText != null ? "bottom-java" : item.status.translation.provider) : "");
@@ -184,11 +185,10 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
readMore.setText(item.status.textExpanded ? R.string.sk_collapse : R.string.sk_expand);
StatusDisplayItem next=getNextVisibleDisplayItem().orElse(null);
int bottomPadding=next instanceof FooterStatusDisplayItem
? V.dp(6)
: (!item.inset && next instanceof DummyStatusDisplayItem) ||
next instanceof EmojiReactionsStatusDisplayItem e && !e.isHidden()
? 0
if(next!=null && !next.parentID.equals(item.parentID)) next=null;
int bottomPadding=next instanceof FooterStatusDisplayItem ? V.dp(6)
: item.inset ? V.dp(12)
: (next instanceof EmojiReactionsStatusDisplayItem || next==null) ? 0
: V.dp(12);
itemView.setPadding(itemView.getPaddingLeft(), itemView.getPaddingTop(), itemView.getPaddingRight(), bottomPadding);

View File

@@ -4,18 +4,15 @@ import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
import org.joinmastodon.android.R;
import org.joinmastodon.android.fragments.BaseStatusListFragment;
import org.joinmastodon.android.model.AltTextFilter;
import org.joinmastodon.android.model.Filter;
import org.joinmastodon.android.model.LegacyFilter;
import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.ui.drawables.SawtoothTearDrawable;
import java.util.ArrayList;
import java.util.List;
// Mind the gap!
@@ -55,7 +52,8 @@ public class WarningFilteredStatusDisplayItem extends StatusDisplayItem{
@Override
public void onBind(WarningFilteredStatusDisplayItem item) {
filteredItems = item.filteredItems;
text.setText(item.parentFragment.getString(R.string.sk_filtered, item.applyingFilter.title));
String title = item.applyingFilter instanceof AltTextFilter ? item.parentFragment.getString(R.string.sk_no_alt_text) : item.applyingFilter.title;
text.setText(item.parentFragment.getString(R.string.sk_filtered, title));
}
@Override

View File

@@ -23,9 +23,9 @@ import me.grishka.appkit.utils.V;
public class SawtoothTearDrawable extends Drawable{
private final Paint topPaint, bottomPaint;
private static final int TOP_SAWTOOTH_HEIGHT=5;
private static final int BOTTOM_SAWTOOTH_HEIGHT=3;
private static final int STROKE_WIDTH=2;
private static final int TOP_SAWTOOTH_HEIGHT=4;
private static final int BOTTOM_SAWTOOTH_HEIGHT=4;
private static final int STROKE_WIDTH=1;
private static final int SAWTOOTH_PERIOD=14;
public SawtoothTearDrawable(Context context){

View File

@@ -1715,7 +1715,9 @@ public class TabLayout extends HorizontalScrollView implements CustomViewHelper{
child.getLayoutParams().height);
int childWidthMeasureSpec =
MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY);
MeasureSpec.makeMeasureSpec(
getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
MeasureSpec.EXACTLY);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
}

View File

@@ -10,6 +10,7 @@ import android.widget.TextView;
import org.joinmastodon.android.MastodonApp;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.model.TimelineDefinition;
import java.util.EnumSet;
@@ -44,8 +45,8 @@ public class DiscoverInfoBannerHelper{
banner=((Activity)list.getContext()).getLayoutInflater().inflate(R.layout.discover_info_banner, list, false);
TextView text=banner.findViewById(R.id.banner_text);
text.setText(switch(type){
case TRENDING_POSTS -> list.getResources().getString(R.string.trending_posts_info_banner);
case TRENDING_LINKS -> list.getResources().getString(R.string.trending_links_info_banner);
case TRENDING_POSTS -> list.getResources().getString(R.string.sk_trending_posts_info_banner);
case TRENDING_LINKS -> list.getResources().getString(R.string.sk_trending_links_info_banner);
case FEDERATED_TIMELINE -> list.getResources().getString(R.string.sk_federated_timeline_info_banner);
case POST_NOTIFICATIONS -> list.getResources().getString(R.string.sk_notify_posts_info_banner);
case BUBBLE_TIMELINE -> list.getResources().getString(R.string.sk_bubble_timeline_info_banner);
@@ -57,8 +58,10 @@ public class DiscoverInfoBannerHelper{
case TRENDING_POSTS -> R.drawable.ic_fluent_arrow_trending_24_regular;
case TRENDING_LINKS -> R.drawable.ic_fluent_news_24_regular;
case ACCOUNTS -> R.drawable.ic_fluent_people_add_24_regular;
// no icon because those are displayed as timelines - with icon in top left
case LOCAL_TIMELINE, FEDERATED_TIMELINE, BUBBLE_TIMELINE, POST_NOTIFICATIONS -> 0;
case LOCAL_TIMELINE -> TimelineDefinition.LOCAL_TIMELINE.getDefaultIcon().iconRes;
case FEDERATED_TIMELINE -> TimelineDefinition.FEDERATED_TIMELINE.getDefaultIcon().iconRes;
case BUBBLE_TIMELINE -> TimelineDefinition.BUBBLE_TIMELINE.getDefaultIcon().iconRes;
case POST_NOTIFICATIONS -> TimelineDefinition.POSTS_TIMELINE.getDefaultIcon().iconRes;
});
adapter.addAdapter(new SingleViewRecyclerAdapter(banner));
}

View File

@@ -84,23 +84,18 @@ public class InsetStatusItemDecoration extends RecyclerView.ItemDecoration{
@Override
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state){
List<StatusDisplayItem> displayItems=listFragment.getDisplayItems();
// List<StatusDisplayItem> displayItems=listFragment.getDisplayItems();
RecyclerView.ViewHolder holder=parent.getChildViewHolder(view);
if(holder instanceof StatusDisplayItem.Holder<?> sdi){
boolean inset=sdi.getItem().inset;
int pos=holder.getAbsoluteAdapterPosition();
// int pos=holder.getAbsoluteAdapterPosition();
if(inset){
boolean topSiblingInset=pos>0 && displayItems.get(pos-1).inset;
boolean bottomSiblingInset=pos<displayItems.size()-1 && displayItems.get(pos+1).inset;
int pad;
// boolean topSiblingInset=pos>0 && displayItems.get(pos-1).inset;
// boolean bottomSiblingInset=pos<displayItems.size()-1 && displayItems.get(pos+1).inset;
// if(holder instanceof MediaGridStatusDisplayItem.Holder || holder instanceof LinkCardStatusDisplayItem.Holder)
pad=V.dp(16);
// else
// pad=V.dp(12);
boolean insetPadding=((StatusDisplayItem.Holder<?>) holder).getItem().insetPadding;
if(insetPadding)
int pad=V.dp(16);
// else pad=V.dp(12);
outRect.left=pad;
if(insetPadding)
outRect.right=pad;
// had to comment this out because animations with offsets aren't handled properly.

View File

@@ -0,0 +1,56 @@
package org.joinmastodon.android.ui.utils;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import org.joinmastodon.android.GlobalUserPreferences;
import org.joinmastodon.android.R;
import org.joinmastodon.android.model.Attachment;
import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.ui.displayitems.MediaGridStatusDisplayItem;
import org.joinmastodon.android.ui.drawables.BlurhashCrossfadeDrawable;
import org.joinmastodon.android.ui.drawables.PlayIconDrawable;
public class PreviewlessMediaAttachmentViewController{
public final View view;
public final MediaGridStatusDisplayItem.GridItemType type;
private final TextView title, domain;
public final View inner;
private final ImageView icon;
private final Context context;
private Status status;
public PreviewlessMediaAttachmentViewController(Context context, MediaGridStatusDisplayItem.GridItemType type){
view=context.getSystemService(LayoutInflater.class).inflate(R.layout.display_item_file, null);
title=view.findViewById(R.id.title);
domain=view.findViewById(R.id.domain);
icon=view.findViewById(R.id.imageView);
inner=view.findViewById(R.id.inner);
this.context=context;
this.type=type;
}
public void bind(Attachment attachment, Status status){
this.status=status;
title.setText(attachment.description != null
? attachment.description
: context.getString(R.string.sk_no_alt_text));
title.setSingleLine(false);
domain.setText(status.sensitive ? context.getString(R.string.sensitive_content_explain) : null);
domain.setVisibility(status.sensitive ? View.VISIBLE : View.GONE);
if(attachment.type == Attachment.Type.IMAGE)
icon.setImageDrawable(context.getDrawable(R.drawable.ic_fluent_image_24_regular));
if(attachment.type == Attachment.Type.VIDEO)
icon.setImageDrawable(context.getDrawable(R.drawable.ic_fluent_video_clip_24_regular));
if(attachment.type == Attachment.Type.GIFV)
icon.setImageDrawable(context.getDrawable(R.drawable.ic_fluent_gif_24_regular));
}
}

View File

@@ -54,6 +54,8 @@ import android.view.SubMenu;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowInsets;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.webkit.MimeTypeMap;
import android.widget.Button;
import android.widget.ImageView;
@@ -97,7 +99,6 @@ import org.joinmastodon.android.fragments.ComposeFragment;
import org.joinmastodon.android.fragments.HashtagTimelineFragment;
import org.joinmastodon.android.fragments.ProfileFragment;
import org.joinmastodon.android.fragments.ThreadFragment;
import org.joinmastodon.android.fragments.settings.SettingsServerAboutFragment;
import org.joinmastodon.android.fragments.settings.SettingsServerFragment;
import org.joinmastodon.android.model.Account;
import org.joinmastodon.android.model.AccountField;
@@ -175,6 +176,19 @@ public class UiUtils {
private static final DateTimeFormatter TIME_FORMATTER=DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT);
public static int MAX_WIDTH, SCROLL_TO_TOP_DELTA;
public static final float ALPHA_PRESSED=0.55f;
public static final Animation opacityOut, opacityIn;
static {
opacityOut = new AlphaAnimation(1, ALPHA_PRESSED);
opacityOut.setDuration(300);
opacityOut.setInterpolator(CubicBezierInterpolator.DEFAULT);
opacityOut.setFillAfter(true);
opacityIn = new AlphaAnimation(ALPHA_PRESSED, 1);
opacityIn.setDuration(400);
opacityIn.setInterpolator(CubicBezierInterpolator.DEFAULT);
}
private UiUtils() {
}
@@ -194,28 +208,33 @@ public class UiUtils {
}
public static String formatRelativeTimestamp(Context context, Instant instant) {
long t = instant.toEpochMilli();
long now = System.currentTimeMillis();
return formatPeriodBetween(context, instant, null);
}
public static String formatPeriodBetween(Context context, Instant since, Instant until) {
boolean ago = until == null;
long t = since.toEpochMilli();
long now = ago ? System.currentTimeMillis() : until.toEpochMilli();
long diff = now - t;
if(diff<1000L){
return context.getString(R.string.time_now);
}else if(diff<60_000L){
return context.getString(R.string.time_seconds_ago_short, diff/1000L);
return context.getString(ago ? R.string.time_seconds_ago_short : R.string.sk_time_seconds, diff/1000L);
}else if(diff<3600_000L){
return context.getString(R.string.time_minutes_ago_short, diff/60_000L);
return context.getString(ago ? R.string.time_minutes_ago_short : R.string.sk_time_minutes, diff/60_000L);
}else if(diff<3600_000L*24L){
return context.getString(R.string.time_hours_ago_short, diff/3600_000L);
return context.getString(ago ? R.string.time_hours_ago_short : R.string.sk_time_hours, diff/3600_000L);
} else {
int days = (int) (diff / (3600_000L * 24L));
if (days > 30) {
ZonedDateTime dt = instant.atZone(ZoneId.systemDefault());
if (ago && days > 30) {
ZonedDateTime dt = since.atZone(ZoneId.systemDefault());
if (dt.getYear() == ZonedDateTime.now().getYear()) {
return DATE_FORMATTER_SHORT.format(dt);
} else {
return DATE_FORMATTER_SHORT_WITH_YEAR.format(dt);
}
}
return context.getString(R.string.time_days_ago_short, days);
return context.getString(ago ? R.string.time_days_ago_short : R.string.sk_time_days, days);
}
}
@@ -792,7 +811,7 @@ public class UiUtils {
confirmToggleMuteUser(activity, accountID, account, true, resultCallback);
} else if (!relationship.following && !relationship.requested) {
follow(activity, accountID, account, true, progressCallback, resultCallback);
} else {
} else if (GlobalUserPreferences.confirmUnfollow){
showConfirmationAlert(activity,
activity.getString(R.string.mo_confirm_unfollow_title),
activity.getString(R.string.mo_confirm_unfollow, account.getDisplayUsername()),
@@ -800,8 +819,9 @@ public class UiUtils {
0,
() -> follow(activity, accountID, account, false, progressCallback, resultCallback),
() -> progressCallback.accept(false));
} else {
follow(activity, accountID, account, false, progressCallback, resultCallback);
}
}
private static void follow(Activity activity, String accountID, Account account, boolean followed, Consumer<Boolean> progressCallback, Consumer<Relationship> resultCallback) {

View File

@@ -27,10 +27,14 @@ import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.joinmastodon.android.MastodonApp;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.MastodonAPIController;
import org.joinmastodon.android.api.ProgressListener;
import org.joinmastodon.android.api.requests.statuses.CreateStatus;
import org.joinmastodon.android.api.requests.statuses.GetAttachmentByID;
import org.joinmastodon.android.api.requests.statuses.UpdateAttachment;
import org.joinmastodon.android.api.requests.statuses.UploadAttachment;
@@ -47,8 +51,11 @@ import org.parceler.Parcel;
import org.parceler.Parcels;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Locale;
import java.util.Objects;
import java.util.function.Consumer;
@@ -551,6 +558,14 @@ public class ComposeMediaViewController{
return attachments.stream().map(a->a.serverAttachment.id).collect(Collectors.toList());
}
public List<CreateStatus.Request.MediaAttribute> getAttachmentAttributes(){
List<CreateStatus.Request.MediaAttribute> mediaAttributes = new ArrayList<>();
for (DraftMediaAttachment att:attachments){
mediaAttributes.add(new CreateStatus.Request.MediaAttribute(att.serverAttachment.id, att.description, null));
}
return mediaAttributes;
}
public boolean isEmpty(){
return attachments.isEmpty();
}
@@ -592,7 +607,7 @@ public class ComposeMediaViewController{
public void saveAltTextsBeforePublishing(Runnable onSuccess, Consumer<ErrorResponse> onError){
ArrayList<UpdateAttachment> updateAltTextRequests=new ArrayList<>();
for(DraftMediaAttachment att:attachments){
if(!att.descriptionSaved){
if(!att.descriptionSaved && (fragment.editingStatus==null || !fragment.editingStatus.mediaAttachments.contains(att.serverAttachment))){
UpdateAttachment req=new UpdateAttachment(att.serverAttachment.id, att.description);
req.setCallback(new Callback<>(){
@Override

View File

@@ -213,6 +213,7 @@ public class AccountViewHolder extends BindableViewHolder<AccountViewModel> impl
Account account=item.account;
menu.findItem(R.id.share).setTitle(fragment.getString(R.string.share_user, account.getDisplayUsername()));
menu.findItem(R.id.manage_user_lists).setTitle(fragment.getString(R.string.sk_lists_with_user, account.getShortUsername()));
menu.findItem(R.id.mute).setTitle(fragment.getString(relationship.muting ? R.string.unmute_user : R.string.mute_user, account.getDisplayUsername()));
menu.findItem(R.id.block).setTitle(fragment.getString(relationship.blocking ? R.string.unblock_user : R.string.block_user, account.getDisplayUsername()));
menu.findItem(R.id.report).setTitle(fragment.getString(R.string.report_user, account.getDisplayUsername()));

View File

@@ -40,11 +40,9 @@ public abstract class ListItemViewHolder<T extends ListItem<?>> extends Bindable
if(TextUtils.isEmpty(item.subtitle) && item.subtitleRes==0){
subtitle.setVisibility(View.GONE);
title.setMaxLines(2);
view.setMinimumHeight(V.dp(56));
}else{
subtitle.setVisibility(View.VISIBLE);
title.setMaxLines(1);
view.setMinimumHeight(V.dp(72));
if(TextUtils.isEmpty(item.subtitle))
subtitle.setText(item.subtitleRes);

View File

@@ -1,6 +1,16 @@
package org.joinmastodon.android.utils;
import static org.joinmastodon.android.model.FilterAction.HIDE;
import static org.joinmastodon.android.model.FilterAction.WARN;
import static org.joinmastodon.android.model.FilterContext.ACCOUNT;
import static org.joinmastodon.android.model.FilterContext.HOME;
import static org.joinmastodon.android.model.FilterContext.NOTIFICATIONS;
import static org.joinmastodon.android.model.FilterContext.PUBLIC;
import static org.joinmastodon.android.model.FilterContext.THREAD;
import org.joinmastodon.android.GlobalUserPreferences;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.model.AltTextFilter;
import org.joinmastodon.android.model.LegacyFilter;
import org.joinmastodon.android.model.FilterAction;
import org.joinmastodon.android.model.FilterContext;
@@ -14,6 +24,7 @@ import java.util.stream.Collectors;
import java.util.stream.Stream;
public class StatusFilterPredicate implements Predicate<Status>{
private final List<LegacyFilter> clientFilters;
private final List<LegacyFilter> filters;
private final FilterContext context;
private final FilterAction action;
@@ -28,10 +39,12 @@ public class StatusFilterPredicate implements Predicate<Status>{
this.filters = filters;
this.context = context;
this.action = action;
this.clientFilters = GlobalUserPreferences.showPostsWithoutAlt ? List.of()
: List.of(new AltTextFilter(WARN, HOME, PUBLIC, ACCOUNT, THREAD, NOTIFICATIONS));
}
public StatusFilterPredicate(List<LegacyFilter> filters, FilterContext context){
this(filters, context, FilterAction.HIDE);
this(filters, context, HIDE);
}
/**
@@ -43,13 +56,15 @@ public class StatusFilterPredicate implements Predicate<Status>{
filters=AccountSessionManager.getInstance().getAccount(accountID).wordFilters.stream().filter(f->f.context.contains(context)).collect(Collectors.toList());
this.context = context;
this.action = action;
this.clientFilters = GlobalUserPreferences.showPostsWithoutAlt ? List.of()
: List.of(new AltTextFilter(WARN, HOME, PUBLIC, ACCOUNT, THREAD, NOTIFICATIONS));
}
/**
* @param context null makes the predicate pass automatically
*/
public StatusFilterPredicate(String accountID, FilterContext context){
this(accountID, context, FilterAction.HIDE);
this(accountID, context, HIDE);
}
/**
@@ -74,9 +89,18 @@ public class StatusFilterPredicate implements Predicate<Status>{
// only apply filters for given context
.filter(filter -> filter.context.contains(context))
// treating filterAction = null (from filters list) as FilterAction.HIDE
.filter(filter -> filter.filterAction == null ? action == FilterAction.HIDE : filter.filterAction == action)
.filter(filter -> filter.filterAction == null ? action == HIDE : filter.filterAction == action)
.findAny();
//Apply client filters if no server filter is triggered
if (applyingFilter.isEmpty() && !clientFilters.isEmpty()) {
applyingFilter = clientFilters.stream()
.filter(filter -> filter.context.contains(context))
.filter(filter -> filter.filterAction == null ? action == HIDE : filter.filterAction == action)
.filter(filter -> filter.matches(status))
.findAny();
}
this.applyingFilter = applyingFilter.orElse(null);
return applyingFilter.isEmpty();
}

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="?bookmark_selected" android:state_selected="true"/>
<item android:color="?attr/colorBookmark" android:state_selected="true"/>
<item android:color="?android:textColorSecondary"/>
</selector>

View File

@@ -2,5 +2,5 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="?colorM3Primary" android:state_selected="true"/>
<item android:color="?colorM3OnBackground" android:state_enabled="true"/>
<item android:color="@color/m3_on_background_alpha38" />
<item android:color="?android:textColorSecondary"/>
</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="?favorite_selected" android:state_selected="true"/>
<item android:color="?colorFavorite" android:state_selected="true"/>
<item android:color="?android:textColorSecondary"/>
</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="?colorM3OnSecondaryContainer" android:alpha="@dimen/overlay_ripple_alpha"/>
<item android:color="?colorM3OnSecondaryContainer" 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="?colorM3OnSurface" android:alpha="0.12"/>
</selector>

View File

@@ -14,7 +14,7 @@
<layer-list>
<item android:gravity="center_vertical" android:height="40dp">
<shape>
<solid android:color="@color/m3_on_surface_overlay"/>
<solid android:color="@color/m3_on_surface_alpha12"/>
<corners android:radius="20dp"/>
</shape>
</item>

View File

@@ -3,13 +3,13 @@
<item>
<shape>
<solid android:color="?colorM3Surface"/>
<corners android:radius="4dp"/>
<corners android:radius="12dp"/>
</shape>
</item>
<item>
<shape android:tint="?colorM3Primary">
<solid android:color="#14000000"/>
<corners android:radius="4dp"/>
<corners android:radius="12dp"/>
<padding android:top="8dp" android:bottom="8dp"/>
</shape>
</item>

View File

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

View File

@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<color android:color="?colorM3SurfaceVariant"/>
<color android:color="?colorM3SecondaryContainer"/>
</item>
<item android:drawable="?android:selectableItemBackground"/>
</layer-list>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:left="-1dp" android:right="-1dp">
<shape android:shape="rectangle">
<solid android:color="?colorM3Surface" />
<stroke android:color="?colorM3OutlineVariant" android:width="1dp" />
</shape>
</item>
</layer-list>

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="M13.72 5.78c0.293 0.293 0.767 0.293 1.06 0 0.293-0.293 0.293-0.767 0-1.06l-2.5-2.5c-0.293-0.293-0.767-0.293-1.06 0l-2.5 2.5c-0.293 0.293-0.293 0.767 0 1.06 0.293 0.293 0.767 0.293 1.06 0L11 4.56v4.19c0 0.414 0.336 0.75 0.75 0.75s0.75-0.336 0.75-0.75V4.56l1.22 1.22zM4 11.75C4 11.336 4.336 11 4.75 11h14.5c0.414 0 0.75 0.336 0.75 0.75s-0.336 0.75-0.75 0.75H4.75C4.336 12.5 4 12.164 4 11.75zm8.5 3c0-0.414-0.336-0.75-0.75-0.75S11 14.336 11 14.75v4.69l-1.22-1.22c-0.293-0.293-0.767-0.293-1.06 0-0.293 0.293-0.293 0.767 0 1.06l2.5 2.5c0.293 0.293 0.767 0.293 1.06 0l2.5-2.5c0.293-0.293 0.293-0.767 0-1.06-0.293-0.293-0.767-0.293-1.06 0l-1.22 1.22v-4.69z" 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="M8.46 1.897l0.99 0.39c0.353 0.138 0.746 0.138 1.099 0l0.99-0.39c1.21-0.477 2.582 0.091 3.102 1.285l0.424 0.975c0.151 0.348 0.429 0.626 0.777 0.777l0.975 0.424c1.194 0.52 1.762 1.891 1.285 3.103l-0.39 0.99c-0.139 0.352-0.139 0.745 0 1.098l0.39 0.99c0.477 1.21-0.091 2.582-1.285 3.102l-0.975 0.424c-0.348 0.151-0.626 0.429-0.777 0.777l-0.424 0.975c-0.52 1.194-1.891 1.762-3.103 1.285l-0.99-0.39c-0.352-0.139-0.745-0.139-1.098 0l-0.99 0.39c-1.21 0.477-2.582-0.091-3.102-1.285l-0.424-0.975c-0.151-0.348-0.429-0.626-0.777-0.777l-0.975-0.424c-1.194-0.52-1.762-1.891-1.285-3.103l0.39-0.99c0.138-0.352 0.138-0.745 0-1.098l-0.39-0.99C1.42 7.25 1.988 5.878 3.182 5.358l0.975-0.424c0.348-0.151 0.626-0.429 0.777-0.777l0.424-0.975C5.878 1.988 7.25 1.42 8.461 1.897zm3.445 0.93l-0.99 0.39c-0.588 0.232-1.243 0.232-1.831 0l-0.99-0.39C7.384 2.549 6.58 2.881 6.275 3.582L5.851 4.556C5.599 5.136 5.136 5.6 4.556 5.851L3.581 6.275c-0.7 0.305-1.033 1.109-0.753 1.82l0.389 0.989c0.232 0.588 0.232 1.243 0 1.831l-0.39 0.99c-0.279 0.71 0.054 1.514 0.754 1.819l0.975 0.424c0.58 0.252 1.043 0.715 1.295 1.295l0.424 0.975c0.305 0.7 1.109 1.033 1.82 0.753l0.989-0.39c0.588-0.23 1.243-0.23 1.831 0l0.99 0.39c0.71 0.28 1.514-0.053 1.819-0.753l0.424-0.975c0.252-0.58 0.715-1.043 1.295-1.295l0.975-0.424c0.7-0.305 1.033-1.11 0.753-1.82l-0.39-0.989c-0.23-0.588-0.23-1.243 0-1.831l0.39-0.99c0.28-0.71-0.053-1.514-0.753-1.819l-0.975-0.424c-0.58-0.252-1.043-0.715-1.295-1.295l-0.424-0.975c-0.305-0.7-1.11-1.033-1.82-0.753zm-2.927 8.944l3.648-4.104c0.183-0.206 0.5-0.225 0.706-0.041 0.183 0.163 0.218 0.43 0.095 0.633l-0.054 0.073-4 4.5c-0.17 0.19-0.451 0.22-0.655 0.081l-0.072-0.06-2-2c-0.195-0.195-0.195-0.512 0-0.707 0.173-0.174 0.443-0.193 0.638-0.058l0.069 0.058 1.625 1.625 3.648-4.104-3.648 4.104z" 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="M15.794 8.733c0.286 0.3 0.274 0.774-0.026 1.06l-5.25 5.001c-0.29 0.276-0.745 0.276-1.035 0l-5.25-5c-0.3-0.287-0.312-0.761-0.026-1.061 0.286-0.3 0.76-0.312 1.06-0.026l4.734 4.508 4.733-4.508c0.3-0.286 0.774-0.274 1.06 0.026zm0-4c0.286 0.3 0.274 0.774-0.026 1.06l-5.25 5.001c-0.29 0.276-0.745 0.276-1.035 0l-5.25-5c-0.3-0.287-0.312-0.761-0.026-1.061 0.286-0.3 0.76-0.312 1.06-0.026l4.734 4.509 4.733-4.51c0.3-0.285 0.774-0.273 1.06 0.027z" 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="M4.207 15.267c-0.286-0.3-0.274-0.774 0.026-1.06l5.25-5.002c0.29-0.275 0.745-0.275 1.034 0l5.25 5.002c0.3 0.286 0.312 0.76 0.026 1.06-0.286 0.3-0.76 0.312-1.06 0.026L10 10.784l-4.733 4.51c-0.3 0.285-0.774 0.273-1.06-0.027zm0-4.998c-0.286-0.3-0.274-0.775 0.026-1.06l5.25-5.002c0.29-0.276 0.745-0.276 1.034 0l5.25 5.001c0.3 0.286 0.312 0.76 0.026 1.06-0.286 0.3-0.76 0.312-1.06 0.026L10 5.786l-4.733 4.508c-0.3 0.286-0.774 0.275-1.06-0.025z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -1,3 +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="M15.25 13.5h-4c-0.414 0-0.75-0.336-0.75-0.75v-6C10.5 6.336 10.836 6 11.25 6S12 6.336 12 6.75V12h3.25c0.414 0 0.75 0.336 0.75 0.75s-0.336 0.75-0.75 0.75zM12 2C6.478 2 2 6.478 2 12s4.478 10 10 10 10-4.478 10-10S17.522 2 12 2z" android:fillColor="@color/fluent_default_icon_tint"/>
<path android:pathData="M15.25 13.5h-4c-0.414 0-0.75-0.336-0.75-0.75v-6C10.5 6.336 10.836 6 11.25 6S12 6.336 12 6.75V12h3.25c0.414 0 0.75 0.336 0.75 0.75s-0.336 0.75-0.75 0.75zM12 2C6.478 2 2 6.478 2 12s4.478 10 10 10 10-4.478 10-10S17.522 2 12 2z" android:fillColor="?colorM3Surface"/>
</vector>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/ic_fluent_eye_off_24_regular"/>
<item android:drawable="@drawable/ic_fluent_important_24_regular" android:top="14sp" android:width="10sp" android:height="10sp"/>
</layer-list>

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="M13.748 8.996c0.69 0 1.248-0.559 1.248-1.248 0-0.69-0.559-1.248-1.248-1.248-0.69 0-1.248 0.559-1.248 1.248 0 0.69 0.559 1.248 1.248 1.248zM6.25 3C4.455 3 3 4.455 3 6.25v9c0 1.795 1.455 3.25 3.25 3.25h9c1.795 0 3.25-1.455 3.25-3.25v-9C18.5 4.455 17.045 3 15.25 3h-9zM4.5 6.25c0-0.966 0.784-1.75 1.75-1.75h9C16.216 4.5 17 5.284 17 6.25v9c0 0.231-0.045 0.452-0.126 0.654l-4.587-4.291c-0.865-0.81-2.21-0.81-3.075 0l-4.586 4.29C4.545 15.701 4.5 15.481 4.5 15.25v-9zm6.762 6.458l4.505 4.214C15.604 16.972 15.43 17 15.25 17h-9c-0.18 0-0.354-0.027-0.518-0.078l4.505-4.214c0.289-0.27 0.737-0.27 1.025 0zM8.75 21c-1.15 0-2.162-0.598-2.74-1.5h9.74c2.071 0 3.75-1.679 3.75-3.75V6.011C20.402 6.59 21 7.6 21 8.751v7C21 18.65 18.65 21 15.75 21h-7z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

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="M6.75,3H5.75C4.231,3 3,4.231 3,5.75V18.25C3,19.769 4.231,21 5.75,21H6V15C6,13.757 7.007,12.75 8.25,12.75H15.75C16.993,12.75 18,13.757 18,15V21H18.25C19.769,21 21,19.769 21,18.25V8.286C21,7.424 20.658,6.597 20.048,5.987L18.013,3.952C17.411,3.351 16.599,3.009 15.75,3V7.5C15.75,8.743 14.743,9.75 13.5,9.75H9C7.757,9.75 6.75,8.743 6.75,7.5V3ZM14.25,3V7.5C14.25,7.914 13.914,8.25 13.5,8.25H9C8.586,8.25 8.25,7.914 8.25,7.5V3H14.25ZM16.5,21V15C16.5,14.586 16.164,14.25 15.75,14.25H8.25C7.836,14.25 7.5,14.586 7.5,15V21H16.5Z"
android:fillColor="?colorM3Surface"/>
</vector>

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:drawable="@drawable/ic_fluent_save_24_filled" android:state_enabled="true"/>
<item android:drawable="@drawable/ic_fluent_save_24_filled_enabled" android:state_enabled="true"/>
<item android:drawable="@drawable/ic_fluent_save_24_regular"/>
</selector>

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="M2,6.25C2,4.455 3.455,3 5.25,3H18.75C20.545,3 22,4.455 22,6.25V17.75C22,19.545 20.545,21 18.75,21H5.25C3.455,21 2,19.545 2,17.75V6.25ZM5.25,4.5C4.284,4.5 3.5,5.284 3.5,6.25V17.75C3.5,18.716 4.284,19.5 5.25,19.5H18.75C19.716,19.5 20.5,18.716 20.5,17.75V6.25C20.5,5.284 19.716,4.5 18.75,4.5H5.25ZM9,9.251V14.75C9,15.511 9.815,15.993 10.482,15.626L15.481,12.877C16.172,12.497 16.172,11.504 15.481,11.124L10.482,8.374C9.815,8.008 9,8.49 9,9.251Z"
android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="16dp" android:height="16dp" android:viewportWidth="16" android:viewportHeight="16">
<group android:translateX="-2" android:translateY="-1">
<path android:pathData="M10 2c1.657 0 3 1.343 3 3v1h1c1.105 0 2 0.895 2 2v7c0 1.105-0.895 2-2 2H6c-1.105 0-2-0.895-2-2V8c0-1.105 0.895-2 2-2h1V5c0-1.657 1.343-3 3-3zm0 8.5c-0.552 0-1 0.448-1 1s0.448 1 1 1 1-0.448 1-1-0.448-1-1-1zM10 4C9.448 4 9 4.448 9 5v1h2V5c0-0.552-0.448-1-1-1z" android:fillColor="@color/fluent_default_icon_tint"/>
</group>
</vector>

View File

@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="20dp"
android:height="20dp"
android:viewportWidth="20"
android:viewportHeight="20">
<path
android:fillColor="@android:color/white"
android:pathData="M5.5,19Q4.875,19 4.438,18.562Q4,18.125 4,17.5V9.5Q4,8.875 4.438,8.438Q4.875,8 5.5,8H6V6Q6,4.333 7.167,3.167Q8.333,2 10,2Q11.667,2 12.833,3.167Q14,4.333 14,6V8H14.5Q15.125,8 15.562,8.438Q16,8.875 16,9.5V17.5Q16,18.125 15.562,18.562Q15.125,19 14.5,19ZM7.5,8H12.5V6Q12.5,4.958 11.771,4.229Q11.042,3.5 10,3.5Q8.958,3.5 8.229,4.229Q7.5,4.958 7.5,6ZM10,15Q10.625,15 11.062,14.562Q11.5,14.125 11.5,13.5Q11.5,12.875 11.062,12.438Q10.625,12 10,12Q9.375,12 8.938,12.438Q8.5,12.875 8.5,13.5Q8.5,14.125 8.938,14.562Q9.375,15 10,15Z"/>
</vector>

View File

@@ -5,7 +5,9 @@
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:layout_marginHorizontal="16dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="8dp"
android:padding="16dp"
android:background="@drawable/rect_12dp"
android:backgroundTint="?colorM3SurfaceVariant">
@@ -15,7 +17,7 @@
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_marginEnd="16dp"
android:tint="?colorM3OnPrimaryContainer"
android:tint="?colorM3Primary"
android:scaleType="center"
android:importantForAccessibility="no"
tools:src="@drawable/ic_fluent_arrow_trending_24_regular"/>

View File

@@ -43,6 +43,7 @@
android:layout_height="wrap_content"
android:layout_marginStart="-4dp"
android:paddingStart="8dp"
android:paddingEnd="3dp"
android:clipToPadding="false"
android:requiresFadingEdge="horizontal"
android:fadingEdgeLength="24dp" />

View File

@@ -5,7 +5,7 @@
android:padding="8dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?colorM3SurfaceVariant">
android:background="@drawable/bg_m3_surface3">
<org.joinmastodon.android.ui.views.AutoOrientationLinearLayout
android:id="@+id/button_bar"

View File

@@ -1,24 +1,79 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="75dp"
android:background="@drawable/bg_timeline_gap">
<TextView
android:id="@+id/text"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:gravity="center_horizontal"
android:textAppearance="@style/m3_body_large"
android:textColor="?android:textColorSecondary"
android:text="@string/load_missing_posts"/>
android:orientation="vertical"
android:background="@drawable/bg_timeline_gap_border">
<FrameLayout
android:id="@+id/top"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:background="?android:selectableItemBackground">
<ProgressBar
android:id="@+id/progress"
android:layout_width="32dp"
android:layout_height="32dp"
android:id="@+id/progress_top"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center"
android:visibility="gone"/>
</FrameLayout>
<TextView
android:id="@+id/text_top"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:paddingHorizontal="16dp"
android:paddingTop="24dp"
android:paddingBottom="20dp"
android:drawablePadding="16dp"
android:drawableEnd="@drawable/ic_fluent_chevron_double_down_20_filled"
android:textAppearance="@style/m3_title_medium"
android:textColor="?android:textColorSecondary"
android:text="@string/sk_load_missing_posts_below"/>
</FrameLayout>
<TextView
android:id="@+id/gap"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="20dp"
android:gravity="center"
android:textStyle="italic"
android:textColor="?colorM3Primary"
android:textAppearance="@style/m3_label_large"
android:background="@drawable/bg_timeline_gap"/>
<FrameLayout
android:id="@+id/bottom"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:background="?android:selectableItemBackground">
<ProgressBar
android:id="@+id/progress_bottom"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center"
android:visibility="gone"/>
<TextView
android:id="@+id/text_bottom"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:paddingHorizontal="16dp"
android:paddingTop="20dp"
android:paddingBottom="24dp"
android:drawablePadding="16dp"
android:drawableEnd="@drawable/ic_fluent_chevron_double_up_20_filled"
android:textAppearance="@style/m3_title_medium"
android:textColor="?android:textColorSecondary"
android:text="@string/sk_load_missing_posts_above"/>
</FrameLayout>
</LinearLayout>

View File

@@ -173,6 +173,17 @@
android:textColor="?colorM3OnSurfaceVariant"
tools:text="\@Gargron@mastodon.social"/>
<ImageView
android:id="@+id/bot_icon"
android:layout_width="16sp"
android:layout_height="16sp"
android:layout_marginStart="4sp"
android:layout_gravity="center_vertical"
android:visibility="gone"
android:importantForAccessibility="no"
android:contentDescription="@string/sk_icon_bot"
android:src="@drawable/ic_fluent_bot_20_filled" />
<TextView
android:id="@+id/separator"
android:layout_width="wrap_content"

View File

@@ -8,83 +8,33 @@
android:paddingLeft="16dp"
android:clipToPadding="false">
<FrameLayout
android:id="@+id/checkbox_wrap"
android:layout_width="wrap_content"
android:layout_height="46sp"
android:duplicateParentState="true">
<View
android:id="@+id/checkbox"
android:layout_gravity="center_vertical"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_marginStart="-4dp"
android:layout_marginTop="-8dp"
android:layout_marginTop="0dp"
android:layout_marginEnd="12dp"
android:duplicateParentState="true"/>
<ImageView
android:id="@+id/more"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_alignParentTop="true"
android:layout_alignParentEnd="true"
android:layout_marginTop="-2dp"
android:layout_marginEnd="-2dp"
android:background="?android:selectableItemBackgroundBorderless"
android:scaleType="center"
android:tint="?colorM3OnSurfaceVariant"
android:contentDescription="@string/more_options"
android:src="@drawable/ic_fluent_more_vertical_20_filled" />
</FrameLayout>
<ImageView
android:id="@+id/avatar"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_alignParentTop="true"
android:layout_toEndOf="@id/checkbox"
android:layout_marginTop="2dp"
android:layout_marginEnd="8dp" />
<org.joinmastodon.android.ui.views.HeaderSubtitleLinearLayout
android:id="@+id/name_wrap"
<FrameLayout
android:layout_width="match_parent"
android:layout_height="24dp"
android:layout_toEndOf="@id/avatar"
android:layout_toStartOf="@id/more"
android:layout_marginEnd="8dp">
android:layout_height="wrap_content"
android:layout_marginTop="-16dp"
android:layout_marginStart="-16dp"
android:layout_toEndOf="@id/checkbox_wrap">
<TextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="24dp"
android:ellipsize="end"
android:singleLine="true"
android:textAppearance="@style/m3_title_medium"
android:textColor="?colorM3OnSurface"
android:gravity="start|center_vertical"
tools:text="Eugen" />
<include layout="@layout/display_item_header" />
<TextView
android:id="@+id/extra_text"
android:layout_width="wrap_content"
android:layout_height="24dp"
android:layout_marginStart="8dp"
android:ellipsize="end"
android:singleLine="true"
android:textAppearance="@style/m3_title_medium"
android:fontFamily="sans-serif"
android:textAlignment="viewStart"
android:textColor="?colorM3OnSurface"
tools:text="boosted your cat picture" />
</org.joinmastodon.android.ui.views.HeaderSubtitleLinearLayout>
<TextView
android:id="@+id/time_and_username"
android:layout_width="match_parent"
android:layout_height="20dp"
android:layout_below="@id/name_wrap"
android:layout_toEndOf="@id/avatar"
android:singleLine="true"
android:ellipsize="end"
android:textAppearance="@style/m3_title_small"
android:gravity="center_vertical"
android:textColor="?colorM3OnSurfaceVariant"
tools:text="9h ago · \@Gargron@mastodon.social"/>
</FrameLayout>
</org.joinmastodon.android.ui.views.CheckableRelativeLayout>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="horizontal"
android:layout_width="match_parent"
@@ -8,7 +8,7 @@
android:paddingTop="16dp"
android:paddingRight="16dp">
<LinearLayout
<RelativeLayout
android:id="@+id/spoiler_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -19,11 +19,26 @@
android:paddingRight="12dp"
android:paddingBottom="8dp">
<ImageView
android:id="@+id/media_icon"
android:layout_width="28dp"
android:layout_height="28dp"
android:layout_marginHorizontal="8dp"
android:layout_centerVertical="true"
android:layout_alignParentEnd="true"
android:scaleType="center"
android:contentDescription="@string/sk_post_contains_media"
android:src="@drawable/ic_fluent_image_24_regular"
android:tint="?colorM3OnSecondaryContainer" />
<TextView
android:id="@+id/spoiler_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_toStartOf="@id/media_icon"
android:textAppearance="@style/m3_body_large"
android:textColor="?colorM3OnSecondaryContainer"
tools:text="Spoilery stuff"/>
@@ -32,21 +47,14 @@
android:id="@+id/spoiler_action"
android:layout_width="match_parent"
android:layout_height="20dp"
android:layout_below="@id/spoiler_title"
android:layout_toStartOf="@id/media_icon"
android:textAppearance="@style/m3_label_large"
android:singleLine="true"
android:gravity="center_vertical"
android:textColor="?colorM3Primary"
tools:text="Re-hide"/>
</LinearLayout>
</RelativeLayout>
<ImageView
android:id="@+id/media_icon"
android:layout_width="36dp"
android:layout_height="36dp"
android:background="?android:actionBarItemBackground"
android:scaleType="center"
android:src="@drawable/ic_fluent_image_24_regular"
android:tint="?android:textColorSecondary" />
</LinearLayout>
</FrameLayout>

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