Compare commits

...

910 commits
3.6.3 ... main

Author SHA1 Message Date
a06fb1657d
Add support for Nyastodon-style emoji reactions 2023-02-08 21:13:16 +09:00
Thomas
97ba87aba7 Release 3.17.0 2023-02-05 18:59:05 +01:00
Thomas
7603db8ce0 Missing media description for previews 2023-02-05 18:53:05 +01:00
Thomas
663e33466d Merge remote-tracking branch 'origin/develop' into develop 2023-02-05 16:50:54 +01:00
Thomas
cbed3f1ae1 Fix media cannot be downloaded or shared (Android 10) 2023-02-05 16:50:08 +01:00
Poesty Li
8bdaf8d410
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (1077 of 1077 strings)

Co-authored-by: Poesty Li <poesty7450@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/zh_Hans/
Translation: Fedilab/Strings
2023-02-05 11:55:29 +01:00
Thomas
6595af849e Fix forward tags in replies 2023-02-05 11:55:19 +01:00
Thomas
4111d00025 Group mentions at the top 2023-02-05 11:00:34 +01:00
Thomas
f75d8258f4 Fix button sizes not updated 2023-02-04 17:53:25 +01:00
Thomas
7a11e156a5 Merge remote-tracking branch 'origin/develop' into develop 2023-02-04 11:23:56 +01:00
Thomas
8977990fea Cache messages 2023-02-04 11:23:31 +01:00
Poesty Li
bc44a9be15
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (1077 of 1077 strings)

Co-authored-by: Poesty Li <poesty7450@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/zh_Hans/
Translation: Fedilab/Strings
2023-02-03 18:36:46 +01:00
Eduardo
88d9bf4629
Translated using Weblate (Portuguese)
Currently translated at 89.8% (968 of 1077 strings)

Co-authored-by: Eduardo <edu200399lim@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/pt/
Translation: Fedilab/Strings
2023-02-03 18:36:45 +01:00
josé m
65706e727c
Translated using Weblate (Galician)
Currently translated at 100.0% (1077 of 1077 strings)

Co-authored-by: josé m <correoxm@disroot.org>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/gl/
Translation: Fedilab/Strings
2023-02-03 18:36:45 +01:00
Thomas
b64fd393e9 Fix worker 2023-02-03 17:42:50 +01:00
Thomas
2b300ceae4 record cache work 2023-02-03 17:23:33 +01:00
Thomas
6e4bb95dda More deep link detection 2023-02-02 18:03:09 +01:00
Thomas
440ad039be Fix issues 2023-02-02 17:44:16 +01:00
Thomas
4c89a855c6 Some fixes 2023-02-02 14:18:52 +01:00
Thomas
0ca53b75d2 Fix crashes when replying 2023-02-02 14:03:13 +01:00
Oğuz Ersen
91eb579e2c
Translated using Weblate (Turkish)
Currently translated at 100.0% (1077 of 1077 strings)

Co-authored-by: Oğuz Ersen <oguz@ersen.moe>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/tr/
Translation: Fedilab/Strings
2023-02-02 11:40:27 +01:00
Ajeje Brazorf
1aad4f07df
Translated using Weblate (Sardinian)
Currently translated at 99.2% (1069 of 1077 strings)

Co-authored-by: Ajeje Brazorf <lmelonimamo@yahoo.it>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/sc/
Translation: Fedilab/Strings
2023-02-02 11:40:26 +01:00
claleb
335842c8a2
Translated using Weblate (German)
Currently translated at 100.0% (1077 of 1077 strings)

Co-authored-by: claleb <weblate@claleb.de>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/de/
Translation: Fedilab/Strings
2023-02-02 11:40:26 +01:00
Lukáš Jelínek
c10d078add
Translated using Weblate (Czech)
Currently translated at 99.9% (1076 of 1077 strings)

Co-authored-by: Lukáš Jelínek <devel@aiken.cz>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/cs/
Translation: Fedilab/Strings
2023-02-02 11:40:26 +01:00
Poesty Li
67cdceb90e
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (1075 of 1075 strings)

Co-authored-by: Poesty Li <poesty7450@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/zh_Hans/
Translation: Fedilab/Strings
2023-02-01 19:01:52 +01:00
Thomas
ddbd3f8684 Release 3.16.4 2023-02-01 19:00:38 +01:00
Thomas
4ae8011eff Auto fetch messages 2023-02-01 18:52:55 +01:00
Thomas
d61dbb0315 Auto fetch messages 2023-02-01 17:56:20 +01:00
Thomas
3c13bf7199 Merge remote-tracking branch 'origin/develop' into develop 2023-02-01 15:16:42 +01:00
Thomas
b19cd7c0c9 Add settings 2023-02-01 15:14:37 +01:00
Dan
17438f6f5c
Translated using Weblate (Ukrainian)
Currently translated at 62.6% (668 of 1067 strings)

Co-authored-by: Dan <denqwerta@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/uk/
Translation: Fedilab/Strings
2023-02-01 13:54:31 +01:00
Oğuz Ersen
f05ab805ff
Translated using Weblate (Turkish)
Currently translated at 100.0% (1067 of 1067 strings)

Co-authored-by: Oğuz Ersen <oguz@ersen.moe>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/tr/
Translation: Fedilab/Strings
2023-02-01 13:54:31 +01:00
Lukáš Jelínek
212cd9d54e
Translated using Weblate (Czech)
Currently translated at 99.9% (1066 of 1067 strings)

Co-authored-by: Lukáš Jelínek <devel@aiken.cz>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/cs/
Translation: Fedilab/Strings
2023-02-01 13:54:31 +01:00
Dan
f4437b6955
Translated using Weblate (Ukrainian)
Currently translated at 15.4% (13 of 84 strings)

Co-authored-by: Dan <denqwerta@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/description/uk/
Translation: Fedilab/description
2023-02-01 13:54:07 +01:00
Thomas
e8950f03ae Fetch home in background to cache messages 2023-02-01 12:33:43 +01:00
0xd9a
0e16cb1730 update schedule dialog 2023-02-01 06:41:33 +05:30
Dan
67cfa9cf01
Translated using Weblate (Ukrainian)
Currently translated at 11.9% (10 of 84 strings)

Co-authored-by: Dan <denqwerta@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/description/uk/
Translation: Fedilab/description
2023-02-01 00:55:43 +01:00
Ajeje Brazorf
7bdf2aa1ad
Translated using Weblate (Sardinian)
Currently translated at 99.2% (1059 of 1067 strings)

Translated using Weblate (Sardinian)

Currently translated at 99.2% (1058 of 1066 strings)

Co-authored-by: Ajeje Brazorf <lmelonimamo@yahoo.it>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/sc/
Translation: Fedilab/Strings
2023-01-31 18:36:50 +01:00
Thomas
44676707f0 Automatically switch to tabs when searching 2023-01-31 18:36:37 +01:00
Thomas
d9e92b13b6 avoid a crash 2023-01-31 18:16:01 +01:00
Thomas
833ea5d0c3 Fix 103 response code for OG 2023-01-31 18:11:22 +01:00
Thomas
04c5f32c53 Merge remote-tracking branch 'origin/develop' into develop 2023-01-31 17:21:15 +01:00
Thomas
93d5995ec1 remove title for media description #771 2023-01-31 17:21:01 +01:00
Eduardo
d679d1fb73
Translated using Weblate (Portuguese)
Currently translated at 90.2% (962 of 1066 strings)

Co-authored-by: Eduardo <edu200399lim@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/pt/
Translation: Fedilab/Strings
2023-01-31 16:41:31 +01:00
Thomas
f8b0ed7f18 Fix some crashes 2023-01-31 15:00:40 +01:00
Poesty Li
d83f787956
Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (1066 of 1066 strings)

Co-authored-by: Poesty Li <poesty7450@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/zh_Hans/
Translation: Fedilab/Strings
2023-01-31 02:36:27 +01:00
Eduardo
b958e24dc9
Translated using Weblate (Portuguese)
Currently translated at 90.0% (960 of 1066 strings)

Co-authored-by: Eduardo <edu200399lim@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/pt/
Translation: Fedilab/Strings
2023-01-31 02:36:27 +01:00
josé m
e61de6eb1d
Translated using Weblate (Galician)
Currently translated at 100.0% (1066 of 1066 strings)

Co-authored-by: josé m <correoxm@disroot.org>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/gl/
Translation: Fedilab/Strings
2023-01-31 02:36:27 +01:00
Lukáš Jelínek
b89212a4f6
Translated using Weblate (Czech)
Currently translated at 99.9% (1065 of 1066 strings)

Co-authored-by: Lukáš Jelínek <devel@aiken.cz>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/cs/
Translation: Fedilab/Strings
2023-01-31 02:36:26 +01:00
Thomas
c37d8ab34f Release 3.16.3 2023-01-30 18:18:23 +01:00
Thomas
ca5b37edfe Fix dynamic colors 2023-01-30 18:15:12 +01:00
Thomas
408e51c0a6 Peertube 2FA support 2023-01-30 17:02:24 +01:00
Thomas
9fd834eb44 Merge remote-tracking branch 'origin/develop' into develop 2023-01-29 17:56:48 +01:00
Thomas
58f3d01c87 Release 3.16.2 2023-01-29 17:56:40 +01:00
Thomas
de46b38991 Open profiles only trough avatars 2023-01-29 17:17:57 +01:00
Thomas
926caf2f3f Player layout only for peertube 2023-01-29 16:06:08 +01:00
Poesty Li
8bbe897483
Translated using Weblate (Chinese (Simplified))
Currently translated at 62.2% (664 of 1066 strings)

Co-authored-by: Poesty Li <poesty7450@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/zh_Hans/
Translation: Fedilab/Strings
2023-01-29 08:08:34 +01:00
Oğuz Ersen
59ba9134e7
Translated using Weblate (Turkish)
Currently translated at 100.0% (1066 of 1066 strings)

Co-authored-by: Oğuz Ersen <oguz@ersen.moe>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/tr/
Translation: Fedilab/Strings
2023-01-29 08:08:34 +01:00
claleb
83e68742be
Translated using Weblate (German)
Currently translated at 100.0% (1066 of 1066 strings)

Co-authored-by: claleb <weblate@claleb.de>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/de/
Translation: Fedilab/Strings
2023-01-29 08:08:34 +01:00
Thomas
9a38da9475 fix release note 2023-01-28 18:00:02 +01:00
Thomas
c8d0931d8e Release 3.16.1 2023-01-28 17:55:55 +01:00
Thomas
4d643ae28f Fix a crash for notification with Peertube 2023-01-28 17:46:46 +01:00
Thomas
33b8dd36e4 Add support to edit media 2023-01-28 16:47:56 +01:00
Thomas
25d3803e69 Fix peertube support 2023-01-28 16:26:55 +01:00
Thomas
7b08bf77bf Merge remote-tracking branch 'origin/develop' into develop 2023-01-28 15:49:37 +01:00
Thomas
3f1af73b0e avoid a crash with old peertube instances 2023-01-28 15:49:28 +01:00
Oğuz Ersen
a22dd088a5
Translated using Weblate (Turkish)
Currently translated at 100.0% (1064 of 1064 strings)

Co-authored-by: Oğuz Ersen <oguz@ersen.moe>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/tr/
Translation: Fedilab/Strings
2023-01-28 01:56:17 +01:00
josé m
e88c53d950
Translated using Weblate (Galician)
Currently translated at 100.0% (1064 of 1064 strings)

Co-authored-by: josé m <correoxm@disroot.org>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/gl/
Translation: Fedilab/Strings
2023-01-28 01:56:17 +01:00
claleb
a160d3e212
Translated using Weblate (German)
Currently translated at 100.0% (1064 of 1064 strings)

Co-authored-by: claleb <weblate@claleb.de>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/de/
Translation: Fedilab/Strings
2023-01-28 01:56:16 +01:00
Lukáš Jelínek
dacfba7043
Translated using Weblate (Czech)
Currently translated at 99.9% (1063 of 1064 strings)

Co-authored-by: Lukáš Jelínek <devel@aiken.cz>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/cs/
Translation: Fedilab/Strings
2023-01-28 01:56:16 +01:00
Thomas
9683fee1e6 Release 3.16.0 2023-01-27 18:35:27 +01:00
Thomas
575329586f Some fixes 2023-01-27 18:19:24 +01:00
Thomas
a866f5524a Og values when sharing 2023-01-27 16:42:12 +01:00
Thomas
654f7850de Fixes and improvements 2023-01-27 16:30:45 +01:00
Thomas
b52ab37aed Fix Text cleared when adding a media 2023-01-27 12:10:21 +01:00
Thomas
554335aa6e Release notes + fix a bad behavior when adding a Mastodon account from a Peertube one 2023-01-27 11:36:25 +01:00
Thomas
96abece6da Fix a crash 2023-01-27 11:02:18 +01:00
Thomas
8103bf4a16 Fix cross actions 2023-01-27 10:33:41 +01:00
Thomas
e4affdc9d7 Fix a crash 2023-01-26 18:16:26 +01:00
Thomas
90a6a6ceaa Merge remote-tracking branch 'origin/develop' into develop 2023-01-26 18:06:04 +01:00
Thomas
a97448438a playstore flavor 2023-01-26 18:05:41 +01:00
0xd9a
557e973fca update peertube register ui 2023-01-26 22:32:31 +05:30
Thomas
98385aa706 some improvements 2023-01-26 17:41:07 +01:00
Thomas
7119c12467 Remove context dependency 2023-01-26 16:47:28 +01:00
Thomas
3d1d9534be some code changes 2023-01-26 15:57:03 +01:00
Thomas
b94c08d029 some code changes 2023-01-26 15:39:23 +01:00
Thomas
42cf16b545 some code changes 2023-01-26 15:27:27 +01:00
Thomas
bebe315b08 Some layout changes 2023-01-26 15:19:44 +01:00
Thomas
6a359bbbf8 Redo upload 2023-01-26 12:38:21 +01:00
Thomas
42bdcaf6b6 Support proxy for Peertube 2023-01-26 12:08:31 +01:00
Eduardo
27641128c3
Translated using Weblate (Portuguese)
Currently translated at 90.2% (959 of 1063 strings)

Co-authored-by: Eduardo <edu200399lim@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/pt/
Translation: Fedilab/Strings
2023-01-26 11:54:29 +01:00
0xd9a
be0a85cb3a keep side buttons of compose page in position 2023-01-26 15:49:39 +05:30
0xd9a
f11c0e3d58 fix: empty space on top of compose 2023-01-26 15:49:01 +05:30
0xd9a
58e7346f71 update some peertube UI 2023-01-26 08:38:16 +05:30
Thomas
fc34de1da4 remove spinner lib 2023-01-25 18:02:35 +01:00
Thomas
bf8543bc77 Some cleaning 2023-01-25 17:23:49 +01:00
Thomas
8b26deb064 More update on view 2023-01-25 15:59:17 +01:00
Thomas
6ef2683e5e redo notification 2023-01-25 15:37:51 +01:00
Thomas
e0b12ab0e2 Change alert dialogs 2023-01-25 15:14:42 +01:00
Thomas
5ce553f8cf Some fixes 2023-01-25 12:18:02 +01:00
Thomas
44ff9225bc Merge remote-tracking branch 'origin/peertube_integration' into peertube_integration 2023-01-25 11:55:41 +01:00
Thomas
206d5c7e74 Drawer menu 2023-01-25 11:55:30 +01:00
0xd9a
50698f7325 update peertube drawer 2023-01-25 13:31:56 +05:30
Thomas
6e8381396f Fix a bug 2023-01-24 18:23:59 +01:00
Thomas
e87a347354 working 2023-01-24 18:08:58 +01:00
Thomas
7b071eb9eb Clean / update license 2023-01-24 15:09:21 +01:00
Thomas
77fbebf4a7 improvements 2023-01-24 15:03:02 +01:00
Thomas
c09a7a3c2b improvements 2023-01-23 18:06:25 +01:00
Thomas
1b429a31a2 Fix authentication 2023-01-23 17:05:54 +01:00
Thomas
82a5bfebb4 Some changes 2023-01-23 15:01:44 +01:00
Thomas
4c5232039a fusion 2023-01-23 12:12:22 +01:00
Thomas
74afdc0a7a Some changes 2023-01-23 09:31:32 +01:00
gnu-ewm
1de94be096
Translated using Weblate (Polish)
Currently translated at 86.2% (917 of 1063 strings)

Co-authored-by: gnu-ewm <gnu.ewm@protonmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/pl/
Translation: Fedilab/Strings
2023-01-23 09:22:14 +01:00
gnu-ewm
5c6745dacc
Translated using Weblate (Polish)
Currently translated at 3.7% (3 of 80 strings)

Co-authored-by: gnu-ewm <gnu.ewm@protonmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/description/pl/
Translation: Fedilab/description
2023-01-22 21:21:37 +01:00
Thomas
66161f4eb3 Some changes 2023-01-22 18:51:52 +01:00
Thomas
d77dd2c349 Split files 2023-01-22 16:48:14 +01:00
Thomas
e9571221be Changes 2023-01-22 15:22:59 +01:00
Oğuz Ersen
a33ddf3e51
Translated using Weblate (Turkish)
Currently translated at 100.0% (1063 of 1063 strings)

Co-authored-by: Oğuz Ersen <oguz@ersen.moe>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/tr/
Translation: Fedilab/Strings
2023-01-20 15:27:06 +01:00
Ajeje Brazorf
84073c8df4
Translated using Weblate (Sardinian)
Currently translated at 99.2% (1055 of 1063 strings)

Co-authored-by: Ajeje Brazorf <lmelonimamo@yahoo.it>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/sc/
Translation: Fedilab/Strings
2023-01-20 15:27:06 +01:00
Eduardo
1e9b701f58
Translated using Weblate (Portuguese)
Currently translated at 90.1% (958 of 1063 strings)

Co-authored-by: Eduardo <edu200399lim@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/pt/
Translation: Fedilab/Strings
2023-01-20 15:27:05 +01:00
Oliebol
b48d7adaed
Translated using Weblate (Dutch)
Currently translated at 99.9% (1062 of 1063 strings)

Co-authored-by: Oliebol <schrijfmedan@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/nl/
Translation: Fedilab/Strings
2023-01-20 15:27:05 +01:00
josé m
b6a4fb03fc
Translated using Weblate (Galician)
Currently translated at 100.0% (1063 of 1063 strings)

Co-authored-by: josé m <correoxm@disroot.org>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/gl/
Translation: Fedilab/Strings
2023-01-20 15:27:05 +01:00
ButterflyOfFire
05dfc73d94
Translated using Weblate (French)
Currently translated at 97.8% (1040 of 1063 strings)

Co-authored-by: ButterflyOfFire <ButterflyOfFire@protonmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/fr/
Translation: Fedilab/Strings
2023-01-20 15:27:04 +01:00
claleb
850805b562
Translated using Weblate (German)
Currently translated at 100.0% (1063 of 1063 strings)

Co-authored-by: claleb <weblate@claleb.de>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/de/
Translation: Fedilab/Strings
2023-01-20 15:27:04 +01:00
Lukáš Jelínek
bf5e092f7d
Translated using Weblate (Czech)
Currently translated at 99.8% (1061 of 1063 strings)

Co-authored-by: Lukáš Jelínek <devel@aiken.cz>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/cs/
Translation: Fedilab/Strings
2023-01-20 15:27:03 +01:00
Thomas
f2799e939c Release 3.15.2 2023-01-19 17:40:18 +01:00
Thomas
ff6244883a Fix a crash 2023-01-19 17:05:28 +01:00
Thomas
13fa111394 Merge remote-tracking branch 'origin/develop' into develop 2023-01-19 16:55:10 +01:00
Thomas
f8e22c4a9d Fix a crash 2023-01-19 16:54:39 +01:00
Thomas
fdec78fd61 Fix #758 - Display instance of accounts in dialog when sharing 2023-01-19 16:54:23 +01:00
Oğuz Ersen
2e262e3156
Translated using Weblate (Turkish)
Currently translated at 100.0% (1060 of 1060 strings)

Co-authored-by: Oğuz Ersen <oguz@ersen.moe>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/tr/
Translation: Fedilab/Strings
2023-01-18 20:51:48 +01:00
josé m
3d11a064bd
Translated using Weblate (Galician)
Currently translated at 100.0% (1060 of 1060 strings)

Co-authored-by: josé m <correoxm@disroot.org>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/gl/
Translation: Fedilab/Strings
2023-01-18 20:51:48 +01:00
ButterflyOfFire
688ff330db
Translated using Weblate (French)
Currently translated at 97.4% (1033 of 1060 strings)

Co-authored-by: ButterflyOfFire <ButterflyOfFire@protonmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/fr/
Translation: Fedilab/Strings
2023-01-18 20:51:48 +01:00
Ettore Atalan
4ded712634
Translated using Weblate (German)
Currently translated at 100.0% (1060 of 1060 strings)

Co-authored-by: Ettore Atalan <atalanttore@googlemail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/de/
Translation: Fedilab/Strings
2023-01-18 20:51:48 +01:00
Lukáš Jelínek
a7a8cb6c90
Translated using Weblate (Czech)
Currently translated at 99.8% (1058 of 1060 strings)

Co-authored-by: Lukáš Jelínek <devel@aiken.cz>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/cs/
Translation: Fedilab/Strings
2023-01-18 20:51:48 +01:00
Thomas
c2d270696d Release 3.15.1 2023-01-18 17:27:14 +01:00
Thomas
47c66185a3 Release 3.15.1 2023-01-18 17:19:18 +01:00
Thomas
b83ffa4dcf Fix theme 2023-01-18 17:17:31 +01:00
Thomas
c994ccca0c Merge branch 'develop' of https://codeberg.org/tom79/Fedilab into develop 2023-01-18 15:42:38 +01:00
Thomas
d3bb4a285b Improve notifications 2023-01-18 15:42:20 +01:00
0xd9a
5b4f7d70b6 Update some dialogs 2023-01-18 20:07:03 +05:30
Oliebol
e50c0fa9fe
Translated using Weblate (Dutch)
Currently translated at 99.6% (1051 of 1055 strings)

Co-authored-by: Oliebol <schrijfmedan@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/nl/
Translation: Fedilab/Strings
2023-01-17 18:32:07 +01:00
Oğuz Ersen
0da6ff2c0c
Translated using Weblate (Turkish)
Currently translated at 100.0% (1053 of 1053 strings)

Co-authored-by: Oğuz Ersen <oguz@ersen.moe>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/tr/
Translation: Fedilab/Strings
2023-01-17 18:32:07 +01:00
Ajeje Brazorf
f72e9cb26f
Translated using Weblate (Sardinian)
Currently translated at 99.2% (1049 of 1057 strings)

Translated using Weblate (Sardinian)

Currently translated at 99.2% (1047 of 1055 strings)

Translated using Weblate (Sardinian)

Currently translated at 99.2% (1045 of 1053 strings)

Co-authored-by: Ajeje Brazorf <lmelonimamo@yahoo.it>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/sc/
Translation: Fedilab/Strings
2023-01-17 18:32:07 +01:00
josé m
60bafb2d32
Translated using Weblate (Galician)
Currently translated at 100.0% (1059 of 1059 strings)

Translated using Weblate (Galician)

Currently translated at 100.0% (1053 of 1053 strings)

Co-authored-by: josé m <correoxm@disroot.org>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/gl/
Translation: Fedilab/Strings
2023-01-17 18:32:07 +01:00
claleb
d103844496
Translated using Weblate (German)
Currently translated at 99.9% (1058 of 1059 strings)

Translated using Weblate (German)

Currently translated at 100.0% (1053 of 1053 strings)

Co-authored-by: claleb <weblate@claleb.de>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/de/
Translation: Fedilab/Strings
2023-01-17 18:32:07 +01:00
Lukáš Jelínek
e134d23594
Translated using Weblate (Czech)
Currently translated at 99.8% (1051 of 1053 strings)

Co-authored-by: Lukáš Jelínek <devel@aiken.cz>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/cs/
Translation: Fedilab/Strings
2023-01-17 18:32:07 +01:00
Thomas
7bc983d345 Release 3.15.0 2023-01-17 18:31:50 +01:00
Thomas
255eeac0a5 - Add several targeted languages for translator 2023-01-17 18:08:10 +01:00
Thomas
3b30708954 Some fixes 2023-01-17 16:41:39 +01:00
Thomas
e0e2f7789b Some fixes 2023-01-17 14:36:51 +01:00
Thomas
ef5a211f57 Merge branch 'develop' of https://codeberg.org/tom79/Fedilab into develop 2023-01-17 14:19:54 +01:00
Thomas
939023b71e Some fixes 2023-01-17 14:19:46 +01:00
0xd9a
89c30f16f0 Update poll compose dialog 2023-01-17 16:59:41 +05:30
Thomas
f8641a953a Release 3.14.6 2023-01-16 18:13:09 +01:00
Thomas
a1f97d7cfa Hide single media with preview is now a setting (default: disabled) 2023-01-16 16:49:48 +01:00
Thomas
21abe0e297 Merge remote-tracking branch 'origin/develop' into develop 2023-01-16 16:34:17 +01:00
Thomas
d5c51e6dca Add support for maths. 2023-01-16 16:33:20 +01:00
Thomas
25ad71080e improvements 2023-01-16 14:35:14 +01:00
Thomas
4584630883 do maths 2023-01-16 13:56:25 +01:00
Eduardo Lima
5733eed8bc
Translated using Weblate (Portuguese)
Currently translated at 90.3% (946 of 1047 strings)

Co-authored-by: Eduardo Lima <edu200399lim@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/pt/
Translation: Fedilab/Strings
2023-01-16 07:51:19 +01:00
josé m
e0deba141a
Translated using Weblate (Galician)
Currently translated at 100.0% (1047 of 1047 strings)

Co-authored-by: josé m <correoxm@disroot.org>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/gl/
Translation: Fedilab/Strings
2023-01-16 07:51:19 +01:00
nichu42
1a66bd5d89
Translated using Weblate (German)
Currently translated at 100.0% (1047 of 1047 strings)

Co-authored-by: nichu42 <nroediger@nic-site.de>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/de/
Translation: Fedilab/Strings
2023-01-16 07:51:18 +01:00
Thomas
72a79766b8 Release 3.14.5 2023-01-15 18:47:22 +01:00
Thomas
7751646d50 Fix copy/paste 2023-01-15 18:38:26 +01:00
Thomas
97e15dd37f Fixes and improvements 2023-01-15 16:44:00 +01:00
Thomas
ce9d4c2cd3 Fixes and improvements 2023-01-15 16:04:51 +01:00
Thomas
3ab25e3333 Fix some crashes 2023-01-15 14:48:23 +01:00
Thomas
80f6fb2382 Fix a crash 2023-01-15 12:30:33 +01:00
Thomas
1768a85cbd clean fix 2023-01-15 12:06:38 +01:00
Thomas
eaecabe7b7 Release 3.14.4 2023-01-14 18:10:42 +01:00
Thomas
ae7394888d Improve media activity 2023-01-14 17:48:05 +01:00
Thomas
27423a6ab5 Improve behavior 2023-01-14 16:21:16 +01:00
Thomas
a5d1e8efe0 Fix icon bug colors 2023-01-14 11:15:49 +01:00
Thomas
3a8f037be5 Fix icon bug colors 2023-01-14 11:15:05 +01:00
Thomas
f5b0eacfab Merge remote-tracking branch 'origin/develop' into develop 2023-01-14 10:46:31 +01:00
Thomas
0740f6399b Fix issue #751 - Boost color not applied 2023-01-14 10:46:03 +01:00
Thomas
8dfd55fe10 Fix cross account actions 2023-01-14 10:44:09 +01:00
Oliebol
3046a8a391
Translated using Weblate (Dutch)
Currently translated at 100.0% (1047 of 1047 strings)

Co-authored-by: Oliebol <schrijfmedan@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/nl/
Translation: Fedilab/Strings
2023-01-14 08:54:18 +01:00
Hosted Weblate
ef8bf95c28
Update translation files
Updated by "Remove blank strings" hook in Weblate.

Update translation files

Updated by "Remove blank strings" hook in Weblate.

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/
Translation: Fedilab/Strings
2023-01-14 08:54:18 +01:00
Oğuz Ersen
7e1a8c1af3
Translated using Weblate (Turkish)
Currently translated at 100.0% (1047 of 1047 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (1042 of 1042 strings)

Co-authored-by: Oğuz Ersen <oguz@ersen.moe>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/tr/
Translation: Fedilab/Strings
2023-01-14 08:54:18 +01:00
Eduardo Lima
7c9b67584d
Translated using Weblate (Gaelic)
Currently translated at 32.8% (342 of 1042 strings)

Translated using Weblate (Portuguese)

Currently translated at 75.9% (791 of 1042 strings)

Co-authored-by: Eduardo Lima <edu200399lim@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/gd/
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/pt/
Translation: Fedilab/Strings
2023-01-14 08:54:17 +01:00
joenepraat
fda91e7d3d
Translated using Weblate (Dutch)
Currently translated at 100.0% (1042 of 1042 strings)

Co-authored-by: joenepraat <joenepraat@posteo.org>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/nl/
Translation: Fedilab/Strings
2023-01-14 08:54:17 +01:00
josé m
dcbaa32aa2
Translated using Weblate (Galician)
Currently translated at 100.0% (1042 of 1042 strings)

Co-authored-by: josé m <correoxm@disroot.org>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/gl/
Translation: Fedilab/Strings
2023-01-14 08:54:16 +01:00
claleb
71c7d0a1fa
Translated using Weblate (German)
Currently translated at 100.0% (1047 of 1047 strings)

Translated using Weblate (German)

Currently translated at 100.0% (1042 of 1042 strings)

Co-authored-by: claleb <weblate@claleb.de>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/de/
Translation: Fedilab/Strings
2023-01-14 08:54:16 +01:00
Lukáš Jelínek
b1d9d3016f
Translated using Weblate (Czech)
Currently translated at 99.8% (1045 of 1047 strings)

Translated using Weblate (Czech)

Currently translated at 99.8% (1040 of 1042 strings)

Co-authored-by: Lukáš Jelínek <devel@aiken.cz>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/cs/
Translation: Fedilab/Strings
2023-01-14 08:54:16 +01:00
Thomas
447ad45fc6 Release 3.14.3 2023-01-13 18:07:35 +01:00
Thomas
d3f721f7cc Fix follow button colors 2023-01-13 17:26:25 +01:00
Thomas
107ac13e15 Fix issue #745 - Update library for bottom buttons. 2023-01-13 16:19:50 +01:00
Thomas
d70c285bea Merge branch 'develop' 2023-01-12 17:54:31 +01:00
Thomas
bb2b66ce6a Release 3.14.2 2023-01-12 17:52:34 +01:00
Thomas
849967fe97 Merge branch 'develop' of https://codeberg.org/tom79/Fedilab into develop 2023-01-12 17:47:49 +01:00
Thomas
bb68512502 Fix media cannot be downloaded 2023-01-12 17:11:27 +01:00
Thomas
ae31abaa3b Bot and reply icon indicators more visible 2023-01-12 15:58:59 +01:00
Lukáš Jelínek
80927bfa9c
Translated using Weblate (Czech)
Currently translated at 99.7% (1037 of 1040 strings)

Co-authored-by: Lukáš Jelínek <devel@aiken.cz>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/cs/
Translation: Fedilab/Strings
2023-01-12 14:57:31 +01:00
Thomas
1ef01af199 Some cleaning 2023-01-12 14:55:53 +01:00
Thomas
d5a5f08727 Instance directory 2023-01-12 14:35:44 +01:00
Thomas
e03c02cc35 Merge remote-tracking branch 'origin/develop' into develop 2023-01-12 11:18:37 +01:00
Thomas
c37f4bb643 Familiar followers on profiles 2023-01-12 11:13:52 +01:00
Thomas
098e2d87ec Some fixes 2023-01-12 09:54:43 +01:00
ssantos
fd6d487060
Translated using Weblate (Portuguese)
Currently translated at 73.6% (765 of 1039 strings)

Co-authored-by: ssantos <ssantos@web.de>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/pt/
Translation: Fedilab/Strings
2023-01-12 03:50:57 +01:00
Thomas
af10d647b9 Merge branch 'develop' 2023-01-11 18:30:08 +01:00
Thomas
31025af9ab Release 3.14.1 2023-01-11 18:23:33 +01:00
Thomas
8162e3f171 Some fixes 2023-01-11 18:22:29 +01:00
Thomas
316e750004 Apply suggestion is search tab activity 2023-01-11 18:07:54 +01:00
Thomas
edc4f1357e NSFW for Pixelfed view 2023-01-11 18:04:37 +01:00
Thomas
ac503cdf0c Automatically switch to account tab if no results for tags 2023-01-11 17:51:21 +01:00
Thomas
bc8dfec325 Tag cannot be pinned 2023-01-11 17:35:46 +01:00
Thomas
39f248e0a6 Fix videos cannot be downloaded 2023-01-11 17:07:47 +01:00
Thomas
f966874550 Some fixes 2023-01-11 16:51:42 +01:00
Thomas
7704e15e31 Preload media 2023-01-11 16:26:31 +01:00
Thomas
b0e01e5bf1 Search bar: Display suggestion when starting by @ or # 2023-01-10 16:51:02 +01:00
Thomas
4ec8496124 Merge remote-tracking branch 'origin/develop' into develop 2023-01-10 12:12:57 +01:00
Thomas
b54c36627c Search bar: Display suggestion when starting by @ or # 2023-01-10 12:12:43 +01:00
Ajeje Brazorf
884e8d35de
Translated using Weblate (Sardinian)
Currently translated at 98.8% (1027 of 1039 strings)

Co-authored-by: Ajeje Brazorf <lmelonimamo@yahoo.it>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/sc/
Translation: Fedilab/Strings
2023-01-10 08:48:33 +01:00
Mikhail Kobuk
8154c3425e
Translated using Weblate (Russian)
Currently translated at 74.8% (778 of 1039 strings)

Co-authored-by: Mikhail Kobuk <arktixord@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/ru/
Translation: Fedilab/Strings
2023-01-10 08:48:33 +01:00
Oliebol
7e76d68466
Translated using Weblate (Dutch)
Currently translated at 100.0% (1039 of 1039 strings)

Co-authored-by: Oliebol <schrijfmedan@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/nl/
Translation: Fedilab/Strings
2023-01-10 08:48:33 +01:00
josé m
a01ffab237
Translated using Weblate (Galician)
Currently translated at 100.0% (1039 of 1039 strings)

Co-authored-by: josé m <correoxm@disroot.org>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/gl/
Translation: Fedilab/Strings
2023-01-10 08:48:32 +01:00
Thomas
d97a78362c Fix crashes 2023-01-09 17:37:56 +01:00
Thomas
4ccd299fda Merge remote-tracking branch 'origin/develop' into develop 2023-01-09 17:37:47 +01:00
Thomas
1fe0701342 Fix issue #737 and #738 - Jumps with timeline 2023-01-09 17:37:35 +01:00
Oliebol
902d5176ff
Translated using Weblate (Dutch)
Currently translated at 4.2% (3 of 71 strings)

Co-authored-by: Oliebol <schrijfmedan@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/description/nl/
Translation: Fedilab/description
2023-01-09 11:49:18 +01:00
Oğuz Ersen
0a2effe591
Translated using Weblate (Turkish)
Currently translated at 100.0% (1039 of 1039 strings)

Co-authored-by: Oğuz Ersen <oguz@ersen.moe>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/tr/
Translation: Fedilab/Strings
2023-01-08 15:49:24 +01:00
claleb
e6139c129f
Translated using Weblate (German)
Currently translated at 100.0% (1039 of 1039 strings)

Co-authored-by: claleb <weblate@claleb.de>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/de/
Translation: Fedilab/Strings
2023-01-08 15:49:23 +01:00
Lukáš Jelínek
fafb7dc301
Translated using Weblate (Czech)
Currently translated at 99.5% (1034 of 1039 strings)

Co-authored-by: Lukáš Jelínek <devel@aiken.cz>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/cs/
Translation: Fedilab/Strings
2023-01-08 15:49:23 +01:00
Thomas
8c98b9e46d Merge branch 'develop' 2023-01-07 17:48:43 +01:00
Oğuz Ersen
1a8108d5e0
Translated using Weblate (Turkish)
Currently translated at 100.0% (1036 of 1036 strings)

Co-authored-by: Oğuz Ersen <oguz@ersen.moe>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/tr/
Translation: Fedilab/Strings
2023-01-07 17:48:19 +01:00
josé m
466da9a0b3
Translated using Weblate (Galician)
Currently translated at 100.0% (1036 of 1036 strings)

Co-authored-by: josé m <correoxm@disroot.org>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/gl/
Translation: Fedilab/Strings
2023-01-07 17:48:19 +01:00
Jean-Luc Tibaux
da122028c9
Translated using Weblate (French)
Currently translated at 98.8% (1024 of 1036 strings)

Co-authored-by: Jean-Luc Tibaux <eugentoptic@outlook.de>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/fr/
Translation: Fedilab/Strings
2023-01-07 17:48:19 +01:00
claleb
000552c090
Translated using Weblate (German)
Currently translated at 100.0% (1036 of 1036 strings)

Co-authored-by: claleb <weblate@claleb.de>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/de/
Translation: Fedilab/Strings
2023-01-07 17:48:19 +01:00
Thomas
47e43193b8 Release 3.14.0 2023-01-07 17:47:57 +01:00
Thomas
4057a04302 Merge remote-tracking branch 'origin/develop' into develop 2023-01-07 17:04:24 +01:00
Thomas
6df271c319 Fix urls 2023-01-07 17:03:50 +01:00
Thomas
32f9264558 Fix a crash for long messages 2023-01-07 15:36:57 +01:00
Thomas
3ea4559f69 Option to align left message bottom buttons 2023-01-07 15:27:33 +01:00
Jean-Luc Tibaux
cd4df2d29a
Translated using Weblate (German)
Currently translated at 38.5% (27 of 70 strings)

Co-authored-by: Jean-Luc Tibaux <eugentoptic@outlook.de>
Translate-URL: https://hosted.weblate.org/projects/fedilab/description/de/
Translation: Fedilab/description
2023-01-07 08:51:16 +01:00
Thomas
e23d2caf00 Release 3.13.7 2023-01-06 18:37:26 +01:00
Thomas
566b50d394 Merge remote-tracking branch 'origin/develop' into develop 2023-01-06 18:22:08 +01:00
Thomas
d77f1fc9f6 Pixelfed view 2023-01-06 18:21:41 +01:00
Thomas
c052e376e2 Fix reactions displayed in all notif tabs 2023-01-06 16:27:26 +01:00
Thomas
7e1d9b8910 Fix issue #730 2023-01-06 15:57:05 +01:00
Thomas
17d6152af3 Some fixes 2023-01-06 09:52:46 +01:00
Ajeje Brazorf
caa14ecaa9
Translated using Weblate (Sardinian)
Currently translated at 98.5% (1019 of 1034 strings)

Co-authored-by: Ajeje Brazorf <lmelonimamo@yahoo.it>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/sc/
Translation: Fedilab/Strings
2023-01-06 06:51:02 +01:00
josé m
56b82461b5
Translated using Weblate (Galician)
Currently translated at 100.0% (1034 of 1034 strings)

Co-authored-by: josé m <correoxm@disroot.org>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/gl/
Translation: Fedilab/Strings
2023-01-06 06:51:02 +01:00
Thomas
1e807725e1 release notes 2023-01-05 19:01:27 +01:00
Thomas
8fe26abec7 release notes 2023-01-05 18:53:49 +01:00
Thomas
9e701f82ba release notes 2023-01-05 15:15:39 +01:00
Cilian
97e3d7ed19
Translated using Weblate (Russian)
Currently translated at 72.8% (753 of 1034 strings)

Co-authored-by: Cilian <arusivv@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/ru/
Translation: Fedilab/Strings
2023-01-04 22:51:06 +01:00
Виталий
1c650b56f1
Translated using Weblate (Russian)
Currently translated at 72.8% (753 of 1034 strings)

Co-authored-by: Виталий <remove4kebab@protonmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/ru/
Translation: Fedilab/Strings
2023-01-04 22:51:05 +01:00
claleb
31bdafe5fd
Translated using Weblate (German)
Currently translated at 100.0% (1034 of 1034 strings)

Co-authored-by: claleb <weblate@claleb.de>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/de/
Translation: Fedilab/Strings
2023-01-04 22:51:05 +01:00
Lukáš Jelínek
ca85e500a5
Translated using Weblate (Czech)
Currently translated at 99.6% (1030 of 1034 strings)

Co-authored-by: Lukáš Jelínek <devel@aiken.cz>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/cs/
Translation: Fedilab/Strings
2023-01-04 22:51:05 +01:00
Thomas
229262d478 clean 2023-01-04 14:34:22 +01:00
Thomas
12611a116b Merge remote-tracking branch 'origin/develop' into develop 2023-01-04 11:54:04 +01:00
Thomas
9d6a2057ab Add tags in any when pinned / Fix quotes with tags breaking lines 2023-01-04 11:53:51 +01:00
Thomas
785257cc6c Fix potential crashes 2023-01-04 11:12:17 +01:00
Cilian
4e9af2abb2
Translated using Weblate (Russian)
Currently translated at 66.0% (683 of 1034 strings)

Co-authored-by: Cilian <arusivv@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/ru/
Translation: Fedilab/Strings
2023-01-03 20:46:20 +01:00
Виталий
b45a007797
Translated using Weblate (Russian)
Currently translated at 66.0% (683 of 1034 strings)

Co-authored-by: Виталий <remove4kebab@protonmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/ru/
Translation: Fedilab/Strings
2023-01-03 20:46:20 +01:00
Виталий
e726b6a540
Translated using Weblate (Russian)
Currently translated at 65.9% (682 of 1034 strings)

Co-authored-by: Виталий <remove4kebab@protonmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/ru/
Translation: Fedilab/Strings
2023-01-03 20:45:55 +01:00
Cilian
f6eedc3eb5
Translated using Weblate (Russian)
Currently translated at 65.9% (682 of 1034 strings)

Co-authored-by: Cilian <arusivv@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/ru/
Translation: Fedilab/Strings
2023-01-03 20:45:55 +01:00
Cilian
db8d57b18d
Translated using Weblate (Russian)
Currently translated at 65.7% (680 of 1034 strings)

Co-authored-by: Cilian <arusivv@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/ru/
Translation: Fedilab/Strings
2023-01-03 20:45:25 +01:00
Виталий
f3391b1ac6
Translated using Weblate (Russian)
Currently translated at 65.7% (680 of 1034 strings)

Co-authored-by: Виталий <remove4kebab@protonmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/ru/
Translation: Fedilab/Strings
2023-01-03 20:45:25 +01:00
Cilian
d6a5d2a1c7
Translated using Weblate (Russian)
Currently translated at 65.6% (679 of 1034 strings)

Co-authored-by: Cilian <arusivv@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/ru/
Translation: Fedilab/Strings
2023-01-03 20:45:04 +01:00
Виталий
f7205fbbbb
Translated using Weblate (Russian)
Currently translated at 65.6% (679 of 1034 strings)

Co-authored-by: Виталий <remove4kebab@protonmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/ru/
Translation: Fedilab/Strings
2023-01-03 20:45:03 +01:00
Виталий
090fbce96a
Translated using Weblate (Russian)
Currently translated at 65.4% (677 of 1034 strings)

Co-authored-by: Виталий <remove4kebab@protonmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/ru/
Translation: Fedilab/Strings
2023-01-03 20:44:30 +01:00
Cilian
10ce384282
Translated using Weblate (Russian)
Currently translated at 65.4% (677 of 1034 strings)

Co-authored-by: Cilian <arusivv@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/ru/
Translation: Fedilab/Strings
2023-01-03 20:44:30 +01:00
Cilian
9dbf8ccdc0
Translated using Weblate (Russian)
Currently translated at 65.3% (676 of 1034 strings)

Co-authored-by: Cilian <arusivv@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/ru/
Translation: Fedilab/Strings
2023-01-03 20:43:46 +01:00
Виталий
ba4456f305
Translated using Weblate (Russian)
Currently translated at 65.3% (676 of 1034 strings)

Co-authored-by: Виталий <remove4kebab@protonmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/ru/
Translation: Fedilab/Strings
2023-01-03 20:43:45 +01:00
Thomas
299df2cd59 3.13.6 2023-01-03 18:12:15 +01:00
Thomas
a78a3580b3 Merge remote-tracking branch 'origin/develop' into develop 2023-01-03 17:30:04 +01:00
Thomas
886f066071 Fix issue #607 - Issue with fetchmore 2023-01-03 17:29:53 +01:00
Oğuz Ersen
f140b2883a
Translated using Weblate (Turkish)
Currently translated at 100.0% (1034 of 1034 strings)

Co-authored-by: Oğuz Ersen <oguz@ersen.moe>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/tr/
Translation: Fedilab/Strings
2023-01-03 14:51:02 +01:00
RintanBroadleaf
5480ff91d2
Translated using Weblate (Japanese)
Currently translated at 100.0% (1034 of 1034 strings)

Co-authored-by: RintanBroadleaf <rintanbroadleaf@outlook.jp>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/ja/
Translation: Fedilab/Strings
2023-01-03 14:51:02 +01:00
claleb
3210530975
Translated using Weblate (German)
Currently translated at 100.0% (1034 of 1034 strings)

Co-authored-by: claleb <weblate@claleb.de>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/de/
Translation: Fedilab/Strings
2023-01-03 14:51:02 +01:00
Lukáš Jelínek
807530f8d6
Translated using Weblate (Czech)
Currently translated at 99.2% (1026 of 1034 strings)

Co-authored-by: Lukáš Jelínek <devel@aiken.cz>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/cs/
Translation: Fedilab/Strings
2023-01-03 14:51:01 +01:00
Thomas
a81f27faff Fix expand media with fit preview images when sensitive 2023-01-03 14:22:28 +01:00
Thomas
2ebbc27c0a Fix some issues 2023-01-03 14:10:42 +01:00
Thomas
1edd554fde Fix issue #712 - Empty notifications 2023-01-03 13:42:46 +01:00
Thomas
580024e1b9 Fix issue #710 - Fav/Boost markers with shared message from conversations are not applied in timeline 2023-01-03 11:31:12 +01:00
Thomas
d9f477a57f Fix issue #715 - Custom emoji not displayed in notifications 2023-01-03 10:59:25 +01:00
Thomas
aea0f8ca83 Merge remote-tracking branch 'origin/develop' into develop 2023-01-03 09:07:49 +01:00
Thomas
df5354ec1e Fix issue #719 - Wrong instance emojis with cross compose 2023-01-03 09:07:41 +01:00
claleb
66acb1b8d8
Translated using Weblate (German)
Currently translated at 100.0% (1030 of 1030 strings)

Co-authored-by: claleb <weblate@claleb.de>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/de/
Translation: Fedilab/Strings
2023-01-02 17:28:19 +01:00
Thomas
537bb3f622 Release 3.13.5 2023-01-02 17:28:09 +01:00
Thomas
da2ce8bce4 Merge remote-tracking branch 'origin/develop' into develop 2023-01-02 16:21:05 +01:00
Thomas
508f3c6253 comment #702 - Add post local only (Glitch) 2023-01-02 16:20:58 +01:00
Thomas
64e50bd8b5 Fix crashes 2023-01-02 15:06:31 +01:00
Oğuz Ersen
ccdbc2f45a
Translated using Weblate (Turkish)
Currently translated at 100.0% (1030 of 1030 strings)

Co-authored-by: Oğuz Ersen <oguz@ersen.moe>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/tr/
Translation: Fedilab/Strings
2023-01-02 07:51:07 +01:00
Lukáš Jelínek
6a98b4db48
Translated using Weblate (Czech)
Currently translated at 99.2% (1022 of 1030 strings)

Co-authored-by: Lukáš Jelínek <devel@aiken.cz>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/cs/
Translation: Fedilab/Strings
2023-01-02 07:51:07 +01:00
Oğuz Ersen
0ec4e0c51b
Translated using Weblate (Turkish)
Currently translated at 100.0% (1027 of 1027 strings)

Co-authored-by: Oğuz Ersen <oguz@ersen.moe>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/tr/
Translation: Fedilab/Strings
2023-01-01 18:23:09 +01:00
claleb
0d1107ec15
Translated using Weblate (German)
Currently translated at 100.0% (1027 of 1027 strings)

Co-authored-by: claleb <weblate@claleb.de>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/de/
Translation: Fedilab/Strings
2023-01-01 18:23:09 +01:00
Thomas
cbc5eddc9c Release 3.13.4 2023-01-01 18:23:00 +01:00
Thomas
61798a7ce1 Release 3.13.4 2023-01-01 18:20:18 +01:00
Thomas
7303e7faa8 Spoiler text when editing 2023-01-01 16:46:59 +01:00
Thomas
95f4059509 Merge remote-tracking branch 'origin/develop' into develop 2023-01-01 11:55:41 +01:00
Thomas
c590ab48f5 Some cleaning 2023-01-01 11:55:32 +01:00
Thomas
8fb7e4dc71 comment issue #702 - Add Bubble timeline 2023-01-01 11:54:43 +01:00
claleb
a6f25737f7
Translated using Weblate (German)
Currently translated at 24.2% (16 of 66 strings)

Co-authored-by: claleb <weblate@claleb.de>
Translate-URL: https://hosted.weblate.org/projects/fedilab/description/de/
Translation: Fedilab/description
2023-01-01 05:48:40 +01:00
Oğuz Ersen
f4748cef95
Translated using Weblate (Turkish)
Currently translated at 100.0% (1021 of 1021 strings)

Co-authored-by: Oğuz Ersen <oguz@ersen.moe>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/tr/
Translation: Fedilab/Strings
2023-01-01 04:50:23 +01:00
Oliebol
e450589f76
Translated using Weblate (Dutch)
Currently translated at 99.8% (1019 of 1021 strings)

Co-authored-by: Oliebol <schrijfmedan@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/nl/
Translation: Fedilab/Strings
2023-01-01 04:50:23 +01:00
claleb
ec9e0dbbda
Translated using Weblate (German)
Currently translated at 100.0% (1021 of 1021 strings)

Co-authored-by: claleb <weblate@claleb.de>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/de/
Translation: Fedilab/Strings
2023-01-01 04:50:23 +01:00
Lukáš Jelínek
56e372a2f6
Translated using Weblate (Czech)
Currently translated at 99.6% (1017 of 1021 strings)

Co-authored-by: Lukáš Jelínek <devel@aiken.cz>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/cs/
Translation: Fedilab/Strings
2023-01-01 04:50:23 +01:00
Thomas
f976b6dd72 Add Bubble timeline support 2022-12-31 18:12:31 +01:00
claleb
f81ba544a3
Translated using Weblate (German)
Currently translated at 21.2% (14 of 66 strings)

Co-authored-by: claleb <weblate@claleb.de>
Translate-URL: https://hosted.weblate.org/projects/fedilab/description/de/
Translation: Fedilab/description
2022-12-31 16:50:31 +01:00
Thomas
818f32ffb5 some fixes 2022-12-31 15:10:11 +01:00
Thomas
112701dd86 Improve interactions 2022-12-31 14:59:23 +01:00
Thomas
c3006080ba Merge branch 'develop' 2022-12-30 18:12:09 +01:00
Thomas
12fae6059a update release notes 2022-12-30 18:11:48 +01:00
Thomas
90b9291dab Release 3.13.3 2022-12-30 18:06:28 +01:00
Thomas
b1c22713b1 Merge remote-tracking branch 'origin/develop' into develop 2022-12-30 18:00:15 +01:00
Thomas
a859f3cbef comment #702 - Fix custom emoji reactions 2022-12-30 17:59:33 +01:00
Thomas
31929850c3 comment #702 - Allow to disable some icons 2022-12-30 17:53:26 +01:00
Thomas
e5c4efb4e9 comment #702 - Allow to format the text when composing 2022-12-30 17:40:03 +01:00
0xd9a
7b84049014 Update link actions dialog 2022-12-30 20:41:52 +05:30
Thomas
2164c2fe91 comment #702 - Separated settings 2022-12-30 16:01:51 +01:00
Eduardo Lima
4d5be03db6
Translated using Weblate (Portuguese)
Currently translated at 12.3% (8 of 65 strings)

Co-authored-by: Eduardo Lima <edu200399lim@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/description/pt/
Translation: Fedilab/description
2022-12-30 15:50:20 +01:00
Eduardo Lima
1defbdad83
Translated using Weblate (Portuguese)
Currently translated at 75.0% (755 of 1006 strings)

Co-authored-by: Eduardo Lima <edu200399lim@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/pt/
Translation: Fedilab/Strings
2022-12-30 15:49:45 +01:00
Thomas
9466a76071 comment #702 - Enable extra features per account 2022-12-30 12:28:03 +01:00
Thomas
d55baa92af comment #702 - Improve reactions and quotes 2022-12-30 12:10:52 +01:00
Thomas
23cd690f33 comment #702 - Support quotes 2022-12-29 19:02:15 +01:00
Thomas
cfcab79cf8 Some fixes 2022-12-29 17:27:11 +01:00
Thomas
6ff29f3514 Fix issue #697 - Media not displayed for instances that doe not support media sizes. 2022-12-29 17:22:10 +01:00
Thomas
3a87f65b33 Merge remote-tracking branch 'origin/develop' into develop 2022-12-29 17:08:40 +01:00
Thomas
f1940f5f24 Fix issue when account is null 2022-12-29 17:08:15 +01:00
Thomas
4b1897921a Fix spoiler with media 2022-12-29 14:28:35 +01:00
Hosted Weblate
7fc812073e
Update translation files
Updated by "Remove blank strings" hook in Weblate.

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/
Translation: Fedilab/Strings
2022-12-29 06:50:49 +01:00
GunChleoc
bbf1f20a67
Translated using Weblate (Gaelic)
Currently translated at 33.0% (332 of 1006 strings)

Co-authored-by: GunChleoc <fios@foramnagaidhlig.net>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/gd/
Translation: Fedilab/Strings
2022-12-29 06:50:48 +01:00
Oğuz Ersen
4e12d3fcdd
Translated using Weblate (Turkish)
Currently translated at 100.0% (1006 of 1006 strings)

Co-authored-by: Oğuz Ersen <oguz@ersen.moe>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/tr/
Translation: Fedilab/Strings
2022-12-29 06:50:48 +01:00
Eduardo Lima
8cc8e01614
Translated using Weblate (Portuguese)
Currently translated at 73.9% (744 of 1006 strings)

Co-authored-by: Eduardo Lima <edu200399lim@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/pt/
Translation: Fedilab/Strings
2022-12-29 06:50:48 +01:00
aqC3ALziMWG1
b91f249efc
Translated using Weblate (German)
Currently translated at 100.0% (1006 of 1006 strings)

Co-authored-by: aqC3ALziMWG1 <weblate@claleb.de>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/de/
Translation: Fedilab/Strings
2022-12-29 06:50:47 +01:00
Lukáš Jelínek
51fc16f4a2
Translated using Weblate (Czech)
Currently translated at 99.6% (1002 of 1006 strings)

Co-authored-by: Lukáš Jelínek <devel@aiken.cz>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/cs/
Translation: Fedilab/Strings
2022-12-29 06:50:47 +01:00
Thomas
5766bfce32 Merge branch 'develop' 2022-12-28 18:01:57 +01:00
Thomas
05a7ac4081 Release 3.13.2 2022-12-28 18:01:31 +01:00
Thomas
07d358fa91 Fix issue #682 - Smaller size when media are hidden with fit preview images 2022-12-28 17:53:16 +01:00
Thomas
639641c80a Release notes 2022-12-28 16:21:26 +01:00
Thomas
b72964c851 Fix emoji not displayed 2022-12-28 16:20:14 +01:00
Thomas
3e16d0c310 Fix issue #694 - Home muted not working with no filters 2022-12-28 15:36:39 +01:00
Thomas
d34425f5b3 Merge remote-tracking branch 'origin/develop' into develop 2022-12-28 15:10:29 +01:00
Thomas
a7cc493922 Fix issue #690 - Crash when visiting a profile with a lot of media 2022-12-28 15:10:23 +01:00
Thomas
b6c908b249 Release notes 2022-12-28 15:01:58 +01:00
Thomas
fc4e7fddbe Fix DeepL pro URL 2022-12-28 15:01:25 +01:00
Paulo Marinho
00009707b2
Translated using Weblate (Portuguese)
Currently translated at 7.8% (5 of 64 strings)

Co-authored-by: Paulo Marinho <paulomarinho77@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/description/pt/
Translation: Fedilab/description
2022-12-28 12:51:00 +01:00
Thomas
2c48032127 Request notification for Android 13+ 2022-12-28 11:58:53 +01:00
Thomas
20c031f39e Issue with media 2022-12-28 11:44:36 +01:00
Thomas
f34bfabc5e Merge branch 'develop' 2022-12-27 18:00:52 +01:00
Thomas
e265078210 Secure zip 2022-12-27 18:00:24 +01:00
Thomas
7dfedbdaa4 Merge branch 'develop' 2022-12-27 17:43:00 +01:00
Thomas
eb78c80925 Merge remote-tracking branch 'origin/develop' into develop 2022-12-27 17:39:34 +01:00
Thomas
d70231cd78 Release 3.13.1 2022-12-27 17:39:29 +01:00
Oğuz Ersen
5ca727705b
Translated using Weblate (Turkish)
Currently translated at 100.0% (998 of 998 strings)

Co-authored-by: Oğuz Ersen <oguz@ersen.moe>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/tr/
Translation: Fedilab/Strings
2022-12-27 16:58:49 +01:00
RintanBroadleaf
01e5893c95
Translated using Weblate (Japanese)
Currently translated at 99.8% (997 of 998 strings)

Co-authored-by: RintanBroadleaf <rintanbroadleaf@outlook.jp>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/ja/
Translation: Fedilab/Strings
2022-12-27 16:58:49 +01:00
josé m
4d91c400ab
Translated using Weblate (Galician)
Currently translated at 100.0% (998 of 998 strings)

Co-authored-by: josé m <correoxm@disroot.org>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/gl/
Translation: Fedilab/Strings
2022-12-27 16:58:49 +01:00
Lukáš Jelínek
608843b20a
Translated using Weblate (Czech)
Currently translated at 99.5% (994 of 998 strings)

Co-authored-by: Lukáš Jelínek <devel@aiken.cz>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/cs/
Translation: Fedilab/Strings
2022-12-27 16:58:49 +01:00
Thomas
852be8c608 Fix issue #691 - Suggested accounts cannot be followed 2022-12-27 16:58:38 +01:00
Thomas
4be595ea54 Add deepl support 2022-12-27 16:50:08 +01:00
Thomas
089d6b9ba8 Merge branch 'develop' 2022-12-26 18:16:45 +01:00
Thomas
b9eca58ccb Release 3.13.0 2022-12-26 18:13:50 +01:00
Thomas
48009141dd Fix name in reorder timelines for Nitter 2022-12-26 17:56:26 +01:00
Thomas
ac087eccfd Fix issue #686 - Users with on char username are not linked 2022-12-26 17:53:57 +01:00
Thomas
6bb3573eb4 Merge remote-tracking branch 'origin/develop' into develop 2022-12-26 15:59:03 +01:00
Thomas
fdd1fd1eb8 Fix issue #677 - Channel sounds not applied 2022-12-26 15:58:56 +01:00
aqC3ALziMWG1
3386f3cc29
Translated using Weblate (German)
Currently translated at 100.0% (998 of 998 strings)

Co-authored-by: aqC3ALziMWG1 <weblate@claleb.de>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/de/
Translation: Fedilab/Strings
2022-12-26 15:29:14 +01:00
josé m
188a09c57d
Translated using Weblate (Galician)
Currently translated at 100.0% (995 of 995 strings)

Co-authored-by: josé m <correoxm@disroot.org>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/gl/
Translation: Fedilab/Strings
2022-12-26 15:29:14 +01:00
Thomas
da18537883 Fix issue #680 - Fix link color issues. 2022-12-26 15:28:23 +01:00
Thomas
4b1792040e Fix issue #685 - Bouncing Timeline on refresh 2022-12-26 15:14:54 +01:00
Thomas
e7c3f04fd0 Merge remote-tracking branch 'origin/develop' into develop 2022-12-26 11:43:33 +01:00
Thomas
9f666bafc6 Fix issue #681 - Remove left margin - Default disabled. 2022-12-26 11:43:23 +01:00
3raven
a5fed1e7f0
Translated using Weblate (French)
Currently translated at 100.0% (62 of 62 strings)

Co-authored-by: 3raven <elise_declerck@laposte.net>
Translate-URL: https://hosted.weblate.org/projects/fedilab/description/fr/
Translation: Fedilab/description
2022-12-26 09:49:11 +01:00
3raven
67db19e8b9
Translated using Weblate (French)
Currently translated at 99.7% (993 of 995 strings)

Co-authored-by: 3raven <elise_declerck@laposte.net>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/fr/
Translation: Fedilab/Strings
2022-12-26 08:48:18 +01:00
Thomas
7a8f07d758 Release 3.12.3 2022-12-25 18:15:41 +01:00
Thomas
70e8756974 Fix issue #672 - Support pagination for search / trending 2022-12-25 18:07:25 +01:00
Thomas
e91a2f7984 Fix issue #672 - Support pagination for search / trending 2022-12-25 17:56:31 +01:00
Thomas
f38ae254c1 Fix issue #678 - Wrong instance fetched for instances.social 2022-12-25 16:40:08 +01:00
Thomas
da367ac0c0 Fix issue #675 - Chars sizes not applied for Android 5 2022-12-25 16:34:41 +01:00
Thomas
4e76f2476f Merge remote-tracking branch 'origin/develop' into develop 2022-12-25 16:26:26 +01:00
Thomas
de4faeadbc Fix issue #645 - Open with another account when there are only two accounts 2022-12-25 16:26:21 +01:00
Thomas
7a3c34b749 Fix notifications follow request not removed from cache 2022-12-25 16:21:26 +01:00
Kalle Kniivilä
77a489287b
Translated using Weblate (Esperanto)
Currently translated at 64.0% (637 of 995 strings)

Co-authored-by: Kalle Kniivilä <kalle.kniivila@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/eo/
Translation: Fedilab/Strings
2022-12-25 11:49:02 +01:00
Thomas
e94fd10d90 Merge remote-tracking branch 'origin/develop' into develop 2022-12-25 10:59:19 +01:00
Thomas
1fb129eada Fix #676 - Rollback to previous changes 2022-12-25 10:57:48 +01:00
0xd9a
78447a4c90 fix: hidden buttons in compose can be pressed 2022-12-24 22:27:24 +05:30
Thomas
ef2aafed67 Release 3.12.2 2022-12-24 16:54:54 +01:00
Thomas
c80942e02d Release 3.12.2 2022-12-24 16:53:51 +01:00
Thomas
9819e2d9d3 Fix crashes 2022-12-24 16:52:07 +01:00
Thomas
7030b1e20a Fix crashes 2022-12-24 16:50:40 +01:00
Thomas
c5030087d4 release notes 2022-12-24 16:25:56 +01:00
Thomas
1c9ebf14f9 Merge remote-tracking branch 'origin/develop' into develop 2022-12-24 16:24:00 +01:00
Thomas
e919e98b68 Fix issue #665 - Adding description failed when sharing 2022-12-24 16:23:46 +01:00
0xd9a
626e05b6a0 Update compose UI 2022-12-24 19:23:16 +05:30
0xd9a
c63afc8097 Use 'chip' for description button in compose 2022-12-24 16:20:21 +05:30
Thomas
c1814aa5e9 Update release notes 2022-12-24 10:36:47 +01:00
Thomas
0c60c9dbed Merge remote-tracking branch 'origin/develop' into develop 2022-12-24 10:32:18 +01:00
Thomas
209387820a Fix issue #674 - Fix fail when displaying thread from remote instances 2022-12-24 10:32:12 +01:00
0xd9a
136be83ed5 Update compose UI 2022-12-24 15:00:01 +05:30
Thomas
eb9b5d41a3 Release notes 2022-12-23 18:36:04 +01:00
Thomas
45dad375ad Fix #671 - Improve toggle for blocked accounts 2022-12-23 18:32:56 +01:00
Thomas
c9479aee20 Merge remote-tracking branch 'origin/develop' into develop 2022-12-23 18:08:18 +01:00
Thomas
9dc1b881e3 Fix #669 - Improve regex 2022-12-23 18:08:07 +01:00
0xd9a
f7f2ac542e Update screenshots 2022-12-23 22:24:13 +05:30
0xd9a
ebb809a0dd Remove scrim color in profile activity 2022-12-23 22:21:14 +05:30
Thomas
fbf7577ef7 Merge remote-tracking branch 'origin/develop' into develop 2022-12-23 17:34:41 +01:00
Thomas
ba7c404699 some fixes 2022-12-23 17:34:00 +01:00
Thomas
e5f36c3e43 switch lib 2022-12-23 11:36:03 +01:00
Ajeje Brazorf
f658696c34
Translated using Weblate (Sardinian)
Currently translated at 99.2% (988 of 995 strings)

Co-authored-by: Ajeje Brazorf <lmelonimamo@yahoo.it>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/sc/
Translation: Fedilab/Strings
2022-12-23 05:48:49 +01:00
Eduardo Lima
d9ed8bc3ea
Translated using Weblate (Portuguese)
Currently translated at 69.5% (692 of 995 strings)

Co-authored-by: Eduardo Lima <edu200399lim@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/pt/
Translation: Fedilab/Strings
2022-12-23 05:48:49 +01:00
ButterflyOfFire
ba4874a067
Translated using Weblate (French)
Currently translated at 99.6% (992 of 995 strings)

Translated using Weblate (Arabic)

Currently translated at 86.4% (860 of 995 strings)

Co-authored-by: ButterflyOfFire <ButterflyOfFire@protonmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/ar/
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/fr/
Translation: Fedilab/Strings
2022-12-23 05:48:48 +01:00
Eduardo Lima
51a09b05b1
Translated using Weblate (Portuguese)
Currently translated at 70.6% (703 of 995 strings)

Co-authored-by: Eduardo Lima <edu200399lim@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/pt/
Translation: Fedilab/Strings
2022-12-22 07:50:55 +01:00
Oğuz Ersen
4bc128dbf9
Translated using Weblate (Turkish)
Currently translated at 100.0% (995 of 995 strings)

Co-authored-by: Oğuz Ersen <oguz@ersen.moe>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/tr/
Translation: Fedilab/Strings
2022-12-21 22:21:04 +01:00
Thomas
5e619d7e5c Rename Nitter instances 2022-12-21 18:58:40 +01:00
Thomas
5de4360f38 Merge remote-tracking branch 'origin/develop' into develop 2022-12-21 18:32:22 +01:00
Thomas
8c44f338da Rename Nitter instances 2022-12-21 18:31:21 +01:00
Thomas
8e93b2d2bc Release notes 2022-12-21 15:15:22 +01:00
Oğuz Ersen
29cf55338f
Translated using Weblate (Turkish)
Currently translated at 100.0% (994 of 994 strings)

Co-authored-by: Oğuz Ersen <oguz@ersen.moe>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/tr/
Translation: Fedilab/Strings
2022-12-21 15:12:59 +01:00
Ajeje Brazorf
0001aa0c2c
Translated using Weblate (Sardinian)
Currently translated at 99.3% (988 of 994 strings)

Co-authored-by: Ajeje Brazorf <lmelonimamo@yahoo.it>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/sc/
Translation: Fedilab/Strings
2022-12-21 15:12:59 +01:00
Screenshared
f51c2532ea
Translated using Weblate (Italian)
Currently translated at 98.9% (982 of 992 strings)

Co-authored-by: Screenshared <gian18400@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/it/
Translation: Fedilab/Strings
2022-12-21 15:12:59 +01:00
josé m
506286c3b6
Translated using Weblate (Galician)
Currently translated at 99.8% (991 of 992 strings)

Co-authored-by: josé m <correoxm@disroot.org>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/gl/
Translation: Fedilab/Strings
2022-12-21 15:12:59 +01:00
Kalle Kniivilä
c9c3d34535
Translated using Weblate (Esperanto)
Currently translated at 64.1% (636 of 992 strings)

Co-authored-by: Kalle Kniivilä <kalle.kniivila@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/eo/
Translation: Fedilab/Strings
2022-12-21 15:12:59 +01:00
Lukáš Jelínek
b2e9a08e95
Translated using Weblate (Czech)
Currently translated at 99.4% (989 of 994 strings)

Translated using Weblate (Czech)

Currently translated at 99.4% (987 of 992 strings)

Co-authored-by: Lukáš Jelínek <devel@aiken.cz>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/cs/
Translation: Fedilab/Strings
2022-12-21 15:12:59 +01:00
Thomas
4fa8959f11 Fix issue #663 - Support nav & status bar colors for Android 5 2022-12-21 15:12:44 +01:00
Thomas
f4a38504cd Fix issue #662 - Fix nav bar color in light mode 2022-12-21 14:49:01 +01:00
Thomas
fa2355b25f Release 3.12.1 2022-12-20 17:39:27 +01:00
Thomas
3a1671ae38 Respect blank spaces between words 2022-12-20 14:03:48 +01:00
Thomas
b428cf4de9 Group reblogs 2022-12-20 10:24:03 +01:00
Thomas
611c22a5c8 Display translate icon only when language is different 2022-12-20 09:53:34 +01:00
Thomas
d7a2a6b855 Fix jumps with media preview for some devices 2022-12-19 16:20:42 +01:00
Thomas
221bf45f27 Fix issue #654 - Truncated gimini links 2022-12-19 15:09:14 +01:00
Thomas
9cefa6ab41 Merge remote-tracking branch 'origin/develop' into develop 2022-12-19 14:27:19 +01:00
Thomas
898c5f7cee Fix issue #658 - Toggle cw does not honor choice 2022-12-19 14:26:56 +01:00
Thomas
fbeea6a8b8 Fix issue #658 2022-12-19 14:19:22 +01:00
Thomas
625f15fb32 Allow to get a quote 2022-12-19 12:42:29 +01:00
Oğuz Ersen
d0171236a8
Translated using Weblate (Turkish)
Currently translated at 100.0% (992 of 992 strings)

Co-authored-by: Oğuz Ersen <oguz@ersen.moe>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/tr/
Translation: Fedilab/Strings
2022-12-19 05:50:35 +01:00
Kalle Kniivilä
867f5dbae7
Translated using Weblate (Esperanto)
Currently translated at 64.1% (636 of 992 strings)

Co-authored-by: Kalle Kniivilä <kalle.kniivila@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/eo/
Translation: Fedilab/Strings
2022-12-19 05:50:35 +01:00
Lukáš Jelínek
c0f103b97c
Translated using Weblate (Czech)
Currently translated at 99.3% (986 of 992 strings)

Co-authored-by: Lukáš Jelínek <devel@aiken.cz>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/cs/
Translation: Fedilab/Strings
2022-12-19 05:50:35 +01:00
Kalle Kniivilä
5e40051659
Translated using Weblate (Esperanto)
Currently translated at 5.0% (3 of 59 strings)

Co-authored-by: Kalle Kniivilä <kalle.kniivila@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/description/eo/
Translation: Fedilab/description
2022-12-19 04:49:42 +01:00
Thomas
8e3b146a77 Merge branch 'develop' 2022-12-18 15:43:47 +01:00
Thomas
a63f1a9300 Fix issue #650 2022-12-18 15:42:48 +01:00
Thomas
a5fd4cf841 Fix issue #650 2022-12-18 15:42:14 +01:00
Thomas
324b67c6fa Add release notes 2022-12-18 15:37:34 +01:00
Thomas
aa6652c350 Release 3.12.0 2022-12-18 15:36:35 +01:00
Thomas
15bbb83c65 Fix top notification badges 2022-12-18 15:28:42 +01:00
Thomas
4dd9924857 clean 2022-12-18 15:24:02 +01:00
Thomas
dcb584eeb4 Fix solarized 2022-12-18 15:23:11 +01:00
Thomas
8dedd7a907 Fix solarized 2022-12-18 11:53:28 +01:00
Thomas
4ed081491f Start release notes 2022-12-18 11:45:08 +01:00
Thomas
3fcb0cc6e0 Fix theme bar color 2022-12-18 11:38:51 +01:00
Thomas
769c713a55 Themed icons for Android 13 2022-12-18 11:10:26 +01:00
Thomas
9491d8aa76 Fix solarized dark theme 2022-12-18 10:51:04 +01:00
Thomas
5e4dac1456 Fix filters 2022-12-18 10:36:55 +01:00
Thomas
eae0df43c0 Merge remote-tracking branch 'origin/develop' into develop 2022-12-18 10:04:29 +01:00
Thomas
52ccfdb860 Export/Import all data 2022-12-18 10:03:29 +01:00
Ajeje Brazorf
a108fb7805
Translated using Weblate (Sardinian)
Currently translated at 99.4% (986 of 991 strings)

Co-authored-by: Ajeje Brazorf <lmelonimamo@yahoo.it>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/sc/
Translation: Fedilab/Strings
2022-12-18 06:51:01 +01:00
Oliebol
30508bdc4a
Translated using Weblate (Dutch)
Currently translated at 100.0% (991 of 991 strings)

Co-authored-by: Oliebol <schrijfmedan@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/nl/
Translation: Fedilab/Strings
2022-12-17 09:48:08 +01:00
Thomas
376daf7c63 Merge branch 'develop' 2022-12-16 18:21:27 +01:00
Screenshared
0d9b5b4300
Translated using Weblate (Italian)
Currently translated at 97.9% (971 of 991 strings)

Co-authored-by: Screenshared <gian18400@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/it/
Translation: Fedilab/Strings
2022-12-16 18:21:15 +01:00
jy-fr
0a46f218be
Translated using Weblate (French)
Currently translated at 99.7% (989 of 991 strings)

Co-authored-by: jy-fr <jy.jurie@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/fr/
Translation: Fedilab/Strings
2022-12-16 18:21:15 +01:00
Codimp
8409fbd080
Translated using Weblate (French)
Currently translated at 99.7% (989 of 991 strings)

Co-authored-by: Codimp <contact@lithio.fr>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/fr/
Translation: Fedilab/Strings
2022-12-16 18:21:15 +01:00
Lukáš Jelínek
09a223074a
Translated using Weblate (Czech)
Currently translated at 99.2% (984 of 991 strings)

Co-authored-by: Lukáš Jelínek <devel@aiken.cz>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/cs/
Translation: Fedilab/Strings
2022-12-16 18:21:15 +01:00
Thomas
f858a870bd Release 3.11.3 2022-12-16 18:16:04 +01:00
Thomas
43c6fe4f7b Fix sensitive media not hidden whe clicking on the eye icon 2022-12-16 17:57:47 +01:00
Thomas
84b33751a2 Merge remote-tracking branch 'origin/develop' into develop 2022-12-16 17:26:34 +01:00
Thomas
0cbf0bfbd4 Some fixes 2022-12-16 17:15:46 +01:00
Codimp
29117125ef
Translated using Weblate (French)
Currently translated at 94.7% (54 of 57 strings)

Co-authored-by: Codimp <contact@lithio.fr>
Translate-URL: https://hosted.weblate.org/projects/fedilab/description/fr/
Translation: Fedilab/description
2022-12-16 14:50:10 +01:00
Thomas
30f21c035d Fix a crash when app takes long to load 2022-12-16 10:46:13 +01:00
Thomas
3c7129f47a Add more targeted languages in picker for translations 2022-12-16 10:33:56 +01:00
Thomas
d0342143ef Add acct to notifications 2022-12-16 10:06:43 +01:00
Thomas
1cf3519951 Fix a crash when changing language 2022-12-16 09:48:39 +01:00
Thomas
295243d781 fix counter colors 2022-12-16 09:40:41 +01:00
Thomas
81dd820d0a fix default link color 2022-12-16 09:31:01 +01:00
Thomas
e0953ff5af fix a crash 2022-12-16 09:15:40 +01:00
Thomas
2a675da7c2 Merge branch 'develop' 2022-12-15 18:26:43 +01:00
Thomas
4ec3a430bd Release 3.11.2 2022-12-15 18:25:05 +01:00
Thomas
8ff8223378 Release 3.11.2 2022-12-15 18:19:19 +01:00
Thomas
f35ac8ae6b Improve filters 2022-12-15 18:17:27 +01:00
Thomas
c50df7fc5b Merge branch 'develop' of https://codeberg.org/tom79/Fedilab into develop 2022-12-15 16:53:13 +01:00
Thomas
c04462e19b Fix a crash 2022-12-15 16:52:51 +01:00
Hosted Weblate
b212e90bf0
Update translation files
Updated by "Remove blank strings" hook in Weblate.

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/
Translation: Fedilab/Strings
2022-12-15 13:32:27 +01:00
Screenshared
852f4e7f7a
Translated using Weblate (Italian)
Currently translated at 96.5% (957 of 991 strings)

Co-authored-by: Screenshared <gian18400@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/it/
Translation: Fedilab/Strings
2022-12-15 13:32:27 +01:00
Thomas
d7976734ce Fix issue #636 - Freeze with the app. 2022-12-15 10:46:31 +01:00
Oğuz Ersen
edf4f01309
Translated using Weblate (Turkish)
Currently translated at 100.0% (991 of 991 strings)

Co-authored-by: Oğuz Ersen <oguz@ersen.moe>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/tr/
Translation: Fedilab/Strings
2022-12-15 09:48:26 +01:00
Lukáš Jelínek
a1542f8cf8
Translated using Weblate (Czech)
Currently translated at 98.0% (972 of 991 strings)

Co-authored-by: Lukáš Jelínek <devel@aiken.cz>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/cs/
Translation: Fedilab/Strings
2022-12-15 09:48:26 +01:00
Thomas
15829da776 Release 3.11.1 2022-12-14 15:08:10 +01:00
Thomas
8ae3ff3b35 Merge remote-tracking branch 'origin/develop' into develop 2022-12-14 14:49:08 +01:00
Thomas
a55401df77 Mute from lists 2022-12-14 14:48:32 +01:00
Thomas
d8de016407 Allow to mute/unmute from the list with a new icon 2022-12-14 14:29:20 +01:00
Thomas
807ff6c9da Some changes 2022-12-14 12:15:41 +01:00
Thomas
7a93b22f77 Display home muted 2022-12-14 11:55:19 +01:00
Ajeje Brazorf
83f0699d47
Translated using Weblate (Sardinian)
Currently translated at 99.4% (980 of 985 strings)

Co-authored-by: Ajeje Brazorf <lmelonimamo@yahoo.it>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/sc/
Translation: Fedilab/Strings
2022-12-14 11:49:08 +01:00
Lukáš Jelínek
7d03f5b6c0
Translated using Weblate (Czech)
Currently translated at 98.6% (972 of 985 strings)

Co-authored-by: Lukáš Jelínek <devel@aiken.cz>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/cs/
Translation: Fedilab/Strings
2022-12-14 11:49:08 +01:00
Thomas
93fce1f56e Allow to mute/unmute from profile 2022-12-14 11:21:00 +01:00
Thomas
5e11438f0c Manage accounts 2022-12-14 10:40:29 +01:00
Thomas
7f1098e345 Create table / update db 2022-12-14 09:55:04 +01:00
Thomas
12981deafb Merge branch 'develop' 2022-12-13 18:03:00 +01:00
Thomas
0772f47b13 Release 3.11.0 2022-12-13 18:01:14 +01:00
Thomas
269b83607c Fix issue #629 - Remove animations after refresh 2022-12-13 16:28:26 +01:00
Thomas
a7563d2859 Merge remote-tracking branch 'origin/develop' into develop 2022-12-13 16:09:04 +01:00
Thomas
1d350f46d6 Fix issue #550 - Quick account switch 2022-12-13 16:08:44 +01:00
Thomas
167c3e6251 Fix some crashes 2022-12-13 12:04:35 +01:00
Thomas
770eef7629 Convert to java 2022-12-13 10:48:06 +01:00
Oğuz Ersen
c5bc6413b4
Translated using Weblate (Turkish)
Currently translated at 100.0% (985 of 985 strings)

Co-authored-by: Oğuz Ersen <oguz@ersen.moe>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/tr/
Translation: Fedilab/Strings
2022-12-13 09:48:52 +01:00
Lukáš Jelínek
79adda7059
Translated using Weblate (Czech)
Currently translated at 98.6% (972 of 985 strings)

Co-authored-by: Lukáš Jelínek <devel@aiken.cz>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/cs/
Translation: Fedilab/Strings
2022-12-13 09:48:52 +01:00
Thomas
5f977d0422 Release 3.10.2 2022-12-12 18:36:56 +01:00
Thomas
6fc8275aee Automatically attach tag to message when composing from a tag timeline 2022-12-12 18:29:49 +01:00
Thomas
c59b1e6f3f Fix tag colors 2022-12-12 18:19:27 +01:00
Thomas
bd8d3405b2 Fix contact not added when composing 2022-12-12 18:13:06 +01:00
Thomas
2c32113476 Some fixes when resuming 2022-12-12 17:30:23 +01:00
Thomas
792b0ca77e Fix issue #599 - Edit messages in thread are duplicated 2022-12-12 16:15:58 +01:00
Thomas
9846d9ce79 Small fix in login 2022-12-12 15:50:47 +01:00
Thomas
173043a684 Fix issue #614 - background color for Android 5 2022-12-12 15:36:05 +01:00
Thomas
202e527b5f Fix issue #603 - Fix status bar color 2022-12-12 14:51:34 +01:00
Thomas
5a0eff4ae8 Fix issue #623 - Allow to display the translate button at the bottom. 2022-12-12 12:18:19 +01:00
Thomas
1b643bcb1d Fix issue #615 - add reverse option for tags + Change label descriptions 2022-12-12 11:57:41 +01:00
Thomas
83ffc82851 Merge remote-tracking branch 'origin/develop' into develop 2022-12-12 09:19:52 +01:00
Thomas
f6c0d8345b Fix issue #622 - Rollback to previous view for notifications 2022-12-12 09:17:50 +01:00
Lukáš Jelínek
529fbaede8
Translated using Weblate (Czech)
Currently translated at 98.8% (967 of 978 strings)

Co-authored-by: Lukáš Jelínek <devel@aiken.cz>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/cs/
Translation: Fedilab/Strings
2022-12-12 02:51:26 +01:00
josé m
072240edd0
Translated using Weblate (Galician)
Currently translated at 7.5% (4 of 53 strings)

Co-authored-by: josé m <correoxm@disroot.org>
Translate-URL: https://hosted.weblate.org/projects/fedilab/description/gl/
Translation: Fedilab/description
2022-12-11 17:49:41 +01:00
Thomas
536e6d9c47 Merge remote-tracking branch 'origin/develop' into develop 2022-12-11 17:10:05 +01:00
Thomas
34a1ae16d8 Fix issue #621 - Mentions in bio does not open the correct account 2022-12-11 17:09:57 +01:00
josé m
8310b03b61
Translated using Weblate (Galician)
Currently translated at 100.0% (978 of 978 strings)

Co-authored-by: josé m <correoxm@disroot.org>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/gl/
Translation: Fedilab/Strings
2022-12-11 11:53:49 +01:00
Eric
4dab4f34a4
Translated using Weblate (Chinese (Simplified))
Currently translated at 65.3% (639 of 978 strings)

Co-authored-by: Eric <hamburger1024@duck.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/zh_Hans/
Translation: Fedilab/Strings
2022-12-11 11:53:49 +01:00
Ajeje Brazorf
7cf79d3304
Translated using Weblate (Sardinian)
Currently translated at 99.4% (973 of 978 strings)

Co-authored-by: Ajeje Brazorf <lmelonimamo@yahoo.it>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/sc/
Translation: Fedilab/Strings
2022-12-11 11:53:49 +01:00
Lukáš Jelínek
89467ce58b
Translated using Weblate (Czech)
Currently translated at 98.7% (966 of 978 strings)

Co-authored-by: Lukáš Jelínek <devel@aiken.cz>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/cs/
Translation: Fedilab/Strings
2022-12-11 11:53:49 +01:00
Oğuz Ersen
ea2756f013
Translated using Weblate (Turkish)
Currently translated at 100.0% (978 of 978 strings)

Co-authored-by: Oğuz Ersen <oguz@ersen.moe>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/tr/
Translation: Fedilab/Strings
2022-12-11 11:53:49 +01:00
Eduardo Lima
6cd4fc7d78
Translated using Weblate (Portuguese)
Currently translated at 71.5% (697 of 974 strings)

Co-authored-by: Eduardo Lima <edu200399lim@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/pt/
Translation: Fedilab/Strings
2022-12-11 11:53:48 +01:00
Thomas
6da23a9da3 Fix issue #617 - Update counters 2022-12-11 11:53:40 +01:00
Thomas
bd66462968 Fix issue #617 2022-12-11 11:36:39 +01:00
Thomas
7fc8f96eef Fix visibility for description warning 2022-12-10 19:01:02 +01:00
Thomas
2ec7142561 Fix a display issue with notifications 2022-12-10 18:36:13 +01:00
Thomas
c36d6f4bdd Fix a display issue with notifications 2022-12-10 17:01:31 +01:00
Thomas
57551a716e Release 3.10.1 2022-12-10 10:57:28 +01:00
Thomas
2edb2759e5 Merge remote-tracking branch 'origin/develop' into develop 2022-12-10 10:38:47 +01:00
Thomas
a8eb33f3dd Allow to check remote conversations 2022-12-10 10:38:01 +01:00
Thomas
dbda1e13d2 Merge branch 'develop' into check_remote_context 2022-12-10 10:08:22 +01:00
Hosted Weblate
0f2b5db07b
Update translation files
Updated by "Remove blank strings" hook in Weblate.

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/
Translation: Fedilab/Strings
2022-12-10 00:19:30 +01:00
Oğuz Ersen
ebd4f9ba89
Translated using Weblate (Turkish)
Currently translated at 100.0% (974 of 974 strings)

Co-authored-by: Oğuz Ersen <oguz@ersen.moe>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/tr/
Translation: Fedilab/Strings
2022-12-10 00:19:29 +01:00
Ajeje Brazorf
550f93b735
Translated using Weblate (Sardinian)
Currently translated at 98.5% (960 of 974 strings)

Co-authored-by: Ajeje Brazorf <lmelonimamo@yahoo.it>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/sc/
Translation: Fedilab/Strings
2022-12-10 00:19:29 +01:00
Eduardo Lima
e6a499fcc5
Translated using Weblate (Portuguese)
Currently translated at 71.3% (695 of 974 strings)

Co-authored-by: Eduardo Lima <edu200399lim@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/pt/
Translation: Fedilab/Strings
2022-12-10 00:19:29 +01:00
ButterflyOfFire
fd482317dd
Translated using Weblate (Gaelic)
Currently translated at 28.8% (281 of 974 strings)

Translated using Weblate (Silesian)

Currently translated at 62.0% (604 of 974 strings)

Translated using Weblate (Russian)

Currently translated at 61.9% (603 of 974 strings)

Translated using Weblate (Portuguese)

Currently translated at 71.3% (695 of 974 strings)

Translated using Weblate (Norwegian Bokmål)

Currently translated at 68.6% (669 of 974 strings)

Translated using Weblate (Korean)

Currently translated at 62.0% (604 of 974 strings)

Translated using Weblate (Hungarian)

Currently translated at 61.7% (601 of 974 strings)

Co-authored-by: ButterflyOfFire <ButterflyOfFire@protonmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/gd/
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/hu/
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/ko/
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/nb_NO/
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/pt/
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/ru/
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/szl/
Translation: Fedilab/Strings
2022-12-10 00:19:28 +01:00
Lukáš Jelínek
62356ff873
Translated using Weblate (Czech)
Currently translated at 98.4% (959 of 974 strings)

Co-authored-by: Lukáš Jelínek <devel@aiken.cz>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/cs/
Translation: Fedilab/Strings
2022-12-10 00:19:28 +01:00
Thomas
002bc4886b Merge branch 'develop' 2022-12-09 18:15:26 +01:00
Thomas
6710ef2e1f Release 3.10.0 2022-12-09 18:14:07 +01:00
Thomas
7546d79533 Check status 2022-12-09 18:07:44 +01:00
Thomas
d45ac1b9f9 Fix icon 2022-12-09 17:08:39 +01:00
Thomas
70f9324b8c avoid a crash 2022-12-09 15:37:35 +01:00
Thomas
dda3296154 Changes action icons 2022-12-09 15:36:27 +01:00
Thomas
165f85c8af Fix issue #613 2022-12-09 14:28:53 +01:00
Thomas
c68b4b6b2e Fix issue #366 - Text with links not displayed 2022-12-09 11:55:50 +01:00
Thomas
e7dd2ac5d2 Fix crashes 2022-12-09 11:34:14 +01:00
Thomas
52fa2b0815 Customize notifications and conversations 2022-12-09 10:59:03 +01:00
Thomas
335d87ad11 Fix typo 2022-12-09 10:20:08 +01:00
Thomas
b6d513713e Merge remote-tracking branch 'origin/develop' into develop 2022-12-09 10:18:54 +01:00
Thomas
ae80a27db9 Allow to reset customizations 2022-12-09 10:01:57 +01:00
ButterflyOfFire
c61f4bc49c
Translated using Weblate (Arabic)
Currently translated at 82.1% (776 of 945 strings)

Co-authored-by: ButterflyOfFire <ButterflyOfFire@protonmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/ar/
Translation: Fedilab/Strings
2022-12-09 06:49:56 +01:00
Thomas
d9ed098464 Change colors 2022-12-08 18:56:06 +01:00
Thomas
5337841da0 Pref settings customization of messages 2022-12-08 18:10:51 +01:00
Thomas
724813407d Fix typo 2022-12-08 10:31:58 +01:00
Thomas
6fa23d1bc3 Allow to enable or disable the cardview presentation in settings 2022-12-08 10:20:03 +01:00
Thomas
49df8bda8c Release 3.9.7 2022-12-07 18:40:15 +01:00
Thomas
63e09ee9a4 Release 3.9.7 2022-12-07 18:37:18 +01:00
Thomas
98f8b269b0 Some fixes 2022-12-07 18:12:32 +01:00
Thomas
8ca6f69da7 Theme changes 2022-12-07 17:40:00 +01:00
Thomas
201252b71d Theme changes 2022-12-07 17:39:27 +01:00
Thomas
b6f74cb297 Add Dracula theme 2022-12-07 12:46:34 +01:00
Thomas
6cfe8b7b95 Release 3.9.6 2022-12-06 17:59:28 +01:00
Thomas
c0f3b0f86b Release 3.9.6 2022-12-06 17:55:51 +01:00
Thomas
7ccb1c96b2 Fix issue #607 - Fetch more broken 2022-12-06 17:50:25 +01:00
Thomas
03d6c7f911 Fix issue #604 - Jumps when scrolling up with media 2022-12-06 15:59:31 +01:00
Thomas
83638e9a92 Beta release 3.9.5 2022-12-05 17:34:37 +01:00
Thomas
2cb08046f5 Fix issue #288 - Custom Emoji not always displayed 2022-12-05 16:23:13 +01:00
Thomas
ffb6596c99 Fix issue #598 - Avoid jumps with fit preview images. 2022-12-05 09:01:37 +01:00
Thomas
0c6fe112c9 Fix issue #592 - Second search clears term field 2022-12-04 18:46:51 +01:00
Thomas
427073a369 Fix issue #591 2022-12-04 18:39:41 +01:00
Thomas
65156e0a84 Fix issue when refreshing notifications + fix an issue with cache and DM 2022-12-04 18:25:10 +01:00
Thomas
5290173184 Merge branch 'develop' 2022-12-04 15:50:14 +01:00
Thomas
468f825dc0 Release 3.9.4 2022-12-04 15:49:38 +01:00
Thomas
15ce78b85a Fix a crash 2022-12-04 15:00:28 +01:00
Thomas
0b2eb64a72 Merge branch 'develop' 2022-12-03 19:45:04 +01:00
Thomas
88f5ed27bd Release 3.9.3 2022-12-03 19:44:10 +01:00
Thomas
5c899e6fba Release 3.9.3 2022-12-03 19:42:47 +01:00
Thomas
7ada093a0f Merge branch 'develop' 2022-12-03 18:44:07 +01:00
Thomas
29a5678a47 Release 3.9.2 2022-12-03 18:43:36 +01:00
Thomas
d66c257370 Some fixes 2022-12-03 18:40:00 +01:00
Thomas
77b6c478d1 Some improvements 2022-12-03 18:05:37 +01:00
Thomas
27120026c2 Some improvements 2022-12-03 17:47:59 +01:00
Thomas
7576dc8bee Fix issue #577 2022-12-03 14:49:01 +01:00
Thomas
674ece5e4c Fix issue #577 2022-12-03 14:47:48 +01:00
Thomas
2e3adc0262 Fix issue #578 2022-12-03 14:37:56 +01:00
Thomas
1ea8f1fd45 Fix issue #573 2022-12-03 14:10:16 +01:00
Thomas
dc4f6f5751 Fix issue #583 2022-12-03 14:02:44 +01:00
Thomas
c76cc66643 Merge remote-tracking branch 'origin/develop' into develop 2022-12-03 11:48:20 +01:00
Thomas
bf54150e31 Fix some color issues 2022-12-03 11:48:08 +01:00
Lukáš Jelínek
ce6523e9a1
Translated using Weblate (Czech)
Currently translated at 21.7% (10 of 46 strings)

Co-authored-by: Lukáš Jelínek <devel@aiken.cz>
Translate-URL: https://hosted.weblate.org/projects/fedilab/description/cs/
Translation: Fedilab/description
2022-12-03 11:48:07 +01:00
Thomas
27102284c4 Merge remote-tracking branch 'origin/develop' into develop 2022-12-03 11:24:46 +01:00
Thomas
191dc85d36 Fix issue #581 - List cannot be hidden. 2022-12-03 11:24:25 +01:00
Micha
ef3572abfe
Translated using Weblate (German)
Currently translated at 28.2% (13 of 46 strings)

Co-authored-by: Micha <weblate@slow.pictures>
Translate-URL: https://hosted.weblate.org/projects/fedilab/description/de/
Translation: Fedilab/description
2022-12-02 21:53:58 +01:00
Micha
06ee3f9038
Translated using Weblate (German)
Currently translated at 100.0% (945 of 945 strings)

Co-authored-by: Micha <weblate@slow.pictures>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/de/
Translation: Fedilab/Strings
2022-12-02 21:53:49 +01:00
Thomas
4c1a8a6372 Release 3.9.1 2022-12-02 18:25:47 +01:00
Thomas
b02172242e Release 3.9.1 2022-12-02 18:22:45 +01:00
Thomas
360e075bdf Merge remote-tracking branch 'origin/develop' into develop 2022-12-02 18:18:19 +01:00
Thomas
6f3512354f more place for icons 2022-12-02 18:18:13 +01:00
Micha
344f61d928
Translated using Weblate (German)
Currently translated at 100.0% (946 of 946 strings)

Co-authored-by: Micha <weblate@slow.pictures>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/de/
Translation: Fedilab/Strings
2022-12-02 16:36:50 +01:00
ButterflyOfFire
e2d0c21f48
Translated using Weblate (Arabic)
Currently translated at 80.5% (762 of 946 strings)

Co-authored-by: ButterflyOfFire <ButterflyOfFire@protonmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/ar/
Translation: Fedilab/Strings
2022-12-02 16:36:50 +01:00
Thomas
90cc5b0e06 Fix issue #563 2022-12-02 16:36:23 +01:00
Thomas
0f876f501b some fixes. 2022-12-02 16:14:57 +01:00
Thomas
4e2701cb5e remove data-base 2022-12-02 15:53:58 +01:00
Thomas
95beb7f3c1 Remove built-in browser 2022-12-02 15:48:23 +01:00
Thomas
403fb1aba7 Fix color issue 2022-12-02 10:40:23 +01:00
Thomas
360d7a6952 Fix cards with long title 2022-12-02 10:23:06 +01:00
Thomas
c2c780f58e Some fixes with the theme 2022-12-02 09:57:23 +01:00
Thomas
5fdb654aa2 Merge remote-tracking branch 'origin/develop' into develop 2022-12-01 18:26:56 +01:00
Thomas
774a73c624 Fix #566 #565 #564 2022-12-01 18:26:46 +01:00
Oğuz Ersen
e83c161409
Translated using Weblate (Turkish)
Currently translated at 100.0% (946 of 946 strings)

Co-authored-by: Oğuz Ersen <oguz@ersen.moe>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/tr/
Translation: Fedilab/Strings
2022-12-01 17:55:05 +01:00
Ajeje Brazorf
d6d6459954
Translated using Weblate (Sardinian)
Currently translated at 99.4% (941 of 946 strings)

Co-authored-by: Ajeje Brazorf <lmelonimamo@yahoo.it>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/sc/
Translation: Fedilab/Strings
2022-12-01 17:55:04 +01:00
Oliebol
b2e5c1202a
Translated using Weblate (Dutch)
Currently translated at 100.0% (946 of 946 strings)

Co-authored-by: Oliebol <schrijfmedan@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/nl/
Translation: Fedilab/Strings
2022-12-01 17:55:04 +01:00
Xosé M
d0a4429fe7
Translated using Weblate (Galician)
Currently translated at 100.0% (946 of 946 strings)

Co-authored-by: Xosé M <correoxm@disroot.org>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/gl/
Translation: Fedilab/Strings
2022-12-01 17:55:03 +01:00
Micha
bc6904f49d
Translated using Weblate (German)
Currently translated at 99.2% (939 of 946 strings)

Co-authored-by: Micha <weblate@slow.pictures>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/de/
Translation: Fedilab/Strings
2022-12-01 17:55:03 +01:00
Murat H
f39869bb9e
Translated using Weblate (German)
Currently translated at 99.2% (939 of 946 strings)

Co-authored-by: Murat H <karabela81sta@googlemail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/de/
Translation: Fedilab/Strings
2022-12-01 17:55:03 +01:00
Lukáš Jelínek
1e660c5d01
Translated using Weblate (Czech)
Currently translated at 91.8% (869 of 946 strings)

Co-authored-by: Lukáš Jelínek <devel@aiken.cz>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/cs/
Translation: Fedilab/Strings
2022-12-01 17:55:02 +01:00
Thomas
e1e5284a47 Release 3.9.0 2022-11-30 18:15:32 +01:00
Thomas
9959904853 Some fixes 2022-11-30 17:07:23 +01:00
Thomas
0ff17dd96f Header more visible 2022-11-30 16:52:54 +01:00
Thomas
4730f7fb53 Header more visible 2022-11-30 16:16:54 +01:00
Thomas
506f5fcf49 Fix some issues 2022-11-30 16:07:12 +01:00
Thomas
d10549ddd1 Merge remote-tracking branch 'origin/develop' into develop 2022-11-30 15:52:47 +01:00
Thomas
bd5fccf2db Fix issue 558 - App doesn't remember position on profiles/fav/bookmarks etc. 2022-11-30 15:52:41 +01:00
Murat H
46e38c3581
Translated using Weblate (German)
Currently translated at 100.0% (934 of 934 strings)

Co-authored-by: Murat H <karabela81sta@googlemail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/de/
Translation: Fedilab/Strings
2022-11-30 15:27:31 +01:00
Thomas
ef24d7586b Upgrade libs 2022-11-30 15:27:23 +01:00
Thomas
65202df92a Merge remote-tracking branch 'origin/develop' into develop 2022-11-30 15:08:07 +01:00
Thomas
349d8578f0 some fixes 2022-11-30 15:06:34 +01:00
Thomas
29752106d2 Add black theme 2022-11-30 12:12:55 +01:00
Thomas
0c2a6e2aad Switch between themes 2022-11-30 11:49:13 +01:00
Thomas
13b9a43bc3 Split activities 2022-11-30 11:00:53 +01:00
Murat H
1e3933f5df
Translated using Weblate (German)
Currently translated at 100.0% (934 of 934 strings)

Co-authored-by: Murat H <karabela81sta@googlemail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/de/
Translation: Fedilab/Strings
2022-11-30 09:38:48 +01:00
LostInWeb
2de89425f2
Translated using Weblate (German)
Currently translated at 100.0% (934 of 934 strings)

Co-authored-by: LostInWeb <weblate@lostinweb.eu>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/de/
Translation: Fedilab/Strings
2022-11-30 09:38:47 +01:00
LostInWeb
20dbd48c4e
Translated using Weblate (German)
Currently translated at 100.0% (934 of 934 strings)

Co-authored-by: LostInWeb <weblate@lostinweb.eu>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/de/
Translation: Fedilab/Strings
2022-11-30 09:30:09 +01:00
Micha
845bf93bea
Translated using Weblate (German)
Currently translated at 100.0% (934 of 934 strings)

Co-authored-by: Micha <weblate@slow.pictures>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/de/
Translation: Fedilab/Strings
2022-11-30 09:30:09 +01:00
Murat H
24c6803af7
Translated using Weblate (German)
Currently translated at 100.0% (934 of 934 strings)

Co-authored-by: Murat H <karabela81sta@googlemail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/de/
Translation: Fedilab/Strings
2022-11-30 09:30:08 +01:00
joenepraat
6b7f8b6d8a
Translated using Weblate (Dutch)
Currently translated at 100.0% (934 of 934 strings)

Co-authored-by: joenepraat <joenepraat@posteo.org>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/nl/
Translation: Fedilab/Strings
2022-11-30 08:15:04 +01:00
ButterflyOfFire
ade0a24d78
Translated using Weblate (German)
Currently translated at 99.7% (932 of 934 strings)

Co-authored-by: ButterflyOfFire <ButterflyOfFire@protonmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/de/
Translation: Fedilab/Strings
2022-11-30 08:15:04 +01:00
Thomas
7d6f7167fd Fix colors 2022-11-29 18:31:31 +01:00
Ajeje Brazorf
d33397068c
Translated using Weblate (Sardinian)
Currently translated at 99.4% (929 of 934 strings)

Co-authored-by: Ajeje Brazorf <lmelonimamo@yahoo.it>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/sc/
Translation: Fedilab/Strings
2022-11-29 18:26:04 +01:00
Oliebol
98f68de5f8
Translated using Weblate (Dutch)
Currently translated at 97.7% (913 of 934 strings)

Co-authored-by: Oliebol <schrijfmedan@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/nl/
Translation: Fedilab/Strings
2022-11-29 18:26:04 +01:00
Thomas
1dbf3a050c Fix calendar 2022-11-29 16:24:23 +01:00
Thomas
9a10a7aaf6 switch between themes 2022-11-29 15:18:46 +01:00
Thomas
c8a4ffa922 some improvements 2022-11-29 14:30:46 +01:00
Thomas
d98ab320f1 Some fixes 2022-11-29 12:15:20 +01:00
Thomas
542477f37f Some cosmetic 2022-11-29 12:11:20 +01:00
Thomas
1fbce05dbd Improvements 2022-11-29 11:06:37 +01:00
Thomas
635dc94b8d new colors 2022-11-29 10:23:19 +01:00
Thomas
f29122e3ef change colors 2022-11-29 09:44:11 +01:00
Thomas
9f670f5e5e Fix some colors 2022-11-29 08:39:31 +01:00
Thomas
22902ac070 Fix some crashes 2022-11-29 08:33:35 +01:00
Mi Klo
468702553c
Translated using Weblate (Polish)
Currently translated at 81.5% (762 of 934 strings)

Co-authored-by: Mi Klo <peertube@miklobit.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/pl/
Translation: Fedilab/Strings
2022-11-28 22:29:10 +01:00
Cathelijne
bbb0aa7afc
Translated using Weblate (Dutch)
Currently translated at 97.3% (909 of 934 strings)

Co-authored-by: Cathelijne <weblate@hornstra.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/nl/
Translation: Fedilab/Strings
2022-11-28 22:29:10 +01:00
Lon
6556f9aaef
Translated using Weblate (Dutch)
Currently translated at 97.3% (909 of 934 strings)

Co-authored-by: Lon <schrijfmedan@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/nl/
Translation: Fedilab/Strings
2022-11-28 22:29:09 +01:00
malfisya
1dad39145b
Translated using Weblate (Indonesian)
Currently translated at 65.9% (616 of 934 strings)

Co-authored-by: malfisya <ems1000.syahrin@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/id/
Translation: Fedilab/Strings
2022-11-28 22:29:09 +01:00
Xosé M
d9d93026b3
Translated using Weblate (Galician)
Currently translated at 100.0% (934 of 934 strings)

Co-authored-by: Xosé M <correoxm@disroot.org>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/gl/
Translation: Fedilab/Strings
2022-11-28 22:29:09 +01:00
jy-fr
2e5fa92af6
Translated using Weblate (French)
Currently translated at 98.9% (924 of 934 strings)

Co-authored-by: jy-fr <jy.jurie@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/fr/
Translation: Fedilab/Strings
2022-11-28 22:29:08 +01:00
ButterflyOfFire
fdec0c5a58
Translated using Weblate (French)
Currently translated at 98.9% (924 of 934 strings)

Co-authored-by: ButterflyOfFire <ButterflyOfFire@protonmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/fr/
Translation: Fedilab/Strings
2022-11-28 22:29:08 +01:00
Leandro Leites Barrios
dc40026c82
Translated using Weblate (Spanish)
Currently translated at 62.3% (582 of 934 strings)

Co-authored-by: Leandro Leites Barrios <laloleites@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/es/
Translation: Fedilab/Strings
2022-11-28 22:29:08 +01:00
Murat H
7a48cd236a
Translated using Weblate (German)
Currently translated at 100.0% (934 of 934 strings)

Co-authored-by: Murat H <karabela81sta@googlemail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/de/
Translation: Fedilab/Strings
2022-11-28 22:29:07 +01:00
Micha
540ebf9de8
Translated using Weblate (German)
Currently translated at 100.0% (934 of 934 strings)

Co-authored-by: Micha <weblate@slow.pictures>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/de/
Translation: Fedilab/Strings
2022-11-28 22:29:07 +01:00
LostInWeb
96eed3a75b
Translated using Weblate (German)
Currently translated at 100.0% (934 of 934 strings)

Co-authored-by: LostInWeb <weblate@lostinweb.eu>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/de/
Translation: Fedilab/Strings
2022-11-28 22:29:06 +01:00
Lukáš Jelínek
fcaaa39def
Translated using Weblate (Czech)
Currently translated at 84.0% (785 of 934 strings)

Co-authored-by: Lukáš Jelínek <devel@aiken.cz>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/cs/
Translation: Fedilab/Strings
2022-11-28 22:29:06 +01:00
dtalens
d70ae7df94
Translated using Weblate (Catalan)
Currently translated at 88.7% (829 of 934 strings)

Co-authored-by: dtalens <databio@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/ca/
Translation: Fedilab/Strings
2022-11-28 22:29:06 +01:00
Thomas
db3d317a55 some changes 2022-11-28 18:39:48 +01:00
Thomas
11ea166a0d Replacements 2022-11-28 17:06:27 +01:00
Thomas
216887800b Replacements 2022-11-28 16:46:13 +01:00
Thomas
abf1da579c Replacements 2022-11-28 16:34:59 +01:00
Thomas
8a883de5a0 Replacements 2022-11-28 15:56:58 +01:00
Thomas
0ab66d34c1 Replacements 2022-11-28 15:29:54 +01:00
Thomas
ee22181a91 Fix icon colors 2022-11-28 15:14:37 +01:00
Thomas
320f913c63 remove useless elements 2022-11-28 15:03:17 +01:00
Thomas
4ea542c1f8 Some changes 2022-11-28 11:46:48 +01:00
Thomas
ccf7ac5cff Remove cyanea 2022-11-28 10:37:57 +01:00
Thomas
da4ed393a9 Add a note 2022-11-27 17:14:23 +01:00
Thomas
2e3d5b6b94 Release notes for 3.8.1 2022-11-27 17:10:51 +01:00
Thomas
8307cb8e75 Remove cache messages when muting 2022-11-27 16:52:26 +01:00
Thomas
a796d54166 Live remove when muting tags 2022-11-27 16:47:28 +01:00
Thomas
c7420a8760 Fix some crashes 2022-11-27 16:30:51 +01:00
Thomas
ab9b367a38 Hide messages instead of removing them and add fetch more support 2022-11-27 12:27:31 +01:00
Thomas
6f288acbc9 Remove fake user agent 2022-11-27 11:48:14 +01:00
Thomas
6f8ca561bf Fix #517 - force to rescan 2022-11-27 11:47:14 +01:00
Thomas
72f123d26b Fix authentication with external browser does not grant admin scope 2022-11-27 09:53:06 +01:00
Thomas
58dcdbf050 Fix issue #517 - Media not displayed in public gallery for some devices 2022-11-27 09:28:33 +01:00
Thomas
4398873c08 Inverse display name and acct for art 2022-11-27 09:03:47 +01:00
Thomas
f1358df54c Merge remote-tracking branch 'origin/develop' into develop 2022-11-27 08:19:11 +01:00
Thomas
88b6093a53 Fix issue #545 - Open with does not work 2022-11-27 08:18:45 +01:00
Murat H
03725c6072
Translated using Weblate (German)
Currently translated at 16.2% (7 of 43 strings)

Co-authored-by: Murat H <karabela81sta@googlemail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/description/de/
Translation: Fedilab/description
2022-11-26 19:48:26 +01:00
Vri 🌈
e6b0098427
Translated using Weblate (German)
Currently translated at 99.1% (926 of 934 strings)

Co-authored-by: Vri 🌈 <weblate@vrifox.cc>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/de/
Translation: Fedilab/Strings
2022-11-26 18:28:47 +01:00
Oğuz Ersen
28637fb035
Translated using Weblate (Turkish)
Currently translated at 100.0% (934 of 934 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (933 of 933 strings)

Co-authored-by: Oğuz Ersen <oguz@ersen.moe>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/tr/
Translation: Fedilab/Strings
2022-11-26 18:28:47 +01:00
Murat H
3868bd83c9
Translated using Weblate (German)
Currently translated at 99.2% (926 of 933 strings)

Co-authored-by: Murat H <karabela81sta@googlemail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/de/
Translation: Fedilab/Strings
2022-11-26 18:28:47 +01:00
dtalens
ccf11c29c6
Translated using Weblate (Catalan)
Currently translated at 87.0% (812 of 933 strings)

Co-authored-by: dtalens <databio@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/ca/
Translation: Fedilab/Strings
2022-11-26 18:28:47 +01:00
Thomas
c147240384 Fix issue #544 - Jump in profiles 2022-11-26 18:28:00 +01:00
Thomas
487057bbc8 Fix issue #544 - Jump in profiles 2022-11-26 18:10:47 +01:00
Thomas
c0f556290d Fix issue go to top on profiles 2022-11-26 17:56:56 +01:00
Thomas
d96c83a282 Muted tags 2022-11-26 16:46:26 +01:00
Thomas
7dc57d27a4 Set AKKOMA instance as PLEROMA 2022-11-26 11:20:15 +01:00
Thomas
27f1d594bb Merge branch 'develop' 2022-11-25 18:21:33 +01:00
Thomas
5bc122cb5e Merge remote-tracking branch 'origin/develop' into develop 2022-11-25 18:21:07 +01:00
Thomas
401eb4f1ae Release 3.8.0 2022-11-25 18:21:03 +01:00
Thomas
f029f785cc Avoid crash with friendica and suggestions 2022-11-25 17:48:56 +01:00
Thomas
99644a7e64 Some improvements 2022-11-25 17:30:04 +01:00
Thomas
66fb64c5d1 Improve blocked domains for admins 2022-11-25 16:39:52 +01:00
Oğuz Ersen
2a1dbc1802
Translated using Weblate (Turkish)
Currently translated at 100.0% (916 of 916 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (915 of 915 strings)

Co-authored-by: Oğuz Ersen <oguz@ersen.moe>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/tr/
Translation: Fedilab/Strings
2022-11-25 15:13:31 +01:00
Thomas
5d0d838471 Fix issue #527 - Visibility set to unlisted after changing language 2022-11-25 15:13:25 +01:00
Thomas
e66fbf5fd7 Domain blocks for admin (create/update) 2022-11-25 15:03:14 +01:00
Thomas
23d2626370 Fix issue #529 - Lists are ordered alphabetically 'ASC' by default can be reverted with a button 2022-11-25 10:08:57 +01:00
Thomas
099b18014e Fix issue #532 - Make filtered messages smaller 2022-11-25 09:27:58 +01:00
Thomas
39f5a83105 Fix issue #536 - Set as single line 2022-11-25 09:22:44 +01:00
Thomas
6acf279023 Merge remote-tracking branch 'origin/develop' into develop 2022-11-25 09:04:38 +01:00
Thomas
d2644b22a7 Fix issue #538 - Open a message with another account 2022-11-25 09:04:24 +01:00
Thomas
bfa50d19c4 Fix a crash when long pressing URLs 2022-11-25 08:29:11 +01:00
Zekovski
6d39c6a45f
Translated using Weblate (French)
Currently translated at 97.8% (894 of 914 strings)

Co-authored-by: Zekovski <zekovski@e.email>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/fr/
Translation: Fedilab/Strings
2022-11-25 07:45:55 +01:00
Oğuz Ersen
b044246457
Translated using Weblate (Turkish)
Currently translated at 100.0% (914 of 914 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (898 of 898 strings)

Co-authored-by: Oğuz Ersen <oguz@ersen.moe>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/tr/
Translation: Fedilab/Strings
2022-11-25 07:45:55 +01:00
Ajeje Brazorf
0fa5ed41ce
Translated using Weblate (Sardinian)
Currently translated at 98.9% (889 of 898 strings)

Co-authored-by: Ajeje Brazorf <lmelonimamo@yahoo.it>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/sc/
Translation: Fedilab/Strings
2022-11-25 07:45:55 +01:00
RintanBroadleaf
ce9a15d034
Translated using Weblate (Japanese)
Currently translated at 100.0% (898 of 898 strings)

Co-authored-by: RintanBroadleaf <rintanbroadleaf@outlook.jp>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/ja/
Translation: Fedilab/Strings
2022-11-25 07:45:55 +01:00
Xosé M
824be3d2d1
Translated using Weblate (Galician)
Currently translated at 100.0% (898 of 898 strings)

Co-authored-by: Xosé M <correoxm@disroot.org>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/gl/
Translation: Fedilab/Strings
2022-11-25 07:45:55 +01:00
Murat H
f38f93b85b
Translated using Weblate (German)
Currently translated at 99.5% (910 of 914 strings)

Translated using Weblate (German)

Currently translated at 98.3% (883 of 898 strings)

Co-authored-by: Murat H <karabela81sta@googlemail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/de/
Translation: Fedilab/Strings
2022-11-25 07:45:55 +01:00
RintanBroadleaf
6bf67ecbfd
Translated using Weblate (Japanese)
Currently translated at 19.0% (8 of 42 strings)

Co-authored-by: RintanBroadleaf <rintanbroadleaf@outlook.jp>
Translate-URL: https://hosted.weblate.org/projects/fedilab/description/ja/
Translation: Fedilab/description
2022-11-25 07:45:43 +01:00
Murat H
99d6eb0fd0
Translated using Weblate (German)
Currently translated at 14.2% (6 of 42 strings)

Co-authored-by: Murat H <karabela81sta@googlemail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/description/de/
Translation: Fedilab/description
2022-11-25 07:45:43 +01:00
Thomas
f9b87f762b Fix issue #534 - Allow to filter notifications for admin/moderators 2022-11-24 17:33:20 +01:00
Thomas
0b690838bb Fix issue #533 - Add an option to keep notifications when muting 2022-11-24 16:44:24 +01:00
Thomas
b5a304be56 Add some endpoints + ui 2022-11-24 12:32:33 +01:00
Thomas
52ba7ef31c fix url 2022-11-24 11:35:50 +01:00
Thomas
f96de62fef Add api endpoints 2022-11-24 11:24:55 +01:00
Thomas
c6bd8ac265 Add new endpoints and fix migration to v2 2022-11-24 10:53:44 +01:00
Thomas
e32b3bf6da Fix admin with api/v2 2022-11-24 10:35:50 +01:00
Thomas
f70e190863 Merge branch 'main' into develop 2022-11-23 19:17:02 +01:00
Thomas
2d3bb92925 Templates 2022-11-23 19:15:17 +01:00
Thomas
40e6f75251 Merge remote-tracking branch 'origin/develop' into develop 2022-11-23 16:19:06 +01:00
Thomas
132cb89b2a Fix issue #522 - Notifications lost their content and fallback to default view. 2022-11-23 16:19:01 +01:00
Xosé M
637bb92a41
Translated using Weblate (Galician)
Currently translated at 100.0% (895 of 895 strings)

Co-authored-by: Xosé M <correoxm@disroot.org>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/gl/
Translation: Fedilab/Strings
2022-11-23 15:57:12 +01:00
Thomas
9d864857da Fix issue #490 - Remove grouped notifications for Android 5 & 6 2022-11-23 15:57:06 +01:00
Thomas
9c728f24c3 Fix error message when adding the first list 2022-11-23 15:49:31 +01:00
Thomas
4f8377d9d9 Fix issue #524 - List cannot be removed from "Manage timelines" 2022-11-23 15:42:43 +01:00
Thomas
80023219b3 Fix some crashes 2022-11-23 12:13:24 +01:00
Thomas
43fe552ee4 issue template 2022-11-23 10:58:12 +01:00
Thomas
db6470a787 issue template 2022-11-23 10:56:45 +01:00
Thomas
1c19b8815e Merge remote-tracking branch 'origin/develop' into develop 2022-11-23 10:52:16 +01:00
Thomas
0e0fb6e13c Fix crashes from bug reports. 2022-11-23 10:52:09 +01:00
Xosé M
14135ea5ef
Translated using Weblate (Galician)
Currently translated at 100.0% (895 of 895 strings)

Co-authored-by: Xosé M <correoxm@disroot.org>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/gl/
Translation: Fedilab/Strings
2022-11-23 09:09:34 +01:00
Lukáš Jelínek
2b3590353a
Translated using Weblate (Czech)
Currently translated at 85.4% (765 of 895 strings)

Co-authored-by: Lukáš Jelínek <devel@aiken.cz>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/cs/
Translation: Fedilab/Strings
2022-11-23 09:09:34 +01:00
Oğuz Ersen
86ce8f451d
Translated using Weblate (Turkish)
Currently translated at 100.0% (895 of 895 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (894 of 894 strings)

Co-authored-by: Oğuz Ersen <oguz@ersen.moe>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/tr/
Translation: Fedilab/Strings
2022-11-23 09:09:34 +01:00
Thomas
a389681b86 Fix issue #518 - Remove serif font 2022-11-23 09:09:28 +01:00
Thomas
a14bebc721 Release 3.7.5 2022-11-23 09:08:08 +01:00
Thomas
48c9f1e48c Release 3.7.5 2022-11-22 18:14:16 +01:00
Thomas
900e78f5e8 Fix issue #514 - Add about and privacy links into About the instance. 2022-11-22 17:14:00 +01:00
Thomas
9d4a576bc1 Fix automatically hide after x seconds 2022-11-22 16:42:05 +01:00
Thomas
a1d1058ff3 Merge remote-tracking branch 'origin/develop' into develop 2022-11-22 15:27:25 +01:00
Thomas
047086f5e5 Fix issue #510 - Add support for gemini links 2022-11-22 15:27:13 +01:00
Oğuz Ersen
969a35bac2
Translated using Weblate (Turkish)
Currently translated at 100.0% (891 of 891 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (890 of 890 strings)

Co-authored-by: Oğuz Ersen <oguz@ersen.moe>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/tr/
Translation: Fedilab/Strings
2022-11-22 15:04:13 +01:00
Xosé M
e8f4fe981a
Translated using Weblate (Galician)
Currently translated at 89.6% (799 of 891 strings)

Translated using Weblate (Galician)

Currently translated at 82.6% (736 of 890 strings)

Co-authored-by: Xosé M <correoxm@disroot.org>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/gl/
Translation: Fedilab/Strings
2022-11-22 15:04:13 +01:00
Thomas
c729b091e8 Fix issue #512 - Drafts removed when no changes after opening them 2022-11-22 15:04:04 +01:00
Thomas
5f1cd06566 Fix issue #513 - Add the ability to check blocked domains and to unblock them. 2022-11-22 14:50:21 +01:00
Thomas
2d80e89982 Merge branch 'fix_cache_notification' into develop 2022-11-22 13:44:44 +01:00
Thomas
28e10ddc82 Fix pagination from db 2022-11-22 12:32:16 +01:00
Thomas
0bcb4e0fca Fix #515 - set resolve to false when searching accounts 2022-11-22 11:03:43 +01:00
Thomas
1b8304d230 Fix #515 - set resolve to false when searching accounts 2022-11-22 09:44:58 +01:00
Thomas
f647c308a2 Fix a crash if app is killed 2022-11-22 09:41:10 +01:00
Thomas
59472e7f67 Fix db calls 2022-11-22 09:33:55 +01:00
Thomas
020d218df9 add suggestions 2022-11-21 19:06:03 +01:00
Thomas
0796ff7b69 Add classes and logic 2022-11-21 17:43:29 +01:00
Thomas
59a21008f1 Fix issue #497 - Filter view is not syncing after edition 2022-11-21 14:46:53 +01:00
Thomas
03f0ac4be3 Fix issue #498 - Keep searched word in search field 2022-11-21 14:23:54 +01:00
Thomas
43781d1618 Merge remote-tracking branch 'origin/develop' into develop 2022-11-21 14:09:27 +01:00
Thomas
e8ab16747d Fix issue #500 - Crash due to proxy - Disable DNS check 2022-11-21 14:09:10 +01:00
Cárlisson Galdino
ae3cca3a2d
Translated using Weblate (Portuguese)
Currently translated at 75.4% (670 of 888 strings)

Co-authored-by: Cárlisson Galdino <cordeis@vivaldi.net>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/pt/
Translation: Fedilab/Strings
2022-11-21 12:29:25 +01:00
ButterflyOfFire
bbf3ce3a79
Translated using Weblate (French)
Currently translated at 99.5% (884 of 888 strings)

Co-authored-by: ButterflyOfFire <ButterflyOfFire@protonmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/fr/
Translation: Fedilab/Strings
2022-11-21 12:29:24 +01:00
Thomas
495bc70ffb Fix a crash when writing an unsupported domain 2022-11-21 11:11:14 +01:00
Thomas
e8018a6560 Fix issue #501 - Disabling "attach media when sharing" not honored 2022-11-21 10:19:51 +01:00
Thomas
5160cd65c5 Fix issue #503 - Wrong URL displayed when long pressing a link that has been transformed 2022-11-21 09:55:42 +01:00
Thomas
9b45f57f87 Release 3.7.4 2022-11-20 18:11:04 +01:00
Thomas
e3c288fcac Theme issue for accounts in list 2022-11-20 17:41:21 +01:00
Thomas
6f72dd7c2d Merge remote-tracking branch 'origin/develop' into develop 2022-11-20 16:30:18 +01:00
Thomas
b38eb3bb09 Fix some color issues 2022-11-20 16:30:13 +01:00
Eric
91ca5216be
Translated using Weblate (Chinese (Simplified))
Currently translated at 69.0% (612 of 886 strings)

Co-authored-by: Eric <hamburger1024@mailbox.org>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/zh_Hans/
Translation: Fedilab/Strings
2022-11-20 16:00:09 +01:00
Oğuz Ersen
cd0b57c1b0
Translated using Weblate (Turkish)
Currently translated at 100.0% (888 of 888 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (886 of 886 strings)

Co-authored-by: Oğuz Ersen <oguz@ersen.moe>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/tr/
Translation: Fedilab/Strings
2022-11-20 16:00:09 +01:00
Ajeje Brazorf
2d29ddfe96
Translated using Weblate (Sardinian)
Currently translated at 99.3% (880 of 886 strings)

Co-authored-by: Ajeje Brazorf <lmelonimamo@yahoo.it>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/sc/
Translation: Fedilab/Strings
2022-11-20 16:00:09 +01:00
Thomas
99e3cdda1a Fix issue #488 - Copy content of a message doesn't work 2022-11-20 16:00:03 +01:00
Thomas
fa760ba959 Add the ability to timed-mute from profiles 2022-11-20 15:55:30 +01:00
Thomas
3f8f15256d Fix issue #496 - wrong label for new reports and user sign-up 2022-11-20 15:37:09 +01:00
Thomas
b334a5b655 Fix issue #486 - Empty images when sharing from another app. 2022-11-20 12:07:54 +01:00
Thomas
2f9b1e9bcf Avoid empty page 2022-11-20 11:39:49 +01:00
Thomas
d95db5b1b8 Fix issue #491 - Download and share media don't work for Android 10 2022-11-20 11:07:42 +01:00
Thomas
b70103526b Fix an issue with mentions 2022-11-20 10:55:36 +01:00
Thomas
731f8d97fb Merge remote-tracking branch 'origin/develop' into develop 2022-11-20 10:21:02 +01:00
Thomas
d76fcd0f7a Fix issue #485 - Add a delete icon on items + rename menu item 2022-11-20 10:20:52 +01:00
Oğuz Ersen
ac55d2945c
Translated using Weblate (Turkish)
Currently translated at 100.0% (885 of 885 strings)

Co-authored-by: Oğuz Ersen <oguz@ersen.moe>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/tr/
Translation: Fedilab/Strings
2022-11-20 10:06:30 +01:00
RintanBroadleaf
41ed974815
Translated using Weblate (Japanese)
Currently translated at 100.0% (885 of 885 strings)

Co-authored-by: RintanBroadleaf <rintanbroadleaf@outlook.jp>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/ja/
Translation: Fedilab/Strings
2022-11-20 10:06:30 +01:00
ButterflyOfFire
3d30588dae
Translated using Weblate (French)
Currently translated at 100.0% (885 of 885 strings)

Translated using Weblate (Arabic)

Currently translated at 83.2% (737 of 885 strings)

Co-authored-by: ButterflyOfFire <ButterflyOfFire@protonmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/ar/
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/fr/
Translation: Fedilab/Strings
2022-11-20 10:06:30 +01:00
Thomas
0e8354b0c1 Fix issue #478 - Tags cannot be followed when clicking on them. 2022-11-20 10:04:22 +01:00
Thomas
6d99ab479f Merge remote-tracking branch 'origin/develop' into develop 2022-11-20 09:45:10 +01:00
Thomas
3a3f6aa347 Fix issue #493 - Crash when adding account to a list from profile 2022-11-20 09:45:05 +01:00
Oğuz Ersen
c7c733ad9f
Translated using Weblate (Turkish)
Currently translated at 100.0% (883 of 883 strings)

Co-authored-by: Oğuz Ersen <oguz@ersen.moe>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/tr/
Translation: Fedilab/Strings
2022-11-19 18:14:34 +01:00
Ajeje Brazorf
403fdf4ce2
Translated using Weblate (Sardinian)
Currently translated at 99.2% (876 of 883 strings)

Co-authored-by: Ajeje Brazorf <lmelonimamo@yahoo.it>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/sc/
Translation: Fedilab/Strings
2022-11-19 18:14:34 +01:00
ButterflyOfFire
d713490d48
Translated using Weblate (Arabic)
Currently translated at 83.2% (735 of 883 strings)

Co-authored-by: ButterflyOfFire <ButterflyOfFire@protonmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/ar/
Translation: Fedilab/Strings
2022-11-19 18:14:34 +01:00
Bai
3117cfe33b
Translated using Weblate (Turkish)
Currently translated at 100.0% (882 of 882 strings)

Co-authored-by: Bai <batuhanakkurt000@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/tr/
Translation: Fedilab/Strings
2022-11-19 18:14:34 +01:00
Thomas
9d8c815a1a Release 3.7.3 2022-11-19 18:13:16 +01:00
Thomas
90ed830b33 Fix saving media 2022-11-19 17:59:26 +01:00
Thomas
aa4aefe9dc Merge branch 'check_profiles_remotely' into develop 2022-11-19 17:43:42 +01:00
Thomas
98c1a7e1b5 Comment #477 - Display remote media timeline 2022-11-19 17:43:27 +01:00
Thomas
4263d6f842 Comment #477 - Add button to display messages remotely 2022-11-19 17:17:23 +01:00
Thomas
fdc365dc00 Comment #477 - Allow remote actions 2022-11-19 16:50:55 +01:00
Thomas
a57659bd69 Comment #477 - Add logic for visiting remote profiles 2022-11-19 16:44:23 +01:00
Thomas
ad9048300c Merge pull request 'Use correct tense of "choose".' (#483) from trem/Fedilab:develop into develop
Reviewed-on: https://codeberg.org/tom79/Fedilab/pulls/483
2022-11-19 14:19:53 +00:00
trem
ef0137838a Use correct tense of "choose".
"Chose" is the past tense, whereas in such descriptions, one uses the present tense (in the infinitive form), which is "choose".
2022-11-19 14:09:18 +00:00
Thomas
86c332a326 put values 2022-11-19 12:08:31 +01:00
Thomas
9298f22b5d Fix issue #482 - Warn when the app fails to add account into a list 2022-11-19 11:58:58 +01:00
Thomas
26bc658bee More fixes 2022-11-19 11:44:36 +01:00
Thomas
e9b465d9f4 Fix patterns 2022-11-19 11:39:16 +01:00
Thomas
d06f7b4cde Fix issue #464 - More restrictive pattern 2022-11-19 11:36:50 +01:00
Thomas
65b353da5c Merge remote-tracking branch 'origin/develop' into develop 2022-11-19 11:27:09 +01:00
Thomas
b3e2af6e3c fix media not stored 2022-11-19 11:26:54 +01:00
ButterflyOfFire
e1ddb2cd7c
Translated using Weblate (English)
Currently translated at 100.0% (882 of 882 strings)

Co-authored-by: ButterflyOfFire <ButterflyOfFire@protonmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/en/
Translation: Fedilab/Strings
2022-11-19 08:27:19 +01:00
Oğuz Ersen
967d9b0a72
Translated using Weblate (Turkish)
Currently translated at 100.0% (882 of 882 strings)

Co-authored-by: Oğuz Ersen <oguz@ersen.moe>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/tr/
Translation: Fedilab/Strings
2022-11-19 08:24:48 +01:00
ButterflyOfFire
b5cc8b4b26
Translated using Weblate (French)
Currently translated at 98.7% (871 of 882 strings)

Co-authored-by: ButterflyOfFire <ButterflyOfFire@protonmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/fr/
Translation: Fedilab/Strings
2022-11-19 08:24:48 +01:00
Oğuz Ersen
5d68c223ad
Translated using Weblate (Turkish)
Currently translated at 100.0% (869 of 869 strings)

Co-authored-by: Oğuz Ersen <oguz@ersen.moe>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/tr/
Translation: Fedilab/Strings
2022-11-18 19:18:05 +01:00
Ajeje Brazorf
0b1b913120
Translated using Weblate (Sardinian)
Currently translated at 99.1% (862 of 869 strings)

Co-authored-by: Ajeje Brazorf <lmelonimamo@yahoo.it>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/sc/
Translation: Fedilab/Strings
2022-11-18 19:18:05 +01:00
Thomas
f2a4fdb2db Release 3.7.2 2022-11-18 19:17:58 +01:00
Thomas
7878b51907 Comment #467 #464 - extends warns to notifications 2022-11-18 19:07:48 +01:00
Thomas
b584a37281 Comment #467 #464 - hide or remove message in timelines 2022-11-18 17:40:25 +01:00
Thomas
58c3a5fc35 Comment #467 #464 - apply theme on buttons 2022-11-18 16:05:31 +01:00
Thomas
b09e21a589 Comment #467 #464 - fix api calls 2022-11-18 15:52:25 +01:00
Thomas
d886693c75 Comment #467 #464 - fix some theme colors / bugs 2022-11-17 19:16:14 +01:00
Thomas
536af42e27 Comment #467 #464 - fix some theme colors / bugs 2022-11-17 19:03:36 +01:00
Thomas
74599d8a7f Comment #467 #464 - Support API endpoints 2022-11-17 18:43:48 +01:00
Thomas
8bf21db632 Comment #467 #464 - Start migration to api/v2 for filters 2022-11-17 17:27:25 +01:00
Thomas
9dc4b46fdb Order notification by date 2022-11-17 14:06:48 +01:00
Thomas
a946ea49d1 Fix issue #469 - Crashes with pinned and Pixelfed 2022-11-17 13:43:18 +01:00
Thomas
ba53e174ab Fix issue #471 - Takes value from reblog if not null 2022-11-17 13:32:19 +01:00
Thomas
47dfc3f33c Merge remote-tracking branch 'origin/develop' into develop 2022-11-17 12:00:58 +01:00
Thomas
c72b888078 Fix issue #471 - Fix counter issue for fab/boost with remote instances 2022-11-17 12:00:54 +01:00
Oğuz Ersen
871ba79200
Translated using Weblate (Turkish)
Currently translated at 100.0% (866 of 866 strings)

Co-authored-by: Oğuz Ersen <oguz@ersen.moe>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/tr/
Translation: Fedilab/Strings
2022-11-17 11:42:24 +01:00
Ajeje Brazorf
8b21ff4b18
Translated using Weblate (Sardinian)
Currently translated at 99.4% (861 of 866 strings)

Co-authored-by: Ajeje Brazorf <lmelonimamo@yahoo.it>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/sc/
Translation: Fedilab/Strings
2022-11-17 11:42:24 +01:00
Jipem
6c75032eda
Translated using Weblate (French)
Currently translated at 98.4% (853 of 866 strings)

Co-authored-by: Jipem <web+weblate@jipem.me>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/fr/
Translation: Fedilab/Strings
2022-11-17 11:42:24 +01:00
Thomas
4b106ffba4 Fix issue #475 - Support notifications when edited 2022-11-17 11:42:06 +01:00
Thomas
2593d08d20 Fix issue #472 - Allow to browse Mastodon instances before registering 2022-11-17 10:55:01 +01:00
Thomas
5b164d60da Fix issue #473 - Transparent background 2022-11-17 09:37:49 +01:00
Thomas
96a0d5e485 Fix issue #473 - Transparent background 2022-11-17 09:04:46 +01:00
Thomas
a6e5254043 Fix issue #474 - Give a fixed width to instance logo 2022-11-17 08:57:18 +01:00
Thomas
d283340518 Release 3.7.1 2022-11-16 17:58:18 +01:00
Thomas
aa768127ea Merge remote-tracking branch 'origin/develop' into develop 2022-11-16 17:42:09 +01:00
Thomas
962a59ac4f Fix issue #449 - Fix draft issues 2022-11-16 17:41:47 +01:00
Oğuz Ersen
14286d7049
Translated using Weblate (Turkish)
Currently translated at 100.0% (864 of 864 strings)

Co-authored-by: Oğuz Ersen <oguz@ersen.moe>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/tr/
Translation: Fedilab/Strings
2022-11-16 17:29:50 +01:00
Ajeje Brazorf
7f4e4d1303
Translated using Weblate (Sardinian)
Currently translated at 99.4% (859 of 864 strings)

Co-authored-by: Ajeje Brazorf <lmelonimamo@yahoo.it>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/sc/
Translation: Fedilab/Strings
2022-11-16 17:29:50 +01:00
Thomas
869b4d25e2 Fix issue #460 - Specific messages when a feature is not supported. 2022-11-16 17:29:38 +01:00
Thomas
9a64dd60b0 Open link with the app. 2022-11-16 17:22:26 +01:00
Thomas
2fa46290a1 Fix issue #442 - Tags not filtered 2022-11-16 15:49:20 +01:00
Thomas
1dae37549b Fix issue #463 - Add reply counter when counters are enabled 2022-11-16 14:39:58 +01:00
Thomas
7500f1cd7b Fix issue #459 - Remove offset for compose view 2022-11-16 14:36:20 +01:00
Thomas
23cecbf3c4 Reset notification marker when clearing cache 2022-11-16 14:24:29 +01:00
Thomas
bf0c44e905 fix typo 2022-11-16 12:10:42 +01:00
Thomas
3bca0e6864 fix typo 2022-11-16 12:05:01 +01:00
Thomas
ada128bd2b add open collective 2022-11-16 12:01:31 +01:00
Thomas
da64af1e12 Add issue template 2022-11-16 11:53:37 +01:00
Thomas
7dc9248543 Release 3.7.0 2022-11-15 18:27:17 +01:00
Thomas
ac4a2dccfd Update release notes 2022-11-15 18:21:18 +01:00
Thomas
aa8e0f13bc Fix issue #403 - Freezes 2022-11-15 18:11:22 +01:00
Thomas
393d2990f0 Fix issue #403 - Freezes 2022-11-15 18:02:49 +01:00
Thomas
fe10411618 typo 2022-11-15 16:25:33 +01:00
Thomas
9b2307e2dc Prepare release notes 2022-11-15 16:24:50 +01:00
Thomas
5e1dffa65e Some fixes 2022-11-15 16:11:36 +01:00
Thomas
f1d26f9de7 Merge remote-tracking branch 'origin/develop' into develop 2022-11-15 15:44:17 +01:00
Thomas
81c012c8f0 Language selector 2022-11-15 15:43:53 +01:00
Thomas
197a8d56e1 New layout 2022-11-15 15:04:24 +01:00
Hosted Weblate
0fb495e1cf
Update translation files
Updated by "Remove blank strings" hook in Weblate.

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/
Translation: Fedilab/Strings
2022-11-15 14:25:33 +01:00
Oğuz Ersen
82b33f4d5c
Translated using Weblate (Turkish)
Currently translated at 100.0% (854 of 854 strings)

Co-authored-by: Oğuz Ersen <oguz@ersen.moe>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/tr/
Translation: Fedilab/Strings
2022-11-15 14:25:33 +01:00
Ajeje Brazorf
4fe37dc23d
Translated using Weblate (Sardinian)
Currently translated at 99.5% (850 of 854 strings)

Co-authored-by: Ajeje Brazorf <lmelonimamo@yahoo.it>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/sc/
Translation: Fedilab/Strings
2022-11-15 14:25:33 +01:00
Zekovski
806cbbcb4d
Translated using Weblate (French)
Currently translated at 97.0% (829 of 854 strings)

Co-authored-by: Zekovski <zekovski@e.email>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/fr/
Translation: Fedilab/Strings
2022-11-15 14:25:33 +01:00
Thomas
ae71b2ba71 Fix issue #457 - Update list name and sync them. 2022-11-15 14:25:25 +01:00
Thomas
cdbfb17d94 Follow Hashtags 2022-11-15 10:51:46 +01:00
Thomas
170dbbd0cf Add endpoints 2022-11-15 09:00:08 +01:00
Thomas
510ba7ba47 Fix issue #418 - Define favorite languages in settings to reduce the list when composing 2022-11-14 18:47:14 +01:00
Thomas
cdaba2f34d Some fixes 2022-11-14 17:18:55 +01:00
Thomas
03f8c33cb2 Fix issue #445 - Timelines not reloaded when applying filters 2022-11-14 17:18:40 +01:00
Thomas
5b9534adc2 Fix some color issues 2022-11-14 15:15:29 +01:00
Thomas
e7dd79d8e0 Ask restart when theme is changed 2022-11-14 12:15:43 +01:00
Thomas
e9b62b71d1 Ask restart when theme is changed 2022-11-14 10:37:53 +01:00
Thomas
f66fda968e Improve cache clear 2022-11-14 10:08:18 +01:00
Thomas
13f180e6f3 change error message 2022-11-14 09:08:02 +01:00
Thomas
f851be8ab9 Beta release 3.6.5 2022-11-13 18:41:45 +01:00
Thomas
49333097cc Fix some issue with admins 2022-11-13 18:34:08 +01:00
Thomas
966b0bce43 Fix an issue #450 2022-11-13 18:33:34 +01:00
Thomas
f78184216d Fix an issue when following/unfollowing in a list of accounts 2022-11-13 16:07:21 +01:00
Thomas
89851a68bc Fix issue #419 - Add theme 2022-11-13 12:16:58 +01:00
Thomas
aa6f87facb Fix issue with icon when following 2022-11-13 12:14:13 +01:00
Thomas
2fc7eada97 Merge remote-tracking branch 'origin/develop' into develop 2022-11-13 11:26:43 +01:00
Thomas
e51764ae42 Fix issue #442 - mentions starting with a "(" 2022-11-13 11:26:26 +01:00
Oğuz Ersen
7ac2edf157
Translated using Weblate (Turkish)
Currently translated at 100.0% (848 of 848 strings)

Co-authored-by: Oğuz Ersen <oguz@ersen.moe>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/tr/
Translation: Fedilab/Strings
2022-11-13 10:56:03 +01:00
Cilian
16cad99fab
Translated using Weblate (Russian)
Currently translated at 68.2% (579 of 848 strings)

Co-authored-by: Cilian <arusivv@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/ru/
Translation: Fedilab/Strings
2022-11-13 10:56:03 +01:00
RintanBroadleaf
406e0d19c5
Translated using Weblate (Japanese)
Currently translated at 100.0% (848 of 848 strings)

Co-authored-by: RintanBroadleaf <rintanbroadleaf@outlook.jp>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/ja/
Translation: Fedilab/Strings
2022-11-13 10:56:03 +01:00
Zekovski
3aa310fafb
Translated using Weblate (French)
Currently translated at 100.0% (819 of 819 strings)

Co-authored-by: Zekovski <zekovski@e.email>
Translate-URL: https://hosted.weblate.org/projects/fedilab/strings/fr/
Translation: Fedilab/Strings
2022-11-13 10:56:03 +01:00
Thomas
dd4d0abf12 Fix issue #446 - Add theme 2022-11-13 10:55:25 +01:00
Thomas
435c34604b Fix issue #448 - Issue with regex for whole word 2022-11-13 10:49:04 +01:00
Thomas
6063246b96 apply changes 2022-11-12 18:57:07 +01:00
Thomas
144dddcc0f Some fixes with admin 2022-11-12 18:27:37 +01:00
Thomas
e110dc71ff Some fixes with admin 2022-11-12 18:02:55 +01:00
Thomas
f213b0ddcb Display reports 2022-11-12 17:30:06 +01:00
Thomas
2d87254ac0 Add reports 2022-11-12 17:14:58 +01:00
Thomas
d7f58dab36 Add reports 2022-11-12 12:15:16 +01:00
Thomas
9a79b9dc7d Merge branch 'develop' into improve_admin 2022-11-12 10:29:32 +01:00
Thomas
39c49fad77 Fix crash with trends 2022-11-12 09:49:48 +01:00
Thomas
e9fd84d7eb Release 3.6.4 2022-11-11 16:59:16 +01:00
Thomas
74b69b60e8 some change with moderation 2022-11-11 16:42:22 +01:00
Thomas
3dd06cb1a1 Merge branch 'develop' into improve_admin 2022-11-11 16:36:11 +01:00
Thomas
1da9fc96b3 some change with moderation 2022-11-11 16:36:00 +01:00
Thomas
90a99cc1c8 Fix issue #436 - Improve tags search 2022-11-11 11:52:57 +01:00
Thomas
d18dce9919 Fix issue #430 - Update json payload 2022-11-11 11:07:05 +01:00
Thomas
0c27c839d3 Fix issue #430 - Store instance 2022-11-11 10:50:03 +01:00
Thomas
f71a73abc8 Fix issue #430 - Encoding URL does not allow to login 2022-11-11 10:45:33 +01:00
Thomas
a4ade2a0f0 Fix issue #429 2022-11-10 17:25:48 +01:00
1684 changed files with 82761 additions and 23823 deletions

View file

@ -0,0 +1,41 @@
---
name: "Bug"
about: "Something isn't working"
labels:
- Bug
---
<!-- Please, describe the issue here -->
## # Steps for reproducing the issue
<!-- Step, to reproduce it -->
---
<!-- The instance you are using -->
Instance:
<!-- Your social network -->
<!-- Put a x between brackets like: - [x] Mastodon -->
- [ ] Mastodon
- [ ] Pleroma
- [ ] Friendica
- [ ] Pixelfed
<!-- If you know the version of Fedilab that you are using (can be found in about page) -->
Version of Fedilab:
<!-- Your Android version -->
Android version:
<!-- If you read our contributing advice -->
[ ] - I read
the [contributing page](https://codeberg.org/tom79/Fedilab/src/branch/main/CONTRIBUTING.md)

View file

@ -0,0 +1,26 @@
---
name: "Feature"
about: "A new feature or an enhancement to an existing feature"
labels:
- Feature
---
## # Describe the improvement
<!-- Your social network -->
<!-- Put a x between brackets like: - [x] Mastodon -->
- [ ] Mastodon
- [ ] Pleroma
- [ ] Friendica
- [ ] Pixelfed
<!-- Describe the improvement here -->
<!-- If you read our contributing advice -->
[ ] - I read
the [contributing page](https://codeberg.org/tom79/Fedilab/src/branch/main/CONTRIBUTING.md)

View file

@ -9,3 +9,13 @@ tools for helping in translations. New translations will be automatically merged
If you're submiting a merge request and your work adds new strings to the app, make sure they only If you're submiting a merge request and your work adds new strings to the app, make sure they only
exist in the default strings.xml file (res/values/strings.xml). If you add or modify strings of exist in the default strings.xml file (res/values/strings.xml). If you add or modify strings of
other languages, it will interfere with weblate's translations. other languages, it will interfere with weblate's translations.
### Issues and Reports:
Before creating an issue please take a moment and search the repository issues to avoid duplicates.
For bug reports, please provide as much details as possible to better debug the problem. The
important part is how to reproduce the bug and steps to reproduce it.
### Pull Requests
Please target the develop branch and not the main branch.

View file

@ -7,6 +7,7 @@
[<img alt="Donate using Liberapay" src="https://img.shields.io/liberapay/patrons/tom79.svg?logo=liberapay"/>](https://liberapay.com/tom79/donate) [<img alt="Donate using Liberapay" src="https://img.shields.io/liberapay/patrons/tom79.svg?logo=liberapay"/>](https://liberapay.com/tom79/donate)
## Download ## Download
[<img alt='Get it on Google Play' src='./images/get-it-on-play.png' height="80"/>](https://play.google.com/store/apps/details?id=app.fedilab.android) [<img alt='Get it on Google Play' src='./images/get-it-on-play.png' height="80"/>](https://play.google.com/store/apps/details?id=app.fedilab.android)
@ -21,4 +22,3 @@
Lead developer: [toot.fedilab.app/@apps](https://toot.fedilab.app/@apps) Lead developer: [toot.fedilab.app/@apps](https://toot.fedilab.app/@apps)

View file

@ -8,13 +8,13 @@ plugins {
} }
def flavor def flavor
android { android {
compileSdk 31 compileSdk 33
defaultConfig { defaultConfig {
minSdk 21 minSdk 21
targetSdk 31 targetSdk 33
versionCode 424 versionCode 477
versionName "3.6.3" versionName "3.17.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
} }
flavorDimensions "default" flavorDimensions "default"
@ -63,6 +63,21 @@ android {
java.srcDirs = ['src/main/java', 'src/fdroid/java'] java.srcDirs = ['src/main/java', 'src/fdroid/java']
res.srcDirs = ['src/main/res', 'src/fdroid/res'] res.srcDirs = ['src/main/res', 'src/fdroid/res']
} }
main {
res.srcDirs = [
'src/main/res/layouts/mastodon',
'src/main/res/layouts/peertube',
'src/main/res/layouts',
'src/main/layout',
'src/main/res/drawables/mastodon',
'src/main/res/drawables/peertube',
'src/main/res/drawables',
'src/main/res/menus/mastodon',
'src/main/res/menus/peertube',
'src/main/res/menus',
'src/main/res'
]
}
} }
configurations { configurations {
all { all {
@ -78,8 +93,12 @@ allprojects {
} }
dependencies { dependencies {
implementation project(':autoimageslider') implementation project(':autoimageslider')
implementation 'androidx.appcompat:appcompat:1.4.2' implementation 'androidx.appcompat:appcompat:1.6.0'
implementation 'com.google.android.material:material:1.6.1'
implementation 'com.google.android.material:material:1.7.0'
implementation 'com.jaredrummler:colorpicker:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4' implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation "com.google.code.gson:gson:2.9.1" implementation "com.google.code.gson:gson:2.9.1"
implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'com.squareup.retrofit2:retrofit:2.9.0'
@ -88,26 +107,28 @@ dependencies {
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0' implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
implementation 'androidx.preference:preference:1.2.0' implementation 'androidx.preference:preference:1.2.0'
implementation "org.conscrypt:conscrypt-android:2.5.2" implementation "org.conscrypt:conscrypt-android:2.5.2"
implementation 'com.github.evozi:Cyanea:1.0.7'
implementation 'com.vanniktech:emoji-one:0.6.0' implementation 'com.vanniktech:emoji-one:0.6.0'
implementation 'com.github.GrenderG:Toasty:1.5.2' implementation 'com.github.GrenderG:Toasty:1.5.2'
implementation 'org.framagit.tom79:SparkButton:1.0.13' implementation "com.github.bumptech.glide:glide:4.14.2"
implementation "com.github.bumptech.glide:glide:4.12.0" implementation "com.github.bumptech.glide:okhttp3-integration:4.14.2"
implementation "com.github.bumptech.glide:okhttp3-integration:4.12.0" implementation("com.github.bumptech.glide:recyclerview-integration:4.14.2") {
// Excludes the support library because it's already included by Glide.
transitive = false
}
implementation "org.jsoup:jsoup:1.15.1" implementation "org.jsoup:jsoup:1.15.1"
implementation 'com.github.mergehez:ArgPlayer:v3.1' implementation 'com.github.mergehez:ArgPlayer:v3.1'
implementation project(path: ':mytransl') implementation project(path: ':mytransl')
implementation project(path: ':ratethisapp') implementation project(path: ':ratethisapp')
implementation project(path: ':sparkbutton')
implementation 'com.burhanrashid52:photoeditor:1.5.1' implementation 'com.burhanrashid52:photoeditor:1.5.1'
implementation project(path: ':cropper') implementation("com.vanniktech:android-image-cropper:4.3.3")
implementation project(path: ':mathjaxandroid')
annotationProcessor "com.github.bumptech.glide:compiler:4.12.0" annotationProcessor "com.github.bumptech.glide:compiler:4.12.0"
implementation 'jp.wasabeef:glide-transformations:4.3.0' implementation 'jp.wasabeef:glide-transformations:4.3.0'
implementation 'com.github.penfeizhou.android.animation:glide-plugin:2.22.0' implementation 'com.github.penfeizhou.android.animation:glide-plugin:2.23.0'
implementation 'com.google.android.exoplayer:exoplayer:2.18.1' implementation 'com.google.android.exoplayer:exoplayer:2.18.1'
implementation "androidx.viewpager2:viewpager2:1.0.0" implementation "androidx.viewpager2:viewpager2:1.0.0"
implementation 'com.github.piasy:rxandroidaudio:1.7.0' implementation 'com.github.piasy:rxandroidaudio:1.7.0'
@ -118,7 +139,6 @@ dependencies {
implementation "ch.acra:acra-mail:5.9.6" implementation "ch.acra:acra-mail:5.9.6"
implementation "ch.acra:acra-limiter:5.9.3" implementation "ch.acra:acra-limiter:5.9.3"
implementation "ch.acra:acra-dialog:5.9.6" implementation "ch.acra:acra-dialog:5.9.6"
implementation "com.madgag.spongycastle:bctls-jdk15on:1.58.0.0" implementation "com.madgag.spongycastle:bctls-jdk15on:1.58.0.0"
implementation 'com.github.UnifiedPush:android-connector:2.0.1' implementation 'com.github.UnifiedPush:android-connector:2.0.1'
// implementation 'com.github.UnifiedPush:android-foss_embedded_fcm_distributor:1.0.0-beta1' // implementation 'com.github.UnifiedPush:android-foss_embedded_fcm_distributor:1.0.0-beta1'
@ -132,14 +152,56 @@ dependencies {
implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0' implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0'
implementation 'androidx.lifecycle:lifecycle-livedata:2.5.1' implementation 'androidx.lifecycle:lifecycle-livedata:2.5.1'
implementation 'androidx.lifecycle:lifecycle-viewmodel:2.5.1' implementation 'androidx.lifecycle:lifecycle-viewmodel:2.5.1'
implementation 'androidx.navigation:navigation-fragment:2.5.1' implementation 'androidx.navigation:navigation-fragment:2.5.3'
implementation 'androidx.navigation:navigation-ui:2.5.1' implementation 'androidx.navigation:navigation-ui:2.5.3'
testImplementation 'junit:junit:' testImplementation 'junit:junit:'
androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test.ext:junit:1.1.4'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.0'
// debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.8.1' // debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.8.1'
implementation 'com.r0adkll:slidableactivity:2.1.0'
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
implementation 'androidx.vectordrawable:vectordrawable:1.1.0'
implementation "androidx.fragment:fragment:1.5.5"
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
implementation 'androidx.browser:browser:1.4.0'
implementation 'androidx.documentfile:documentfile:1.0.1'
implementation 'com.github.amoskorir:avatarimagegenerator:1.5.0'
implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.1.0'
implementation 'com.google.android.exoplayer:extension-mediasession:2.18.1'
implementation "com.github.mabbas007:TagsEditText:1.0.5"
implementation "net.gotev:uploadservice:4.7.0"
implementation "net.gotev:uploadservice-okhttp:4.7.0"
implementation 'androidx.media:media:1.6.0'
implementation 'com.github.mancj:MaterialSearchBar:0.8.5'
implementation 'com.github.vkay94:DoubleTapPlayerView:1.0.0'
//************ CAST **************///
//---> Google libs (google_full)
playstoreImplementation "com.google.android.gms:play-services-cast-tv:19.0.1"
playstoreImplementation "com.google.android.gms:play-services-cast:21.0.1"
playstoreImplementation "androidx.mediarouter:mediarouter:1.3.0"
playstoreImplementation 'com.google.android.gms:play-services-cast-framework:21.0.1'
playstoreImplementation "com.google.android.gms:play-services-cast-tv:19.0.1"
playstoreImplementation "com.google.android.gms:play-services-cast:21.0.1"
playstoreImplementation "androidx.mediarouter:mediarouter:1.3.0"
playstoreImplementation 'com.google.android.gms:play-services-cast-framework:21.0.1'
//----> Other flavors
fdroidImplementation 'su.litvak.chromecast:api-v2:0.11.3'
fdroidImplementation 'com.fasterxml.jackson.core:jackson-core:2.12.0'
fdroidImplementation 'org.slf4j:slf4j-simple:1.7.30'
fdroidImplementation 'com.github.evozi:Cyanea:1.0.7'
fdroidImplementation 'su.litvak.chromecast:api-v2:0.11.3'
fdroidImplementation 'com.fasterxml.jackson.core:jackson-core:2.12.0'
fdroidImplementation 'org.slf4j:slf4j-simple:1.7.30'
} }
def getCurrentFlavor() { def getCurrentFlavor() {
Gradle gradle = getGradle() Gradle gradle = getGradle()

View file

@ -1,15 +1,15 @@
package app.fedilab.android; package app.fedilab.android;
import static org.junit.Assert.assertEquals;
import android.content.Context; import android.content.Context;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.platform.app.InstrumentationRegistry;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/** /**
* Instrumented test, which will execute on an Android device. * Instrumented test, which will execute on an Android device.
* *

View file

@ -0,0 +1,19 @@
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<shortcut
android:shortcutId="compose"
android:enabled="true"
android:icon="@drawable/ic_baseline_add_comment_24"
android:shortcutShortLabel="@string/compose_shortcut_short_label1"
tools:targetApi="n_mr1">
<intent
android:action="app.fedilab.android.shorcut.compose"
android:targetClass="app.fedilab.android.activities.MainActivity"
android:targetPackage="fr.gouv.etalab.mastodon.debug" />
<categories android:name="android.shortcut.conversation" />
<capability-binding android:key="actions.intent.CREATE_MESSAGE" />
</shortcut>
</shortcuts>

View file

@ -0,0 +1,211 @@
package app.fedilab.android.activities;
/* Copyright 2023 Thomas Schneider
*
* This file is a part of Fedilab
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */
import static app.fedilab.android.peertube.helper.Helper.CAST_ID;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.webkit.MimeTypeMap;
import androidx.appcompat.app.AlertDialog;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
import java.security.GeneralSecurityException;
import app.fedilab.android.R;
import app.fedilab.android.databinding.ActivityPeertubeBinding;
import app.fedilab.android.mastodon.activities.BaseBarActivity;
import app.fedilab.android.peertube.client.data.VideoData;
import app.fedilab.android.peertube.helper.Helper;
import su.litvak.chromecast.api.v2.ChromeCast;
import su.litvak.chromecast.api.v2.MediaStatus;
import su.litvak.chromecast.api.v2.Status;
public class BasePeertubeActivity extends BaseBarActivity {
protected ActivityPeertubeBinding binding;
protected VideoData.Video peertube;
protected ExoPlayer player;
protected String videoURL;
protected String subtitlesStr;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityPeertubeBinding.inflate(getLayoutInflater());
View view = binding.getRoot();
setContentView(view);
binding.minController.castPlay.setOnClickListener(v -> {
binding.minController.castLoader.setVisibility(View.VISIBLE);
if (PeertubeBaseMainActivity.chromeCast != null) {
new Thread(() -> {
try {
int icon = -1;
if (PeertubeBaseMainActivity.chromeCast.getMediaStatus().playerState == MediaStatus.PlayerState.PLAYING) {
PeertubeBaseMainActivity.chromeCast.pause();
icon = R.drawable.ic_baseline_play_arrow_32;
} else if (PeertubeBaseMainActivity.chromeCast.getMediaStatus().playerState == MediaStatus.PlayerState.PAUSED) {
PeertubeBaseMainActivity.chromeCast.play();
icon = R.drawable.ic_baseline_pause_32;
}
if (icon != -1) {
Handler mainHandler = new Handler(Looper.getMainLooper());
int finalIcon = icon;
Runnable myRunnable = () -> binding.minController.castPlay.setImageResource(finalIcon);
mainHandler.post(myRunnable);
}
Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> binding.minController.castLoader.setVisibility(View.GONE);
mainHandler.post(myRunnable);
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
});
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.action_cast) {
if (PeertubeBaseMainActivity.chromeCasts != null && PeertubeBaseMainActivity.chromeCasts.size() > 0) {
String[] chromecast_choice = new String[PeertubeBaseMainActivity.chromeCasts.size()];
AlertDialog.Builder alt_bld = new MaterialAlertDialogBuilder(this);
alt_bld.setTitle(R.string.chromecast_choice);
int i = 0;
for (ChromeCast cc : PeertubeBaseMainActivity.chromeCasts) {
chromecast_choice[i] = cc.getTitle();
i++;
}
i = 0;
for (ChromeCast cc : PeertubeBaseMainActivity.chromeCasts) {
if (PeertubeBaseMainActivity.chromecastActivated && cc.isConnected()) {
break;
}
i++;
}
alt_bld.setSingleChoiceItems(chromecast_choice, i, (dialog, position) -> {
PeertubeBaseMainActivity.chromeCast = PeertubeBaseMainActivity.chromeCasts.get(position);
new Thread(() -> {
if (PeertubeBaseMainActivity.chromeCast != null) {
Intent intentBC = new Intent(Helper.RECEIVE_CAST_SETTINGS);
Bundle b = new Bundle();
if (PeertubeBaseMainActivity.chromecastActivated) {
b.putInt("displayed", 0);
intentBC.putExtras(b);
LocalBroadcastManager.getInstance(BasePeertubeActivity.this).sendBroadcast(intentBC);
Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> {
binding.doubleTapPlayerView.setVisibility(View.VISIBLE);
binding.minController.castMiniController.setVisibility(View.GONE);
};
mainHandler.post(myRunnable);
} else {
b.putInt("displayed", 1);
b.putSerializable("castedTube", peertube);
intentBC.putExtras(b);
LocalBroadcastManager.getInstance(BasePeertubeActivity.this).sendBroadcast(intentBC);
try {
Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> {
invalidateOptionsMenu();
binding.minController.castLoader.setVisibility(View.VISIBLE);
player.setPlayWhenReady(false);
binding.doubleTapPlayerView.setVisibility(View.GONE);
binding.minController.castMiniController.setVisibility(View.VISIBLE);
dialog.dismiss();
if (videoURL != null) {
if (player != null && player.getCurrentPosition() > 0) {
videoURL += "?start=" + (player.getCurrentPosition() / 1000);
}
}
};
mainHandler.post(myRunnable);
if (!PeertubeBaseMainActivity.chromeCast.isConnected()) {
PeertubeBaseMainActivity.chromeCast.connect();
}
myRunnable = this::invalidateOptionsMenu;
mainHandler.post(myRunnable);
Status status = PeertubeBaseMainActivity.chromeCast.getStatus();
if (PeertubeBaseMainActivity.chromeCast.isAppAvailable(CAST_ID) && !status.isAppRunning(CAST_ID)) {
PeertubeBaseMainActivity.chromeCast.launchApp(CAST_ID);
}
if (videoURL != null) {
String mime = MimeTypeMap.getFileExtensionFromUrl(videoURL);
PeertubeBaseMainActivity.chromeCast.setRequestTimeout(60000);
PeertubeBaseMainActivity.chromeCast.load(peertube.getTitle(), null, videoURL, mime);
PeertubeBaseMainActivity.chromeCast.play();
binding.minController.castPlay.setImageResource(R.drawable.ic_baseline_pause_32);
}
myRunnable = () -> binding.minController.castLoader.setVisibility(View.GONE);
mainHandler.post(myRunnable);
} catch (IOException | GeneralSecurityException e) {
e.printStackTrace();
}
}
Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> {
invalidateOptionsMenu();
dialog.dismiss();
};
mainHandler.post(myRunnable);
}
}).start();
});
alt_bld.setPositiveButton(R.string.close, (dialog, id) -> dialog.dismiss());
AlertDialog alert = alt_bld.create();
alert.show();
}
}
return super.onOptionsItemSelected(item);
}
@Override
public boolean onCreateOptionsMenu(@NotNull Menu menu) {
getMenuInflater().inflate(R.menu.video_menu, menu);
MenuItem castItem = menu.findItem(R.id.action_cast);
if (PeertubeBaseMainActivity.chromeCasts != null && PeertubeBaseMainActivity.chromeCasts.size() > 0) {
castItem.setVisible(true);
if (PeertubeBaseMainActivity.chromeCast != null && PeertubeBaseMainActivity.chromeCast.isConnected()) {
castItem.setIcon(R.drawable.ic_baseline_cast_connected_24);
} else {
castItem.setIcon(R.drawable.ic_baseline_cast_24);
}
}
return true;
}
}

View file

@ -0,0 +1,263 @@
package app.fedilab.android.activities;
/* Copyright 2023 Thomas Schneider
*
* This file is a part of Fedilab
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.View;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import java.io.IOException;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import app.fedilab.android.R;
import app.fedilab.android.databinding.ActivityMainPeertubeBinding;
import app.fedilab.android.mastodon.activities.BaseActivity;
import app.fedilab.android.peertube.client.data.VideoData;
import app.fedilab.android.peertube.helper.Helper;
import su.litvak.chromecast.api.v2.ChromeCast;
import su.litvak.chromecast.api.v2.ChromeCasts;
import su.litvak.chromecast.api.v2.ChromeCastsListener;
import su.litvak.chromecast.api.v2.MediaStatus;
public abstract class PeertubeBaseMainActivity extends BaseActivity implements ChromeCastsListener {
public static List<ChromeCast> chromeCasts;
public static ChromeCast chromeCast;
public static boolean chromecastActivated = false;
protected ActivityMainPeertubeBinding binding;
private BroadcastReceiver manage_chromecast;
private VideoData.Video castedTube;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainPeertubeBinding.inflate(getLayoutInflater());
View view = binding.getRoot();
setContentView(view);
ChromeCastsListener chromeCastsListener = this;
ChromeCasts.registerListener(chromeCastsListener);
binding.castClose.setOnClickListener(v -> {
Intent intentBC = new Intent(Helper.RECEIVE_CAST_SETTINGS);
Bundle b = new Bundle();
b.putInt("displayed", 0);
intentBC.putExtras(b);
LocalBroadcastManager.getInstance(PeertubeBaseMainActivity.this).sendBroadcast(intentBC);
});
binding.castTogglePlay.setOnClickListener(v -> {
if (chromeCast != null) {
new Thread(() -> {
try {
Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> binding.castTogglePlay.setVisibility(View.GONE);
mainHandler.post(myRunnable);
int icon = -1;
if (chromeCast.getMediaStatus().playerState == MediaStatus.PlayerState.PLAYING) {
chromeCast.pause();
icon = R.drawable.ic_baseline_play_arrow_32;
} else if (chromeCast.getMediaStatus().playerState == MediaStatus.PlayerState.PAUSED) {
chromeCast.play();
icon = R.drawable.ic_baseline_pause_32;
}
if (icon != -1) {
int finalIcon = icon;
myRunnable = () -> binding.castTogglePlay.setImageResource(finalIcon);
mainHandler.post(myRunnable);
}
myRunnable = () -> binding.castTogglePlay.setVisibility(View.VISIBLE);
mainHandler.post(myRunnable);
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
});
manage_chromecast = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Bundle b = intent.getExtras();
assert b != null;
int state = b.getInt("state_asked", -1);
int displayed = b.getInt("displayed", -1);
castedTube = (VideoData.Video) b.getSerializable("castedTube");
if (state == 1) {
discoverCast();
} else if (state == 0) {
new Thread(() -> {
try {
if (chromeCast != null) {
chromeCast.stopApp();
chromeCast.disconnect();
}
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
if (displayed == 1) {
chromecastActivated = true;
if (castedTube != null) {
binding.castInfo.setVisibility(View.VISIBLE);
Helper.loadGiF(PeertubeBaseMainActivity.this, castedTube.getThumbnailPath(), binding.castView);
binding.castTitle.setText(castedTube.getTitle());
binding.castDescription.setText(castedTube.getDescription());
}
} else if (displayed == 0) {
chromecastActivated = false;
binding.castInfo.setVisibility(View.GONE);
new Thread(() -> {
try {
if (chromeCast != null) {
chromeCast.stopApp();
}
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
}
};
LocalBroadcastManager.getInstance(PeertubeBaseMainActivity.this).registerReceiver(manage_chromecast, new IntentFilter(Helper.RECEIVE_CAST_SETTINGS));
}
@Override
public void newChromeCastDiscovered(ChromeCast chromeCast) {
if (chromeCasts == null) {
chromeCasts = new ArrayList<>();
chromeCasts.add(chromeCast);
} else {
boolean canBeAdded = true;
for (ChromeCast cast : chromeCasts) {
if (cast.getName().compareTo(chromeCast.getName()) == 0) {
canBeAdded = false;
break;
}
}
if (canBeAdded) {
chromeCasts.add(chromeCast);
}
}
try {
if (chromeCast.isAppRunning(Helper.CAST_ID) && chromeCast.getMediaStatus() != null && chromeCast.getMediaStatus().playerState != null) {
if (binding.castInfo.getVisibility() == View.GONE) {
binding.castInfo.setVisibility(View.VISIBLE);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void chromeCastRemoved(ChromeCast chromeCast) {
}
@Override
public void onDestroy() {
super.onDestroy();
ChromeCasts.unregisterListener(this);
if (manage_chromecast != null) {
LocalBroadcastManager.getInstance(PeertubeBaseMainActivity.this).unregisterReceiver(manage_chromecast);
new Thread(() -> {
if (chromeCasts != null && chromeCasts.size() > 0) {
for (ChromeCast cast : chromeCasts) {
try {
cast.stopApp();
cast.disconnect();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}).start();
}
if (chromeCasts != null) {
chromeCasts = null;
}
if (chromeCast != null) {
chromeCast = null;
}
}
//Method for discovering cast devices
public void discoverCast() {
new Thread(() -> {
if (chromeCasts != null) {
for (ChromeCast cast : chromeCasts) {
try {
cast.disconnect();
} catch (IOException e) {
e.printStackTrace();
}
}
chromeCasts = null;
}
chromeCasts = new ArrayList<>();
try {
List<NetworkInterface> interfaces;
interfaces = Collections.list(NetworkInterface.getNetworkInterfaces());
for (NetworkInterface ni : interfaces) {
if ((!ni.isLoopback()) && ni.isUp() && (ni.getName().equals("wlan0"))) {
Enumeration<InetAddress> inetAddressEnumeration = ni.getInetAddresses();
while (inetAddressEnumeration.hasMoreElements()) {
InetAddress inetAddress = inetAddressEnumeration.nextElement();
ChromeCasts.restartDiscovery(inetAddress);
int tryFind = 0;
while (ChromeCasts.get().isEmpty() && tryFind < 5) {
try {
//noinspection BusyWait
Thread.sleep(1000);
tryFind++;
} catch (InterruptedException ignored) {
}
}
}
}
}
ChromeCasts.stopDiscovery();
Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = this::invalidateOptionsMenu;
mainHandler.post(myRunnable);
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
}

View file

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/castMiniController"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/black"
android:visibility="gone">
<ImageView
android:id="@+id/cast_play"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/play"
android:src="@drawable/ic_baseline_play_arrow_32"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/cast_loader"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
<TextView
android:id="@+id/cast_loader_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/please_wait"
android:textColor="?colorAccent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/cast_loader_small"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ProgressBar
android:id="@+id/cast_loader_small"
android:layout_width="wrap_content"
android:layout_height="18dp"
android:layout_gravity="center"
android:layout_marginStart="5dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/cast_loader_text"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_cast"
android:icon="@drawable/ic_baseline_cast_24"
android:title="@string/cast"
android:visible="false"
app:showAsAction="always" />
</menu>

View file

@ -0,0 +1,19 @@
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<shortcut
android:shortcutId="compose"
android:enabled="true"
android:icon="@drawable/ic_baseline_add_comment_24"
android:shortcutShortLabel="@string/compose_shortcut_short_label1"
tools:targetApi="n_mr1">
<intent
android:action="app.fedilab.android.shorcut.compose"
android:targetClass="app.fedilab.android.activities.MainActivity"
android:targetPackage="fr.gouv.etalab.mastodon" />
<categories android:name="android.shortcut.conversation" />
<capability-binding android:key="actions.intent.CREATE_MESSAGE" />
</shortcut>
</shortcuts>

View file

@ -5,6 +5,7 @@
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission <uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:name="android.permission.WRITE_EXTERNAL_STORAGE"
tools:ignore="ScopedStorage" /> tools:ignore="ScopedStorage" />
@ -27,9 +28,10 @@
android:icon="@mipmap/ic_launcher_bubbles" android:icon="@mipmap/ic_launcher_bubbles"
android:label="@string/app_name" android:label="@string/app_name"
android:largeHeap="true" android:largeHeap="true"
android:requestLegacyExternalStorage="true"
android:roundIcon="@mipmap/ic_launcher_bubbles_round" android:roundIcon="@mipmap/ic_launcher_bubbles_round"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/AppThemeDark" android:theme="@style/AppTheme"
android:usesCleartextTraffic="true" android:usesCleartextTraffic="true"
tools:replace="android:allowBackup"> tools:replace="android:allowBackup">
@ -56,6 +58,18 @@
<data android:mimeType="image/*" /> <data android:mimeType="image/*" />
<data android:mimeType="video/*" /> <data android:mimeType="video/*" />
</intent-filter> </intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<!-- The app is a good candidate for URL in https://domain.name/@xxxxxx-->
<!-- It should cover every URLs for statuses but some others not related to mastodon matching this scheme -->
<data
android:host="*"
android:pathPrefix="/@"
android:scheme="https" />
</intent-filter>
</activity> </activity>
<activity-alias <activity-alias
@ -66,9 +80,13 @@
android:roundIcon="@mipmap/ic_launcher_bubbles_round" android:roundIcon="@mipmap/ic_launcher_bubbles_round"
android:targetActivity=".activities.MainActivity"> android:targetActivity=".activities.MainActivity">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/compose_shortcuts" />
</activity-alias> </activity-alias>
<activity-alias <activity-alias
@ -82,6 +100,9 @@
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/compose_shortcuts" />
</activity-alias> </activity-alias>
<activity-alias <activity-alias
@ -95,6 +116,9 @@
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/compose_shortcuts" />
</activity-alias> </activity-alias>
<activity-alias <activity-alias
@ -108,6 +132,9 @@
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/compose_shortcuts" />
</activity-alias> </activity-alias>
<activity-alias <activity-alias
@ -121,6 +148,9 @@
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/compose_shortcuts" />
</activity-alias> </activity-alias>
<activity-alias <activity-alias
@ -134,6 +164,9 @@
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/compose_shortcuts" />
</activity-alias> </activity-alias>
@ -153,65 +186,71 @@
android:scheme="fedilab" /> android:scheme="fedilab" />
</intent-filter> </intent-filter>
</activity> </activity>
<activity <activity
android:name=".activities.WebviewConnectActivity" android:name=".mastodon.activities.StatusHistoryActivity"
android:configChanges="keyboardHidden|orientation|screenSize" />
<activity
android:name=".activities.StatusHistoryActivity"
android:configChanges="keyboardHidden|orientation|screenSize" android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/status_history" android:label="@string/status_history"
android:theme="@style/AppThemeBar" /> android:theme="@style/AppThemeBar" />
<activity <activity
android:name=".activities.ContextActivity" android:name=".mastodon.activities.ContextActivity"
android:configChanges="keyboardHidden|orientation|screenSize" /> android:configChanges="keyboardHidden|orientation|screenSize" />
<activity <activity
android:name=".activities.DraftActivity" android:name=".mastodon.activities.DraftActivity"
android:configChanges="keyboardHidden|orientation|screenSize" /> android:configChanges="keyboardHidden|orientation|screenSize" />
<activity <activity
android:name=".imageeditor.EditImageActivity" android:name=".mastodon.imageeditor.EditImageActivity"
android:configChanges="keyboardHidden|orientation|screenSize" /> android:configChanges="keyboardHidden|orientation|screenSize" />
<activity <activity
android:name=".activities.ComposeActivity" android:name=".mastodon.activities.ComposeActivity"
android:configChanges="orientation|screenSize" android:configChanges="orientation|screenSize"
android:label="@string/compose" /> android:label="@string/compose" />
<activity <activity
android:name=".activities.StatusInfoActivity" android:name=".mastodon.activities.StatusInfoActivity"
android:configChanges="keyboardHidden|orientation|screenSize" /> android:configChanges="keyboardHidden|orientation|screenSize" />
<activity <activity
android:name=".activities.FollowRequestActivity" android:name=".mastodon.activities.FollowRequestActivity"
android:configChanges="keyboardHidden|orientation|screenSize" /> android:configChanges="keyboardHidden|orientation|screenSize" />
<activity <activity
android:name=".activities.WebviewActivity" android:name=".mastodon.activities.ProfileActivity"
android:configChanges="keyboardHidden|orientation|screenSize" />
<activity
android:name=".activities.ProfileActivity"
android:configChanges="keyboardHidden|orientation|screenSize" android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/account" /> android:label="@string/account" />
<activity <activity
android:name=".activities.AdminAccountActivity" android:name=".mastodon.activities.admin.AdminAccountActivity"
android:configChanges="keyboardHidden|orientation|screenSize" android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/account" /> android:label="@string/account" />
<activity <activity
android:name=".activities.ScheduledActivity" android:name=".mastodon.activities.AccountReportActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/account"
android:theme="@style/AppThemeBar" />
<activity
android:name=".mastodon.activities.admin.AdminReportActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/report"
android:theme="@style/AppThemeBar" />
<activity
android:name=".mastodon.activities.ScheduledActivity"
android:configChanges="keyboardHidden|orientation|screenSize" android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/scheduled" /> android:label="@string/scheduled" />
<activity <activity
android:name="com.theartofdev.edmodo.cropper.CropImageActivity" android:name="com.canhub.cropper.CropImageActivity"
android:theme="@style/Base.Theme.AppCompat" /> android:theme="@style/Base.Theme.AppCompat" />
<activity <activity
android:name=".activities.SearchResultTabActivity" android:name=".mastodon.activities.SearchResultTabActivity"
android:configChanges="keyboardHidden|orientation|screenSize" android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/search" android:label="@string/search"
android:theme="@style/AppThemeBar" /> android:theme="@style/AppThemeBar" />
<activity <activity
android:name=".activities.TrendsActivity" android:name=".mastodon.activities.TrendsActivity"
android:configChanges="keyboardHidden|orientation|screenSize" android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/trending" android:label="@string/trending"
android:theme="@style/AppThemeBar" /> android:theme="@style/AppThemeBar" />
<activity <activity
android:name=".activities.ReorderTimelinesActivity" android:name=".mastodon.activities.ReorderTimelinesActivity"
android:configChanges="keyboardHidden|orientation|screenSize" android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/reorder_timelines" android:label="@string/reorder_timelines"
android:theme="@style/AppThemeBar" /> android:theme="@style/AppThemeBar" />
@ -222,84 +261,86 @@
android:theme="@style/AppThemeBar" /> android:theme="@style/AppThemeBar" />
<activity <activity
android:name=".activities.PartnerShipActivity" android:name=".mastodon.activities.admin.AdminDomainBlockActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/blocked_domains"
android:theme="@style/AppThemeBar" />
<activity
android:name=".mastodon.activities.SuggestionActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/Suggestions"
android:theme="@style/AppThemeBar" />
<activity
android:name=".mastodon.activities.DirectoryActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/Directory"
android:theme="@style/AppThemeBar" />
<activity
android:name=".mastodon.activities.PartnerShipActivity"
android:configChanges="keyboardHidden|orientation|screenSize" android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/action_about" android:label="@string/action_about"
android:theme="@style/AppThemeBar" /> android:theme="@style/AppThemeBar" />
<activity <activity
android:name=".activities.ActionActivity" android:name=".mastodon.activities.ActionActivity"
android:configChanges="keyboardHidden|orientation|screenSize" android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/interactions" android:label="@string/interactions"
android:theme="@style/AppThemeBar" /> android:theme="@style/AppThemeBar" />
<activity <activity
android:name=".activities.AdminActionActivity" android:name=".mastodon.activities.admin.AdminActionActivity"
android:configChanges="keyboardHidden|orientation|screenSize" android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/administration" android:label="@string/administration"
android:theme="@style/AppThemeBar" /> android:theme="@style/AppThemeBar" />
<activity <activity
android:name=".activities.MastodonListActivity" android:name=".mastodon.activities.MastodonListActivity"
android:configChanges="keyboardHidden|orientation|screenSize" android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/action_lists" android:label="@string/action_lists"
android:theme="@style/AppThemeBar" /> android:theme="@style/AppThemeBar" />
<activity <activity
android:name=".activities.SettingsActivity" android:name=".mastodon.activities.FollowedTagActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/followed_tags"
android:theme="@style/AppThemeBar" />
<activity
android:name=".mastodon.activities.SettingsActivity"
android:configChanges="keyboardHidden|orientation|screenSize" android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/settings" android:label="@string/settings"
android:theme="@style/AppThemeBar" /> android:theme="@style/AppThemeBar" />
<activity <activity
android:name=".activities.InstanceActivity" android:name=".mastodon.activities.HashTagActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/action_about_instance"
android:theme="@style/DialogDark" />
<activity
android:name=".activities.InstanceProfileActivity"
android:excludeFromRecents="true"
android:theme="@style/DialogDark" />
<activity
android:name=".activities.ProxyActivity"
android:excludeFromRecents="true"
android:theme="@style/DialogDark" />
<activity
android:name=".activities.HashTagActivity"
android:configChanges="keyboardHidden|orientation|screenSize" /> android:configChanges="keyboardHidden|orientation|screenSize" />
<activity <activity
android:name=".activities.AnnouncementActivity" android:name=".mastodon.activities.AnnouncementActivity"
android:configChanges="keyboardHidden|orientation|screenSize" /> android:configChanges="keyboardHidden|orientation|screenSize" />
<activity <activity
android:name=".activities.MediaActivity" android:name=".mastodon.activities.MediaActivity"
android:configChanges="keyboardHidden|orientation|screenSize" android:configChanges="keyboardHidden|orientation|screenSize"
android:theme="@style/TransparentDark" /> android:theme="@style/Transparent" />
<activity <activity
android:name=".activities.InstanceHealthActivity" android:name=".mastodon.activities.ReportActivity"
android:excludeFromRecents="true" android:theme="@style/AppThemeBar"
android:theme="@style/DialogDark" />
<activity
android:name=".activities.ReportActivity"
android:theme="@style/AppThemeBarDark"
android:windowSoftInputMode="stateVisible" /> android:windowSoftInputMode="stateVisible" />
<activity <activity
android:name=".activities.CustomSharingActivity" android:name=".mastodon.activities.CustomSharingActivity"
android:label="@string/settings_title_custom_sharing" android:label="@string/settings_title_custom_sharing"
android:theme="@style/AppThemeBarDark" android:theme="@style/AppThemeBar"
android:windowSoftInputMode="stateVisible" /> android:windowSoftInputMode="stateVisible" />
<activity <activity
android:name=".activities.FilterActivity" android:name=".mastodon.activities.FilterActivity"
android:label="@string/filters" android:label="@string/filters"
android:theme="@style/AppThemeBarDark" android:theme="@style/AppThemeBar"
android:windowSoftInputMode="stateVisible" /> android:windowSoftInputMode="stateVisible" />
<activity <activity
android:name=".activities.EditProfileActivity" android:name=".mastodon.activities.EditProfileActivity"
android:label="@string/edit_profile" android:label="@string/edit_profile"
android:theme="@style/AppThemeBarDark" android:theme="@style/AppThemeBar"
android:windowSoftInputMode="stateVisible" /> android:windowSoftInputMode="stateVisible" />
<activity <activity
android:name=".activities.CacheActivity" android:name=".mastodon.activities.CacheActivity"
android:label="@string/action_cache" android:label="@string/action_cache"
android:theme="@style/AppThemeBarDark" /> android:theme="@style/AppThemeBar" />
<provider <provider
android:name="androidx.core.content.FileProvider" android:name="androidx.core.content.FileProvider"
@ -312,7 +353,7 @@
</provider> </provider>
<receiver <receiver
android:name=".broadcastreceiver.ToastMessage" android:name=".mastodon.broadcastreceiver.ToastMessage"
android:exported="false"> android:exported="false">
<intent-filter> <intent-filter>
<action android:name="RECEIVE_TOAST_MESSAGE" /> <action android:name="RECEIVE_TOAST_MESSAGE" />
@ -320,7 +361,7 @@
</receiver> </receiver>
<receiver <receiver
android:name=".services.CustomReceiver" android:name=".mastodon.services.CustomReceiver"
android:enabled="true" android:enabled="true"
android:exported="true"> android:exported="true">
<intent-filter> <intent-filter>
@ -331,5 +372,105 @@
<action android:name="org.unifiedpush.android.connector.REGISTRATION_REFUSED" /> <action android:name="org.unifiedpush.android.connector.REGISTRATION_REFUSED" />
</intent-filter> </intent-filter>
</receiver> </receiver>
<activity
android:name=".peertube.activities.PeertubeMainActivity"
android:configChanges="keyboardHidden|orientation|screenSize" />
<activity
android:name=".peertube.activities.PeertubeActivity"
android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
android:launchMode="singleTask"
android:resizeableActivity="true"
android:supportsPictureInPicture="true"
tools:targetApi="n" />
<activity
android:name=".peertube.activities.PeertubeEditUploadActivity"
android:configChanges="orientation|screenSize"
android:exported="false"
android:windowSoftInputMode="stateAlwaysHidden" />
<activity
android:name=".peertube.activities.ShowChannelActivity"
android:configChanges="orientation|screenSize"
android:windowSoftInputMode="stateAlwaysHidden" />
<activity
android:name=".peertube.activities.ShowAccountActivity"
android:configChanges="orientation|screenSize"
android:windowSoftInputMode="stateAlwaysHidden" />
<activity
android:name=".peertube.activities.AccountActivity"
android:configChanges="orientation|screenSize"
android:windowSoftInputMode="stateAlwaysHidden" />
<activity
android:name=".peertube.activities.MyAccountActivity"
android:configChanges="orientation|screenSize"
android:windowSoftInputMode="stateAlwaysHidden" />
<activity
android:name=".peertube.activities.SearchActivity"
android:configChanges="orientation|screenSize"
android:windowSoftInputMode="stateAlwaysHidden" />
<activity
android:name=".peertube.activities.AllPlaylistsActivity"
android:configChanges="orientation|screenSize"
android:windowSoftInputMode="stateAlwaysHidden" />
<activity
android:name=".peertube.activities.AllLocalPlaylistsActivity"
android:configChanges="orientation|screenSize"
android:windowSoftInputMode="stateAlwaysHidden" />
<activity
android:name=".peertube.activities.PlaylistsActivity"
android:configChanges="orientation|screenSize"
android:windowSoftInputMode="stateAlwaysHidden" />
<activity
android:name=".peertube.activities.VideosTimelineActivity"
android:configChanges="orientation|screenSize"
android:windowSoftInputMode="stateAlwaysHidden" />
<activity
android:name=".peertube.activities.SepiaSearchActivity"
android:configChanges="orientation|screenSize"
android:label="@string/sepia_search"
android:windowSoftInputMode="stateAlwaysHidden" />
<activity
android:name=".peertube.activities.ManageInstancesActivity"
android:configChanges="orientation|screenSize"
android:label="@string/instances_picker"
android:windowSoftInputMode="stateAlwaysHidden" />
<activity
android:name=".peertube.activities.WebviewActivity"
android:configChanges="keyboardHidden|orientation|screenSize" />
<activity
android:name=".peertube.activities.LoginActivity"
android:configChanges="orientation|screenSize"
android:exported="true"
android:windowSoftInputMode="stateAlwaysHidden">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:host="backtotubelab"
android:scheme="tubelab" />
</intent-filter>
</activity>
<activity
android:name=".peertube.activities.SettingsActivity"
android:configChanges="orientation|screenSize"
android:label="@string/settings"
android:windowSoftInputMode="stateAlwaysHidden" />
<activity
android:name=".peertube.activities.PeertubeUploadActivity"
android:configChanges="orientation|screenSize"
android:label="@string/upload_video"
android:windowSoftInputMode="stateAlwaysHidden" />
<service
android:name=".peertube.services.RetrieveInfoService"
android:exported="false" />
</application> </application>
</manifest> </manifest>

View file

@ -0,0 +1,20 @@
{
"1": "Music",
"2": "Films",
"3": "Vehicles",
"4": "Art",
"5": "Sports",
"6": "Travels",
"7": "Gaming",
"8": "People",
"9": "Comedy",
"10": "Entertainment",
"11": "News & Politics",
"12": "How To",
"13": "Education",
"14": "Activism",
"15": "Science & Technology",
"16": "Animals",
"17": "Kids",
"18": "Food"
}

View file

@ -0,0 +1,199 @@
{
"aa": "Afar",
"ab": "Abkhazian",
"af": "Afrikaans",
"ak": "Akan",
"am": "Amharic",
"ar": "Arabic",
"an": "Aragonese",
"ase": "American Sign Language",
"as": "Assamese",
"av": "Avaric",
"avk": "Kotava",
"ay": "Aymara",
"az": "Azerbaijani",
"ba": "Bashkir",
"bm": "Bambara",
"be": "Belarusian",
"bn": "Bengali",
"bfi": "British Sign Language",
"bi": "Bislama",
"bo": "Tibetan",
"bs": "Bosnian",
"br": "Breton",
"bg": "Bulgarian",
"bzs": "Brazilian Sign Language",
"ca": "Catalan",
"cs": "Czech",
"ch": "Chamorro",
"ce": "Chechen",
"cv": "Chuvash",
"kw": "Cornish",
"co": "Corsican",
"cr": "Cree",
"cse": "Czech Sign Language",
"csl": "Chinese Sign Language",
"cy": "Welsh",
"da": "Danish",
"de": "German",
"dv": "Dhivehi",
"dsl": "Danish Sign Language",
"dz": "Dzongkha",
"el": "Greek",
"en": "English",
"eo": "Esperanto",
"et": "Estonian",
"eu": "Basque",
"ee": "Ewe",
"fo": "Faroese",
"fa": "Persian",
"fj": "Fijian",
"fi": "Finnish",
"fr": "French",
"fy": "Western Frisian",
"fsl": "French Sign Language",
"ff": "Fulah",
"gd": "Scottish Gaelic",
"ga": "Irish",
"gl": "Galician",
"gv": "Manx",
"gn": "Guarani",
"gsg": "German Sign Language",
"gu": "Gujarati",
"ht": "Haitian",
"ha": "Hausa",
"sh": "Serbo-Croatian",
"he": "Hebrew",
"hz": "Herero",
"hi": "Hindi",
"ho": "Hiri Motu",
"hr": "Croatian",
"hu": "Hungarian",
"hy": "Armenian",
"ig": "Igbo",
"ii": "Sichuan Yi",
"iu": "Inuktitut",
"id": "Indonesian",
"ik": "Inupiaq",
"is": "Icelandic",
"it": "Italian",
"jv": "Javanese",
"jbo": "Lojban",
"ja": "Japanese",
"jsl": "Japanese Sign Language",
"kab": "Kabyle",
"kl": "Kalaallisut",
"kn": "Kannada",
"ks": "Kashmiri",
"ka": "Georgian",
"kr": "Kanuri",
"kk": "Kazakh",
"km": "Khmer",
"ki": "Kikuyu",
"rw": "Kinyarwanda",
"ky": "Kirghiz",
"kv": "Komi",
"kg": "Kongo",
"ko": "Korean",
"kj": "Kuanyama",
"ku": "Kurdish",
"lo": "Lao",
"la": "Latin",
"lv": "Latvian",
"li": "Limburgan",
"ln": "Lingala",
"lt": "Lithuanian",
"lb": "Luxembourgish",
"lu": "Luba-Katanga",
"lg": "Ganda",
"mh": "Marshallese",
"ml": "Malayalam",
"mr": "Marathi",
"mk": "Macedonian",
"mg": "Malagasy",
"mt": "Maltese",
"mn": "Mongolian",
"mi": "Maori",
"ms": "Malay (macrolanguage)",
"my": "Burmese",
"na": "Nauru",
"nv": "Navajo",
"nr": "South Ndebele",
"nd": "North Ndebele",
"ng": "Ndonga",
"ne": "Nepali (macrolanguage)",
"nl": "Dutch",
"nn": "Norwegian Nynorsk",
"nb": "Norwegian Bokmål",
"no": "Norwegian",
"ny": "Nyanja",
"oc": "Occitan",
"oj": "Ojibwa",
"or": "Oriya (macrolanguage)",
"om": "Oromo",
"os": "Ossetian",
"pa": "Panjabi",
"pks": "Pakistan Sign Language",
"pl": "Polish",
"pt": "Portuguese",
"ps": "Pushto",
"qu": "Quechua",
"rm": "Romansh",
"ro": "Romanian",
"rsl": "Russian Sign Language",
"rn": "Rundi",
"ru": "Russian",
"sg": "Sango",
"sdl": "Saudi Arabian Sign Language",
"sfs": "South African Sign Language",
"si": "Sinhala",
"sk": "Slovak",
"sl": "Slovenian",
"se": "Northern Sami",
"sm": "Samoan",
"sn": "Shona",
"sd": "Sindhi",
"so": "Somali",
"st": "Southern Sotho",
"es": "Spanish",
"sq": "Albanian",
"sc": "Sardinian",
"sr": "Serbian",
"ss": "Swati",
"su": "Sundanese",
"sw": "Swahili (macrolanguage)",
"sv": "Swedish",
"swl": "Swedish Sign Language",
"ty": "Tahitian",
"ta": "Tamil",
"tt": "Tatar",
"te": "Telugu",
"tg": "Tajik",
"tl": "Tagalog",
"th": "Thai",
"ti": "Tigrinya",
"tlh": "Klingon",
"to": "Tonga (Tonga Islands)",
"tn": "Tswana",
"ts": "Tsonga",
"tk": "Turkmen",
"tr": "Turkish",
"tw": "Twi",
"ug": "Uighur",
"uk": "Ukrainian",
"ur": "Urdu",
"uz": "Uzbek",
"ve": "Venda",
"vi": "Vietnamese",
"wa": "Walloon",
"wo": "Wolof",
"xh": "Xhosa",
"yi": "Yiddish",
"yo": "Yoruba",
"za": "Zhuang",
"zh": "Chinese",
"zu": "Zulu",
"zxx": "No linguistic content",
"zh-Hans": "Simplified Chinese",
"zh-Hant": "Traditional Chinese"
}

File diff suppressed because it is too large Load diff

View file

@ -1,4 +1,259 @@
[ [
{
"version": "3.17.0",
"code": "477",
"note": "Added:\n- Peertube 2FA support\n- Cache home in background (default disabled -> New settings category and per account) / change frequency\n- Auto-fetch missing messages for the Home (default disabled -> in Settings - Timelines)\n- Automatically switch between tabs when searching\n- More deep links detection\n- Allow to group mentions at the top (default: disabled)\n\n\nFixed:\n- Dynamic color for Android 12+\n- Missing media description for previews\n- Fix a crash when replying\n- Fix button size not changed\n- Forward tags in replies\n- Media cannot be downloaded or shared with Android 10\n- Some crashes"
},
{
"version": "3.16.4",
"code": "476",
"note": "Added:\n- Cache home in background (default disabled -> New settings category and per account) / change frequency\n- Auto-fetch missing messages for the Home (default disabled -> in Settings - Timelines)\n- Automatically switch between tabs when searching\n\nFixed:\n- Some crashes"
},
{
"version": "3.16.3",
"code": "475",
"note": "Added:\n- Peertube 2FA support\n\nFixed:\n- Dynamic color for Android 12+"
},
{
"version": "3.16.2",
"code": "474",
"note": "Added:\n- Peertube support\n- Compose shortcut (long press launcher)\n- Long press compose button to write with another account\n- Edit description and focus for media (for the next Mastodon release)\n\nChanged:\n- Cross actions with two accounts display a dialog\n- Order & compact og values when sharing > title - url - content\n- Tap on top message (user info) open threads\n\nFixed:\n- Text cleared when adding a media\n- Fix Maths not working with quotes\n- Fix crashes"
},
{
"version": "3.16.1",
"code": "473",
"note": "Changed:\n- Edit description and focus for media (for the next Mastodon release)\n\nChanged:\n- Peertube: remove role support to avoid crashes with older instances\n\nFixed:\n- Fix some crashes"
},
{
"version": "3.16.0",
"code": "472",
"note": "Changed:\n- Peertube support\n- Compose shortcut\n- Long press compose button to write with another account\n\nChanged:\n- Cross actions with two accounts display a dialog\n- Order & compact og values when sharing > title - url - content\n\nFixed:\n- Text cleared when adding a media\n- Fix crashes"
},
{
"version": "3.15.2",
"code": "471",
"note": "Changed:\n- Add instance name when sharing\n\nFixed:\n- Fix a crash when removing media\n- Other minor fixes"
},
{
"version": "3.15.1",
"code": "470",
"note": "Changed:\n- Material dialogs\n\nFixed:\n- Light theme issues"
},
{
"version": "3.15.0",
"code": "469",
"note": "Added:\n- Maths support (view and compose)\n- Filter DMs in HOME (long press on the tab)\n- Filter languages for users in home timeline (from their profile)\n- Add several targeted languages for translator\n\nChanged:\n- Hide single media with preview is now a setting (default: disabled)\n- Group items in menu of messages\n\nFixed:\n- Cross-actions didn't display account instances"
},
{
"version": "3.14.6",
"code": "468",
"note": "Added:\n- Maths support (view and compose)\n\nChanged:\n- Hide single media with preview is now a setting (default: disabled)"
},
{
"version": "3.14.5",
"code": "467",
"note": "Changed:\n- Allow to swipe media for profiles\n\nFixed:\n- Fix crashes with pinch zoom\n- Copy/Paste in threads\n- Fix crash when checking redirection on http links\n- Display menu in media viewer resets pinch-zoom"
},
{
"version": "3.14.4",
"code": "466",
"note": "Changed:\n- Media viewer (pinch zoom)\n\nFixed:\n- Cross account actions (long press)\n- Boost color\n- Buttons not visible with custom themes.\n- Fix some bad behaviors with media viewer"
},
{
"version": "3.14.3",
"code": "465",
"note": "Added:\n- Display date of the message instead of the boost (default: disabled)\n- Allow to disable release notes popup in Settings\n\nFixed:\n- Fix timelines slow down and stuttering after some scrolls\n- Fix color issues with follow buttons\n- Fix import from settings (import from login was OK)"
},
{
"version": "3.14.2",
"code": "464",
"note": "Added:\n- Display familiar followers on profiles\n- Display and filter Instance directory\n\nChanged:\n- Bot and reply icon indicators more visible\n- Single media are hidden with link previews\n\nFixed:\n- Fix a crash with Art timelines\n- Friendica: media cannot be downloaded/shared\n- Fix a crash with pinned timelines"
},
{
"version": "3.14.1",
"code": "463",
"note": "Added:\n- Search bar: display suggestions when starting by \"@\" or \"#\"\n\nChanged:\n- Preload media in timelines to avoid jumps\n- Search: Automatically switch to account tab if no results for tags\n\nFixed:\n- Fix jumps with the fetch more feature\n- Fix videos cannot be saved\n- Tags cannot be pinned when there are no custom tabs\n- PixelFed view: NSFW not honored\n- Fix crashes"
},
{
"version": "3.14.0",
"code": "462",
"note": "Added:\n\n- Add Bubble timeline support in extra-features with filters\n- Allow to display public profiles by default to get all messages (Settings > Interface)\n- Glitch: Allow to post messages locally (Can be turned off in Settings)\n- Pixelfed: Custom layout to display Media fully (Also works for other software when there are media)\n- Allow to align left action buttons in messages\n\nChanged:\n- Full rework on links in messages (also mentions and tags)\n- Add pinned tag in \"any\" to avoid to lose it when renaming timeline\n\nFixed:\n- Links to messages not handled by the app\n- CW when editing a message\n- Fix push notifications with several accounts\n- New messages or edition notifications not pushed\n- Fix quotes with tags/mentions\n- Fix notifications\n- Fix sending multiple media\n- Fix crashes"
},
{
"version": "3.13.7",
"code": "461",
"note": "Added:\n- Pixelfed: Custom layout to display Media fully \n*(Settings > Timelines > Pixelfed Presentation) - Also works for other softwares when there are media\n\nChanged:\n- Add pinned tag in \"any\" to avoid to lose it when renaming timeline\n\nFixed:\n- Fix push notifications with several accounts\n- Fix quotes with tags/mentions\n- Fix notifications\n- Fix sending multiple media\n- Some crashes"
},
{
"version": "3.13.6",
"code": "460",
"note": "Fixed:\n- Cross-compose: Wrong instance emojis\n- Custom emojis not displayed in notifications\n- Fav/Boost markers with shared messages\n- Empty notifications\n- Fix cw removed when replying\n- Fix expand media with fit preview images when sensitive\n- Fix an issue with fetch more displayed too often (cache clear will help or wait new messages)"
},
{
"version": "3.13.5",
"code": "459",
"note": "Added:\n- Glitch: Allow to post messages locally (Can be turned off in Settings)\n\nFixed:\n- Crashes"
},
{
"version": "3.13.4",
"code": "458",
"note": "Added:\n- Add Bubble timeline support in extra-features with filters\n- Allow to display public profiles by default to get all messages (Settings > Interface)\n\nChanged:\n- Full rework on links in messages (also mentions and tags)\n\nFixed:\n- Spoiler text when editing\n- Fix watermarks"
},
{
"version": "3.13.3",
"code": "457",
"note": "Added:\n- Allow to enable extra features in Settings\n- Customizable settings for extra features\n- Support quotes, reactions with messages\n- Support text format (html, markdown, etc.) when composing\n\nFixed:\n- CW not working with media\n- Media not displayed for older instances\n- Some crashes\n"
},
{
"version": "3.13.2",
"code": "456",
"note": "Changed:\n- Hidden media smaller with preview images\n\nFixed:\n- Issue with Media for Android 11+\n- Crash when not setting a translation key\n- Fix DeepL for API pro\n- Crash when visiting a profile with a lot of media\n- Home muted accounts not working without filters\n- Animated custom emoji not displayed"
},
{
"version": "3.13.1",
"code": "455",
"note": "Added:\n- DeepL translation support free/pro keys\n\nChanged:\n- Hide buttons for media when editing\n\nFixed:\n- GIF loaded as static images\n- Suggested accounts cannot be followed"
},
{
"version": "3.13.0",
"code": "454",
"note": "Added:\n- Post random quotes\n- Group reblogs in home timeline\n- Rename Nitter timelines\n- Android 13 support\n- Pagination with search / trending\n- Allow to remove left margin in messages (default: disabled)\n\nChanged:\n- Display translate button only when language is different\n- Respect blank spaces between words in messages\n- Focus button more accessible when editing media\n- Visual feedback for block on account list\n- Visual changes with compose / top bar\n- Use custom Nitter timeline name in manage timelines\n\nFixed:\n- Behavior with cw toggle\n- Truncated gimini links\n- Nav buttons not visible with media (Light theme)\n- Status bar with Android 5\n- Fix links not clickable\n- Fix deep links\n- Fix remote threads not fetched for some instances\n- Adding description to shared media\n- Open with another accounts\n- Chars size not respected for Android 5-6\n- Wrong instance fetched for instances.social\n- Bouncing Timeline on refresh\n- Links to mentions, tags, urls, not visible.\n- Custom channel sounds not applied\n- users with short username are not linked\n- Fix crashes"
},
{
"version": "3.12.3",
"code": "453",
"note": "Added:\n- Pagination with search / trending\n\nFixed:\n- Long press on Nitter tabs\n- Open with another accounts\n- Chars size not respected for Android 5-6\n- Wrong instance fetched for instances.social"
},
{
"version": "3.12.2",
"code": "452",
"note": "Added:\n- Rename Nitter timelines\n- Android 13 support\n\nChanged:\n- Visual feedback for block on account list\n- Visual changes with compose / top bar\n\nFixed:\n- Nav buttons not visible with media (Light theme)\n- Status bar with Android 5\n- Fix links not clickable\n- Fix deep links\n- Fix remote threads not fetched for some instances\n- Adding description to shared media\n- Fix crashes"
},
{
"version": "3.12.1",
"code": "451",
"note": "Added:\n- Post random quotes\n- Group reblogs in home timeline\n\nChanged:\n- Display translate button only when language is different\n- Respect blank spaces between words in messages\n- Focus button more accessible when editing media\n\nFixed:\n- Behavior with cw toggle\n- Truncated gimini links"
},
{
"version": "3.12.0",
"code": "450",
"note": "Added:\n- Full data import/export feature\n- Android 13 themed icon support\n\nFixed:\n- Fix a regression with filters\n- Fix dark solarized theme\n- Fix hide link previews for CW\n- Fix status bar color for all themes\n- Fix language in compose \"...\"\n- Fix add all home muted accounts from lists\n- Fix top notification badges"
},
{
"version": "3.11.3",
"code": "449",
"note": "Added:\n- Add more targeted languages in picker for translations\n- Add account name in push notifications\n\nFixed:\n- Fix a crash when changing language\n- Fix counter colors\n- Fix default link color\n- Fix a crash when clicking on mentions"
},
{
"version": "3.11.2",
"code": "448",
"note": "Added:\n- Mute/Unmute accounts in the Home timeline from their messages or their profiles\n- Add all users from a list to \"Muted home\" in one click\n- Display/Manage users that are muted for home\n\nFixed:\n- Timeline crashes"
},
{
"version": "3.11.0",
"code": "446",
"note": "Added:\n- Display all messages in threads from remote instances (when possible)\n- Allow to unmute/unfollow/unpin a tag from tag timelines\n- Display most used accounts in header menu for an easy switch\n- Automatically add the tag when composing from a tag timeline\n- Add a translate button at the bottom of messages (default: disabled)\n- Add account role in profiles\n- Translate morse\n\nChanged:\n- Disable animations after a refresh\n\nFixed:\n- Contact not working when composing\n- Status bar for black theme\n- Message duplicated in conversations when edited\n- Color issue on Android 5\n- Several crashes"
},
{
"version": "3.10.2",
"code": "445",
"note": "Added:\n- Allow to unmute/unfollow/unpin a tag from tag timelines\n- Automatically add the tag when composing from a tag timeline\n- Add a translate button at the bottom of messages (default: disabled)\n- Add account role in profiles\n\nFixed:\n- Contact not working when composing\n- Status bar for black theme\n- Message duplicated in conversations when edited\n- Color issue on Android 5"
},
{
"version": "3.10.1",
"code": "444",
"note": "Added:\n- Display all messages in threads from remote instances (when possible)\n* Only public messages for instances using the Mastodon API\n* A dedicated button is displayed at the top right when conditions are filled."
},
{
"version": "3.10.0",
"code": "443",
"note": "Added:\n- Dracula theme\n- Customize message colors\n- Enable/Disable Card presentation\n\nChanged:\n- Colors for some themes\n- Space between buttons\n\nFixed:\n- Animated profile pictures not displayed\n- Mentions broken in profile bio and fields\n- Jumps with fit preview images when scrolling up\n- Fetch more button broken with cache\n- Tag patterns in URL break the link\n- Typo in followed tags"
},
{
"version": "3.9.7",
"code": "442",
"note": "Added:\n- Dracula theme\n\nChanged:\n- Colors for Light/Dark/Black themes\n\nFixed:\n- Animated profile pictures not displayed\n- Mentions broken in profile bio and fields\n- Tag patterns in URL break the link\n- Typo in followed tags"
},
{
"version": "3.9.6",
"code": "441",
"note": "Fixed:\n- Jumps with fit preview images when scrolling up\n- Fetch more button broken with cache"
},
{
"version": "3.9.5",
"code": "440",
"note": "Fixed:\n- Custom emoji are not always displayed\n- Jumps in timeline when using \"fit preview images\"\n- Dark theme: timeline buttons without toggle"
},
{
"version": "3.9.4",
"code": "439",
"note": "Changed:\n- Remove card presentation\n- Link color for black theme\n\nFixed:\n- Crash when changing the theme"
},
{
"version": "3.9.3",
"code": "438",
"note": "Added:\n- New design with 5 themes\n\nChanged:\n- Remove built-in browser support\n- Fit preview image displays images vertically\n- Add counters next to images\n\nFixed:\n- Jumps in timelines\n- Replies to wrong messages with followed instances\n- Bug with delete&redraft with a media\n- List cannot be hidden\n- Some crashes"
},
{
"version": "3.9.1",
"code": "436",
"note": "Changed:\n- Remove built-in browser support\n- More spaces between action buttons in messages\n\nFixed:\n- Text size issue\n- Text overlap\n- Wrong background for solarized black\n- Mix between light and dark theme\n- Save button hidden"
},
{
"version": "3.9.0",
"code": "435",
"note": "Added:\n- Migrate to Material Design 3\n- 5 Themes (Light, Dark, Solarized Light/Dark, Black)\n- Automatically switch between Light/Dark\n- Light and Dark theme can be defined for time-based switch\n- Android 12+: Dynamic color\n\nFixed:\n- Jumps in timelines\n"
},
{
"version": "3.8.1",
"code": "434",
"note": "Added:\n- Mute tags with long press in timelines\n\nChanged:\n- Muted account messages are now removed from cache\n\nFixed:\n- Open with another account\n- Fix jumps in profiles\n- Media not displayed in album -> force indexation\n- Built-in browser does not give admin scope\n- Some crashes"
},
{
"version": "3.8.0",
"code": "433",
"note": "Added:\n- List of blocked domains (allow to unblock)\n- Support gemini links\n- Suggested followers\n- Mod/Adm: Manage instance blocked domains\n- Open messages with another account\n- Allow to disable notifications for admins\n- Sort lists\n\nChanged:\n- Allow search term to be edited\n\nFixed:\n- Drafts deleted with no warning\n- Remove lists from \"Manage timelines\"\n- App crashes when proxy is set\n- Filter not synced after being edited\n- Some crashes / improvements"
},
{
"version": "3.7.5",
"code": "432",
"note": "Added:\n- List of blocked domains (allow to unblock)\n- Support gemini links\n- Suggested followers\n\nChanged:\n- Allow search term to be edited\n\nFixed:\n- Drafts deleted with no warning\n- App crashes when proxy is set\n- Filter not synced after being edited\n- Some crashes"
},
{
"version": "3.7.4",
"code": "431",
"note": "Added:\n- Full support to new filters for Mastodon 4\n- Visit profiles without being authenticated / Allow to display all their messages\n\nChanged:\n- Compose view takes the whole width even in threads\n- Accounts can be timed-mute from their profile\n\nFixed:\n- Draft stored when replying \"no\" or dialog prompted without changes\n- Empty pages when starting the app\n- Saving and sharing media fails on some devices\n- Add support for admin notifications\n- Copying content of a message"
},
{
"version": "3.7.3",
"code": "430",
"note": "Added:\n- Visit profiles without being authenticated / Allow to display all their messages\n\nFixed:\n- Saving media fails on some devices"
},
{
"version": "3.7.2",
"code": "429",
"note": "Added:\n- Full support to new filters for Mastodon 4"
},
{
"version": "3.7.1",
"code": "428",
"note": "Added:\n- Support to open links containing /@display_name/ in their path (works on older devices)\n- Display reply count when counters are enabled\n- Add support for filtering profile messages\n\nChanged:\n- Compose view takes the whole width even in threads\n- Reset push notification marker when clearing cache\n\nFixed:\n- Draft stored when replying \"no\" or dialog prompted without changes\n- Filters not working with tags\n- Add a specific error message for followed tags\n- Empty pages when starting the app"
},
{
"version": "3.7.0",
"code": "427",
"note": "Added:\n- Follow tags (dedicated entry in menu)\n- Reduce the list of languages when composing (Settings > Compose)\n- Language indicator when composing\n- Replies are automatically set to first message language\n- Two new Light themes\n- More moderation features\n- List name can be edited\n\nFixed:\n- Filter not working\n- Crash with trends\n- Issue with themes\n- Some content lost when sending messages (mentions)\n- Fix freezes in timelines\n- Some other fixes"
},
{
"version": "3.6.5",
"code": "426",
"note": "- Two new Light themes\n- More moderation features\n\nFixed:\n- Filter not working\n- Crash with trends\n- Some content lost when sending messages (mentions)\n- Some other fixes"
},
{
"version": "3.6.4",
"code": "425",
"note": "Changed:\n- Tag search ordered by popularity\n\nFixed:\n- Unable to get client ID on some devices\n- Issue with messages/notifications not correctly displayed\n- Notifications not received\n- Friendica: issues with mentions and tags (open browser)\n- Improve sharing behaviour"
},
{ {
"version": "3.6.3", "version": "3.6.3",
"code": "424", "code": "424",

View file

@ -1,13 +0,0 @@
base_theme,2
author,Fedilab
name,Breeze Dark - Yellow
theme_boost_header_color,-14012878
theme_statuses_color,-14473687
theme_link_color,-12734743
theme_icons_color,-4340793
pref_color_background,-15658735
pref_color_navigation_bar,true
pref_color_status_bar,true
theme_accent,-148405
theme_text_color,-1052431
theme_primary,-13552069
1 base_theme 2
2 author Fedilab
3 name Breeze Dark - Yellow
4 theme_boost_header_color -14012878
5 theme_statuses_color -14473687
6 theme_link_color -12734743
7 theme_icons_color -4340793
8 pref_color_background -15658735
9 pref_color_navigation_bar true
10 pref_color_status_bar true
11 theme_accent -148405
12 theme_text_color -1052431
13 theme_primary -13552069

View file

@ -1,15 +0,0 @@
base_theme,2
author,Roboron
name,Cyberpunk Neon
theme_boost_header_color,-16776697,
theme_text_header_1_line,-1441575,
theme_text_header_2_line,-5242717,
theme_statuses_color,-16181197,
theme_link_color,-1441575,
theme_icons_color,-16138810,
pref_color_background,-16774370,
pref_color_navigation_bar,true,
pref_color_status_bar,true,
theme_accent,-1441575,
theme_text_color,-16138810,
theme_primary,-16774370,
1 base_theme,2
2 author,Roboron
3 name,Cyberpunk Neon
4 theme_boost_header_color,-16776697,
5 theme_text_header_1_line,-1441575,
6 theme_text_header_2_line,-5242717,
7 theme_statuses_color,-16181197,
8 theme_link_color,-1441575,
9 theme_icons_color,-16138810,
10 pref_color_background,-16774370,
11 pref_color_navigation_bar,true,
12 pref_color_status_bar,true,
13 theme_accent,-1441575,
14 theme_text_color,-16138810,
15 theme_primary,-16774370,

View file

@ -1,15 +0,0 @@
base_theme,0
author,S1m
name,Dark Elephant
theme_boost_header_color,-13552317
theme_text_header_1_line,-3479297
theme_text_header_2_line,-7287815
theme_statuses_color,-13552317
theme_link_color,-11098143
theme_icons_color,-789517
pref_color_background,-14144456
pref_color_navigation_bar,true
pref_color_status_bar,true
theme_accent,-13922086
theme_text_color,-789517
theme_primary,-14144456
1 base_theme 0
2 author S1m
3 name Dark Elephant
4 theme_boost_header_color -13552317
5 theme_text_header_1_line -3479297
6 theme_text_header_2_line -7287815
7 theme_statuses_color -13552317
8 theme_link_color -11098143
9 theme_icons_color -789517
10 pref_color_background -14144456
11 pref_color_navigation_bar true
12 pref_color_status_bar true
13 theme_accent -13922086
14 theme_text_color -789517
15 theme_primary -14144456

View file

@ -1,15 +0,0 @@
base_theme,2
author,Jøta Seth
name,Grey Orange
theme_boost_header_color,-14869219
theme_text_header_1_line,-1
theme_text_header_2_line,-1
theme_statuses_color,-14145496
theme_link_color,-26624
theme_icons_color,-26624
pref_color_background,-13092808
pref_color_navigation_bar,true
pref_color_status_bar,true
theme_accent,-26624
theme_text_color,-1
theme_primary,-14408668
1 base_theme 2
2 author Jøta Seth
3 name Grey Orange
4 theme_boost_header_color -14869219
5 theme_text_header_1_line -1
6 theme_text_header_2_line -1
7 theme_statuses_color -14145496
8 theme_link_color -26624
9 theme_icons_color -26624
10 pref_color_background -13092808
11 pref_color_navigation_bar true
12 pref_color_status_bar true
13 theme_accent -26624
14 theme_text_color -1
15 theme_primary -14408668

View file

@ -1,15 +0,0 @@
base_theme,2
author,@AntoineD@h.kher.nl
name,Gruvbox OLED
theme_boost_header_color,-16777216
theme_text_header_1_line,-265785
theme_text_header_2_line,-6777062
theme_statuses_color,-16777216
theme_link_color,-2647775
theme_icons_color,-7175308
pref_color_background,-16777216
pref_color_navigation_bar,true
pref_color_status_bar,true
theme_accent,-9921174
theme_text_color,-265785
theme_primary,-16777216
1 base_theme 2
2 author @AntoineD@h.kher.nl
3 name Gruvbox OLED
4 theme_boost_header_color -16777216
5 theme_text_header_1_line -265785
6 theme_text_header_2_line -6777062
7 theme_statuses_color -16777216
8 theme_link_color -2647775
9 theme_icons_color -7175308
10 pref_color_background -16777216
11 pref_color_navigation_bar true
12 pref_color_status_bar true
13 theme_accent -9921174
14 theme_text_color -265785
15 theme_primary -16777216

View file

@ -1,15 +0,0 @@
base_theme,2
author,AngryTux
name,Less Angry Orange
theme_boost_header_color,-15855063
theme_text_header_1_line,-2128640
theme_text_header_2_line,-5329234
theme_statuses_color,-1
theme_link_color,-12146699
theme_icons_color,-2128640
pref_color_background,-15987700
pref_color_navigation_bar,true
pref_color_status_bar,true
theme_accent,-3968000
theme_text_color,-197380
theme_primary,-14408668
1 base_theme 2
2 author AngryTux
3 name Less Angry Orange
4 theme_boost_header_color -15855063
5 theme_text_header_1_line -2128640
6 theme_text_header_2_line -5329234
7 theme_statuses_color -1
8 theme_link_color -12146699
9 theme_icons_color -2128640
10 pref_color_background -15987700
11 pref_color_navigation_bar true
12 pref_color_status_bar true
13 theme_accent -3968000
14 theme_text_color -197380
15 theme_primary -14408668

View file

@ -1,15 +0,0 @@
base_theme,2
author,Mondstern
name,Mondstern Fedilab
theme_boost_header_color,-1,
theme_text_header_1_line,-13855804,
theme_text_header_2_line,-16227945,
theme_statuses_color,-14935012,
theme_link_color,-15542685,
theme_icons_color,-10723999,
pref_color_background,-15921907,
pref_color_navigation_bar,false,
pref_color_status_bar,false,
theme_accent,-15542685,
theme_text_color,-1,
theme_primary,-14474461,
1 base_theme,2
2 author,Mondstern
3 name,Mondstern Fedilab
4 theme_boost_header_color,-1,
5 theme_text_header_1_line,-13855804,
6 theme_text_header_2_line,-16227945,
7 theme_statuses_color,-14935012,
8 theme_link_color,-15542685,
9 theme_icons_color,-10723999,
10 pref_color_background,-15921907,
11 pref_color_navigation_bar,false,
12 pref_color_status_bar,false,
13 theme_accent,-15542685,
14 theme_text_color,-1,
15 theme_primary,-14474461,

View file

@ -1,13 +0,0 @@
base_theme,2
author,Fedilab
name,Nocturnal
theme_boost_header_color,-12895429
theme_statuses_color,-13553359
theme_link_color,-16747570
theme_icons_color,-10158118
pref_color_background,-14606047
pref_color_navigation_bar,true
pref_color_status_bar,true
theme_accent,-13136013
theme_text_color,-2236963
theme_primary,-14013910
1 base_theme 2
2 author Fedilab
3 name Nocturnal
4 theme_boost_header_color -12895429
5 theme_statuses_color -13553359
6 theme_link_color -16747570
7 theme_icons_color -10158118
8 pref_color_background -14606047
9 pref_color_navigation_bar true
10 pref_color_status_bar true
11 theme_accent -13136013
12 theme_text_color -2236963
13 theme_primary -14013910

View file

@ -1,15 +0,0 @@
base_theme,2
author,Jøta Seth
name,Photon Dark
theme_boost_header_color,-14145496
theme_text_header_1_line,-1
theme_text_header_2_line,-1
theme_statuses_color,-14935012
theme_link_color,-14059009
theme_icons_color,-9474193
pref_color_background,-15921907
pref_color_navigation_bar,true
pref_color_status_bar,true
theme_accent,-14059009
theme_text_color,-1
theme_primary,-14474461
1 base_theme 2
2 author Jøta Seth
3 name Photon Dark
4 theme_boost_header_color -14145496
5 theme_text_header_1_line -1
6 theme_text_header_2_line -1
7 theme_statuses_color -14935012
8 theme_link_color -14059009
9 theme_icons_color -9474193
10 pref_color_background -15921907
11 pref_color_navigation_bar true
12 pref_color_status_bar true
13 theme_accent -14059009
14 theme_text_color -1
15 theme_primary -14474461

View file

@ -1,15 +0,0 @@
base_theme,2
author,Fedilab
name,Solarized Dark - Purple
theme_boost_header_color,-16506327
theme_text_header_1_line,-1120043
theme_text_header_2_line,-1120043
theme_statuses_color,-16304574
theme_link_color,-14251054
theme_icons_color,-7102047
pref_color_background,-16766154
pref_color_navigation_bar,true
pref_color_status_bar,true
theme_accent,-9670204
theme_text_color,-133405
theme_primary,-16304574
1 base_theme 2
2 author Fedilab
3 name Solarized Dark - Purple
4 theme_boost_header_color -16506327
5 theme_text_header_1_line -1120043
6 theme_text_header_2_line -1120043
7 theme_statuses_color -16304574
8 theme_link_color -14251054
9 theme_icons_color -7102047
10 pref_color_background -16766154
11 pref_color_navigation_bar true
12 pref_color_status_bar true
13 theme_accent -9670204
14 theme_text_color -133405
15 theme_primary -16304574

View file

@ -1,47 +0,0 @@
[
{
"theme_name": "Dark",
"base_theme": "DARK",
"primary": "#FF272727",
"primary_dark": "#FF272727",
"primary_light": "#FFd9e1e8",
"accent": "#FF2b90d9",
"accent_dark": "#FF1b80c9",
"accent_light": "#FF772b90d9",
"background": "#FF272727",
"background_dark": "#FF282c37",
"background_light": "#FF282c37",
"should_tint_statusbar": true,
"should_tint_navbar": true
},
{
"theme_name": "Light",
"base_theme": "LIGHT",
"primary": "#FFFFFF",
"primary_dark": "#FFFFFFFF",
"primary_light": "#FFd9e1e8",
"accent": "#FF2b90d9",
"accent_dark": "#FF1b80c9",
"accent_light": "#FF772b90d9",
"background": "#FFFFFFFF",
"background_dark": "#FFFFFFFF",
"background_light": "#FFFFFFFF",
"should_tint_statusbar": true,
"should_tint_navbar": true
},
{
"theme_name": "Black",
"base_theme": "DARK",
"primary": "#FF000000",
"primary_dark": "#FF000000",
"primary_light": "#FF000000",
"accent": "#FF606984",
"accent_dark": "#FF606984",
"accent_light": "#FF606984",
"background": "#FF000000",
"background_dark": "#FF000000",
"background_light": "#FF000000",
"should_tint_statusbar": true,
"should_tint_navbar": true
}
]

File diff suppressed because it is too large Load diff

View file

@ -15,16 +15,20 @@ package app.fedilab.android;
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.os.Build;
import android.os.StrictMode; import android.os.StrictMode;
import android.webkit.WebView;
import androidx.multidex.MultiDex; import androidx.multidex.MultiDex;
import androidx.multidex.MultiDexApplication; import androidx.multidex.MultiDexApplication;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import com.jaredrummler.cyanea.Cyanea; import net.gotev.uploadservice.UploadServiceConfig;
import com.jaredrummler.cyanea.prefs.CyaneaTheme; import net.gotev.uploadservice.observer.request.GlobalRequestObserver;
import org.acra.ACRA; import org.acra.ACRA;
import org.acra.ReportField; import org.acra.ReportField;
@ -33,42 +37,41 @@ import org.acra.config.DialogConfigurationBuilder;
import org.acra.config.MailSenderConfigurationBuilder; import org.acra.config.MailSenderConfigurationBuilder;
import org.acra.data.StringFormat; import org.acra.data.StringFormat;
import java.util.List; import java.util.Objects;
import app.fedilab.android.mastodon.helper.ThemeHelper;
import app.fedilab.android.peertube.services.GlobalUploadObserver;
import es.dmoral.toasty.Toasty; import es.dmoral.toasty.Toasty;
public class MainApplication extends MultiDexApplication { public class MainApplication extends MultiDexApplication {
public static String UPLOAD_CHANNEL_ID = "upload_info_peertube";
private static MainApplication app; private WebView webView;
public static MainApplication getApp() {
return app;
}
@Override @Override
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
app = this; try {
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(MainApplication.this); webView = new WebView(this);
} catch (Exception ignored) {
Cyanea.init(this, super.getResources());
List<CyaneaTheme> list = CyaneaTheme.Companion.from(getAssets(), "themes/cyanea_themes.json");
boolean custom_theme = sharedpreferences.getBoolean("use_custom_theme", false);
boolean no_theme_set = sharedpreferences.getBoolean("no_theme_set", true);
if (no_theme_set && !custom_theme) {
list.get(0).apply(Cyanea.getInstance());
SharedPreferences.Editor editor = sharedpreferences.edit();
editor.putBoolean("no_theme_set", false);
editor.apply();
} }
StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder(); StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
StrictMode.setVmPolicy(builder.build()); StrictMode.setVmPolicy(builder.build());
Toasty.Config.getInstance().apply(); Toasty.Config.getInstance().apply();
if (webView != null) {
try {
webView.destroy();
} catch (Exception ignored) {
} }
}
createNotificationChannel();
UploadServiceConfig.initialize(MainApplication.this, UPLOAD_CHANNEL_ID, true);
new GlobalRequestObserver(this, new GlobalUploadObserver());
}
@Override @Override
protected void attachBaseContext(Context base) { protected void attachBaseContext(Context base) {
@ -76,6 +79,8 @@ public class MainApplication extends MultiDexApplication {
MultiDex.install(MainApplication.this); MultiDex.install(MainApplication.this);
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(MainApplication.this); SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(MainApplication.this);
boolean send_crash_reports = sharedpreferences.getBoolean(getString(R.string.SET_SEND_CRASH_REPORTS), false); boolean send_crash_reports = sharedpreferences.getBoolean(getString(R.string.SET_SEND_CRASH_REPORTS), false);
String currentTheme = sharedpreferences.getString(getString(R.string.SET_THEME_BASE), getString(R.string.SET_DEFAULT_THEME));
ThemeHelper.switchTo(currentTheme);
if (send_crash_reports) { if (send_crash_reports) {
ACRA.init(this, new CoreConfigurationBuilder() ACRA.init(this, new CoreConfigurationBuilder()
//core configuration: //core configuration:
@ -92,7 +97,6 @@ public class MainApplication extends MultiDexApplication {
.withResIcon(R.mipmap.ic_launcher) .withResIcon(R.mipmap.ic_launcher)
.withText(getString(R.string.crash_title)) .withText(getString(R.string.crash_title))
.withCommentPrompt(getString(R.string.crash_message)) .withCommentPrompt(getString(R.string.crash_message))
.withResTheme(R.style.DialogDark)
.withPositiveButtonText(getString(R.string.send_email)) .withPositiveButtonText(getString(R.string.send_email))
.withNegativeButtonText(getString(R.string.cancel)) .withNegativeButtonText(getString(R.string.cancel))
.build() .build()
@ -108,4 +112,15 @@ public class MainApplication extends MultiDexApplication {
); );
} }
} }
private void createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(UPLOAD_CHANNEL_ID,
getString(R.string.notification_channel_name),
NotificationManager.IMPORTANCE_LOW);
channel.setSound(null, null);
((NotificationManager) Objects.requireNonNull(getSystemService(Context.NOTIFICATION_SERVICE))).createNotificationChannel(channel);
}
}
} }

View file

@ -18,13 +18,11 @@ package app.fedilab.android.activities;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageInfo; import android.content.pm.PackageInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle; import android.os.Bundle;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import androidx.core.app.ActivityOptionsCompat; import androidx.core.app.ActivityOptionsCompat;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import java.util.ArrayList; import java.util.ArrayList;
@ -33,17 +31,18 @@ import java.util.List;
import app.fedilab.android.BaseMainActivity; import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.BuildConfig; import app.fedilab.android.BuildConfig;
import app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.client.entities.api.Account;
import app.fedilab.android.client.entities.api.Status;
import app.fedilab.android.databinding.ActivityAboutBinding; import app.fedilab.android.databinding.ActivityAboutBinding;
import app.fedilab.android.helper.CrossActionHelper; import app.fedilab.android.mastodon.activities.BaseBarActivity;
import app.fedilab.android.helper.Helper; import app.fedilab.android.mastodon.activities.ProfileActivity;
import app.fedilab.android.helper.MastodonHelper; import app.fedilab.android.mastodon.client.entities.api.Account;
import app.fedilab.android.helper.ThemeHelper; import app.fedilab.android.mastodon.client.entities.api.Status;
import app.fedilab.android.viewmodel.mastodon.AccountsVM; import app.fedilab.android.mastodon.helper.CrossActionHelper;
import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.mastodon.helper.MastodonHelper;
import app.fedilab.android.mastodon.viewmodel.mastodon.AccountsVM;
public class AboutActivity extends BaseActivity { public class AboutActivity extends BaseBarActivity {
private ActivityAboutBinding binding; private ActivityAboutBinding binding;
@ -52,13 +51,12 @@ public class AboutActivity extends BaseActivity {
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
ThemeHelper.applyThemeBar(this);
binding = ActivityAboutBinding.inflate(getLayoutInflater()); binding = ActivityAboutBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); setContentView(binding.getRoot());
if (getSupportActionBar() != null) { if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
} }
try { try {
@ -79,13 +77,12 @@ public class AboutActivity extends BaseActivity {
} }
binding.aboutSupportPaypal.setOnClickListener(v -> Helper.openBrowser(AboutActivity.this, "https://www.paypal.me/Mastalab")); binding.aboutSupportPaypal.setOnClickListener(v -> Helper.openBrowser(AboutActivity.this, "https://www.paypal.me/Mastalab"));
binding.accountFollow.setBackgroundTintList(ThemeHelper.getButtonActionColorStateList(AboutActivity.this));
if (BuildConfig.DONATIONS) { if (BuildConfig.DONATIONS) {
binding.aboutSupportPaypal.setVisibility(View.VISIBLE); binding.aboutSupportPaypal.setVisibility(View.VISIBLE);
} else { } else {
binding.aboutSupportPaypal.setVisibility(View.GONE); binding.aboutSupportPaypal.setVisibility(View.GONE);
} }
binding.accountFollow.setImageResource(R.drawable.ic_baseline_person_add_24); binding.accountFollow.setIconResource(R.drawable.ic_baseline_person_add_24);
binding.aboutWebsite.setOnClickListener(v -> Helper.openBrowser(AboutActivity.this, "https://fedilab.app")); binding.aboutWebsite.setOnClickListener(v -> Helper.openBrowser(AboutActivity.this, "https://fedilab.app"));
CrossActionHelper.fetchRemoteAccount(AboutActivity.this, "@apps@toot.fedilab.app", new CrossActionHelper.Callback() { CrossActionHelper.fetchRemoteAccount(AboutActivity.this, "@apps@toot.fedilab.app", new CrossActionHelper.Callback() {
@Override @Override
@ -118,7 +115,7 @@ public class AboutActivity extends BaseActivity {
if (relationShips != null && relationShips.size() > 0) { if (relationShips != null && relationShips.size() > 0) {
if (!relationShips.get(0).following) { if (!relationShips.get(0).following) {
binding.accountFollow.setVisibility(View.VISIBLE); binding.accountFollow.setVisibility(View.VISIBLE);
binding.accountFollow.setOnClickListener(v -> accountsVM.follow(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id, true, false) binding.accountFollow.setOnClickListener(v -> accountsVM.follow(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id, true, false, null)
.observe(AboutActivity.this, relationShip -> binding.accountFollow.setVisibility(View.GONE))); .observe(AboutActivity.this, relationShip -> binding.accountFollow.setVisibility(View.GONE)));
} }
} }

View file

@ -1,163 +0,0 @@
package app.fedilab.android.activities;
/* Copyright 2021 Thomas Schneider
*
* This file is a part of Fedilab
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */
import static app.fedilab.android.BaseMainActivity.currentAccount;
import static app.fedilab.android.ui.drawer.StatusAdapter.sendAction;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.TypedValue;
import android.view.Menu;
import android.view.MenuItem;
import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBar;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.preference.PreferenceManager;
import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R;
import app.fedilab.android.client.entities.api.Status;
import app.fedilab.android.client.entities.app.StatusCache;
import app.fedilab.android.databinding.ActivityConversationBinding;
import app.fedilab.android.exception.DBException;
import app.fedilab.android.helper.Helper;
import app.fedilab.android.helper.MastodonHelper;
import app.fedilab.android.helper.ThemeHelper;
import app.fedilab.android.ui.fragment.timeline.FragmentMastodonContext;
import app.fedilab.android.viewmodel.mastodon.StatusesVM;
public class ContextActivity extends BaseActivity {
public static boolean expand;
public static boolean displayCW;
public static Resources.Theme theme;
Fragment currentFragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ThemeHelper.applyTheme(this);
app.fedilab.android.databinding.ActivityConversationBinding binding = ActivityConversationBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
setSupportActionBar(binding.toolbar);
ActionBar actionBar = getSupportActionBar();
//Remove title
if (actionBar != null) {
actionBar.setDisplayShowTitleEnabled(false);
actionBar.setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
}
binding.title.setText(R.string.context_conversation);
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(this);
float scale = sharedpreferences.getFloat(getString(R.string.SET_FONT_SCALE), 1.1f);
binding.title.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18 * 1.1f / scale);
if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true);
}
binding.toolbar.setPopupTheme(Helper.popupStyle());
Bundle b = getIntent().getExtras();
displayCW = sharedpreferences.getBoolean(getString(R.string.SET_EXPAND_CW), false);
Status focusedStatus = null; // or other values
if (b != null)
focusedStatus = (Status) b.getSerializable(Helper.ARG_STATUS);
if (focusedStatus == null || currentAccount == null || currentAccount.mastodon_account == null) {
finish();
return;
}
MastodonHelper.loadPPMastodon(binding.profilePicture, currentAccount.mastodon_account);
Bundle bundle = new Bundle();
bundle.putSerializable(Helper.ARG_STATUS, focusedStatus);
currentFragment = Helper.addFragment(getSupportFragmentManager(), R.id.nav_host_fragment_content_main, new FragmentMastodonContext(), bundle, null, null);
StatusesVM timelinesVM = new ViewModelProvider(ContextActivity.this).get(StatusesVM.class);
timelinesVM.getStatus(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, focusedStatus.id).observe(ContextActivity.this, status -> {
if (status != null) {
StatusCache statusCache = new StatusCache();
statusCache.instance = BaseMainActivity.currentInstance;
statusCache.user_id = BaseMainActivity.currentUserID;
statusCache.status = status;
statusCache.status_id = status.id;
//Update cache
new Thread(() -> {
try {
new StatusCache(getApplication()).updateIfExists(statusCache);
Handler mainHandler = new Handler(Looper.getMainLooper());
//Update UI
Runnable myRunnable = () -> sendAction(ContextActivity.this, Helper.ARG_STATUS_ACTION, status, null);
mainHandler.post(myRunnable);
} catch (DBException e) {
e.printStackTrace();
}
}).start();
}
});
}
@Override
public boolean onCreateOptionsMenu(@NonNull Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_context, menu);
MenuItem itemExpand = menu.findItem(R.id.action_expand);
if (expand) {
itemExpand.setIcon(R.drawable.ic_baseline_expand_less_24);
} else {
itemExpand.setIcon(R.drawable.ic_baseline_expand_more_24);
}
MenuItem itemDisplayCW = menu.findItem(R.id.action_show_cw);
if (displayCW) {
itemDisplayCW.setIcon(R.drawable.ic_baseline_remove_red_eye_24);
} else {
itemDisplayCW.setIcon(R.drawable.ic_outline_remove_red_eye_24);
}
return true;
}
public void setCurrentFragment(FragmentMastodonContext fragmentMastodonContext) {
currentFragment = fragmentMastodonContext;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
finish();
return true;
} else if (item.getItemId() == R.id.action_expand) {
expand = !expand;
if (currentFragment != null && currentFragment instanceof FragmentMastodonContext) {
((FragmentMastodonContext) currentFragment).redraw();
}
invalidateOptionsMenu();
} else if (item.getItemId() == R.id.action_show_cw) {
displayCW = !displayCW;
if (currentFragment != null && currentFragment instanceof FragmentMastodonContext) {
((FragmentMastodonContext) currentFragment).refresh();
}
invalidateOptionsMenu();
}
return true;
}
}

View file

@ -1,170 +0,0 @@
package app.fedilab.android.activities;
/* Copyright 2022 Thomas Schneider
*
* This file is a part of Fedilab
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */
import static app.fedilab.android.BaseMainActivity.currentAccount;
import android.content.Intent;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.Menu;
import android.view.MenuItem;
import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBar;
import androidx.core.content.ContextCompat;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import java.util.ArrayList;
import java.util.List;
import app.fedilab.android.R;
import app.fedilab.android.client.entities.api.Status;
import app.fedilab.android.client.entities.app.Pinned;
import app.fedilab.android.client.entities.app.PinnedTimeline;
import app.fedilab.android.client.entities.app.StatusDraft;
import app.fedilab.android.client.entities.app.TagTimeline;
import app.fedilab.android.client.entities.app.Timeline;
import app.fedilab.android.databinding.ActivityHashtagBinding;
import app.fedilab.android.exception.DBException;
import app.fedilab.android.helper.Helper;
import app.fedilab.android.helper.ThemeHelper;
import app.fedilab.android.ui.fragment.timeline.FragmentMastodonTimeline;
import es.dmoral.toasty.Toasty;
public class HashTagActivity extends BaseActivity {
public static int position;
private String tag;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ThemeHelper.applyTheme(this);
ActivityHashtagBinding binding = ActivityHashtagBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
Bundle b = getIntent().getExtras();
if (b != null) {
tag = b.getString(Helper.ARG_SEARCH_KEYWORD, null);
}
if (tag == null)
finish();
setSupportActionBar(binding.toolbar);
ActionBar actionBar = getSupportActionBar();
//Remove title
if (actionBar != null) {
actionBar.setDisplayShowTitleEnabled(false);
actionBar.setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
}
binding.title.setText(tag);
if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true);
}
Bundle bundle = new Bundle();
bundle.putSerializable(Helper.ARG_TIMELINE_TYPE, Timeline.TimeLineEnum.TAG);
bundle.putString(Helper.ARG_SEARCH_KEYWORD, tag);
Helper.addFragment(getSupportFragmentManager(), R.id.nav_host_fragment_tags, new FragmentMastodonTimeline(), bundle, null, null);
binding.toolbar.setPopupTheme(Helper.popupStyle());
binding.compose.setOnClickListener(v -> {
Intent intentToot = new Intent(HashTagActivity.this, ComposeActivity.class);
StatusDraft statusDraft = new StatusDraft();
Status status = new Status();
status.text = "#" + tag;
List<Status> statuses = new ArrayList<>();
statuses.add(status);
statusDraft.statusDraftList = statuses;
Bundle _b = new Bundle();
_b.putSerializable(Helper.ARG_TAG_TIMELINE, statusDraft);
intentToot.putExtras(_b);
startActivity(intentToot);
});
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
finish();
return true;
} else if (item.getItemId() == R.id.action_add_timeline) {
new Thread(() -> {
try {
Pinned pinned = new Pinned(HashTagActivity.this).getPinned(currentAccount);
boolean canBeAdded = true;
boolean update = true;
if (pinned == null) {
pinned = new Pinned();
pinned.pinnedTimelines = new ArrayList<>();
update = false;
} else {
for (PinnedTimeline pinnedTimeline : pinned.pinnedTimelines) {
if (pinnedTimeline.type == Timeline.TimeLineEnum.TAG) {
if (pinnedTimeline.tagTimeline.name.compareTo(tag.trim()) == 0) {
canBeAdded = false;
}
}
}
}
if (!canBeAdded) {
Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> Toasty.warning(HashTagActivity.this, getString(R.string.tags_already_stored), Toasty.LENGTH_SHORT).show();
mainHandler.post(myRunnable);
return;
}
PinnedTimeline pinnedTimeline = new PinnedTimeline();
pinnedTimeline.type = Timeline.TimeLineEnum.TAG;
pinnedTimeline.position = pinned.pinnedTimelines.size();
pinnedTimeline.displayed = true;
TagTimeline tagTimeline = new TagTimeline();
tagTimeline.name = tag.trim();
tagTimeline.isNSFW = false;
tagTimeline.isART = false;
pinnedTimeline.tagTimeline = tagTimeline;
pinned.pinnedTimelines.add(pinnedTimeline);
if (update) {
new Pinned(HashTagActivity.this).updatePinned(pinned);
} else {
new Pinned(HashTagActivity.this).insertPinned(pinned);
}
Bundle b = new Bundle();
b.putBoolean(Helper.RECEIVE_REDRAW_TOPBAR, true);
Intent intentBD = new Intent(Helper.BROADCAST_DATA);
intentBD.putExtras(b);
LocalBroadcastManager.getInstance(HashTagActivity.this).sendBroadcast(intentBD);
} catch (DBException e) {
e.printStackTrace();
}
}).start();
}
return super.onOptionsItemSelected(item);
}
@Override
public boolean onCreateOptionsMenu(@NonNull Menu menu) {
getMenuInflater().inflate(R.menu.menu_reorder, menu);
return super.onCreateOptionsMenu(menu);
}
}

View file

@ -1,153 +0,0 @@
package app.fedilab.android.activities;
/* Copyright 2022 Thomas Schneider
*
* This file is a part of Fedilab
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.text.Html;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.ViewModelProvider;
import androidx.preference.PreferenceManager;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.target.CustomTarget;
import com.bumptech.glide.request.transition.Transition;
import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R;
import app.fedilab.android.client.entities.api.Instance;
import app.fedilab.android.databinding.ActivityInstanceBinding;
import app.fedilab.android.helper.ThemeHelper;
import app.fedilab.android.viewmodel.mastodon.InstancesVM;
public class InstanceActivity extends BaseActivity {
ActivityInstanceBinding binding;
private boolean applyMaxChar = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ThemeHelper.applyThemeDialog(this);
binding = ActivityInstanceBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
getWindow().setLayout(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
if (getSupportActionBar() != null)
getSupportActionBar().hide();
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(InstanceActivity.this);
binding.close.setOnClickListener(
view -> {
if (applyMaxChar) {
String max_char = binding.maxChar.getText().toString();
SharedPreferences.Editor editor = sharedpreferences.edit();
if (!max_char.isEmpty()) {
try {
editor.putInt(getString(R.string.SET_MAX_INSTANCE_CHAR) + MainActivity.currentInstance, Integer.parseInt(max_char));
editor.apply();
} catch (Exception ignored) {
}
}
}
finish();
}
);
if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true);
}
InstancesVM instancesVM = new ViewModelProvider(InstanceActivity.this).get(InstancesVM.class);
instancesVM.getInstance(BaseMainActivity.currentInstance).observe(InstanceActivity.this, instanceInfo -> {
binding.instanceContainer.setVisibility(View.VISIBLE);
binding.loader.setVisibility(View.GONE);
if (instanceInfo == null || instanceInfo.info == null || instanceInfo.info.description == null) {
binding.maxCharContainer.setVisibility(View.VISIBLE);
binding.instanceContainer.setVisibility(View.GONE);
binding.instanceContact.setVisibility(View.GONE);
int val = sharedpreferences.getInt(getString(R.string.SET_MAX_INSTANCE_CHAR) + MainActivity.currentInstance, -1);
if (val != -1) {
binding.maxChar.setText(String.valueOf(val));
}
applyMaxChar = true;
} else {
Instance instance = instanceInfo.info;
binding.instanceTitle.setText(instance.title);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
binding.instanceDescription.setText(Html.fromHtml(instance.description, Html.FROM_HTML_MODE_LEGACY));
else
binding.instanceDescription.setText(Html.fromHtml(instance.description));
if (instance.description == null || instance.description.trim().length() == 0)
binding.instanceDescription.setText(getString(R.string.instance_no_description));
binding.instanceVersion.setText(instance.version);
binding.instanceUri.setText(instance.uri);
if (instance.email == null) {
binding.instanceContact.hide();
}
Glide.with(InstanceActivity.this)
.asDrawable()
.load(instance.thumbnail)
.into(new CustomTarget<Drawable>() {
@Override
public void onResourceReady(@NonNull Drawable resource, @Nullable Transition<? super Drawable> transition) {
binding.background.setAlpha(0.2f);
binding.background.setBackground(resource);
}
@Override
public void onLoadCleared(@Nullable Drawable placeholder) {
}
});
binding.instanceContact.setOnClickListener(v -> {
Intent emailIntent = new Intent(Intent.ACTION_SENDTO, Uri.fromParts("mailto", instance.email, null));
emailIntent.putExtra(Intent.EXTRA_SUBJECT, "[Mastodon] - " + instance.uri);
startActivity(Intent.createChooser(emailIntent, getString(R.string.send_email)));
});
}
});
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
}

View file

@ -1,114 +0,0 @@
package app.fedilab.android.activities;
/* Copyright 2022 Thomas Schneider
*
* This file is a part of Fedilab
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.text.SpannableString;
import android.text.style.UnderlineSpan;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.ViewModelProvider;
import com.bumptech.glide.Glide;
import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R;
import app.fedilab.android.client.entities.app.InstanceSocial;
import app.fedilab.android.databinding.ActivityInstanceSocialBinding;
import app.fedilab.android.helper.Helper;
import app.fedilab.android.helper.ThemeHelper;
import app.fedilab.android.viewmodel.mastodon.InstanceSocialVM;
public class InstanceHealthActivity extends BaseActivity {
private ActivityInstanceSocialBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ThemeHelper.applyThemeDialog(this);
binding = ActivityInstanceSocialBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
getWindow().setLayout(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
if (getSupportActionBar() != null)
getSupportActionBar().hide();
binding.close.setOnClickListener(view -> finish());
SpannableString content = new SpannableString(binding.refInstance.getText().toString());
content.setSpan(new UnderlineSpan(), 0, content.length(), 0);
binding.refInstance.setText(content);
binding.refInstance.setOnClickListener(view -> {
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://instances.social"));
startActivity(browserIntent);
});
checkInstance();
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
private void checkInstance() {
InstanceSocialVM instanceSocialVM = new ViewModelProvider(InstanceHealthActivity.this).get(InstanceSocialVM.class);
instanceSocialVM.getInstances(BaseMainActivity.currentInstance.trim()).observe(InstanceHealthActivity.this, instanceSocialList -> {
if (instanceSocialList != null && instanceSocialList.instances.size() > 0) {
InstanceSocial.Instance instanceSocial = instanceSocialList.instances.get(0);
if (instanceSocial.thumbnail != null && !instanceSocial.thumbnail.equals("null"))
Glide.with(InstanceHealthActivity.this)
.asBitmap()
.load(instanceSocial.thumbnail)
.into(binding.backGroundImage);
binding.name.setText(instanceSocial.name);
if (instanceSocial.up) {
binding.up.setText(R.string.is_up);
binding.up.setTextColor(ContextCompat.getColor(InstanceHealthActivity.this, R.color.green_1));
} else {
binding.up.setText(R.string.is_down);
binding.up.setTextColor(ContextCompat.getColor(InstanceHealthActivity.this, R.color.red_1));
}
binding.uptime.setText(getString(R.string.instance_health_uptime, (instanceSocial.uptime * 100)));
if (instanceSocial.checked_at != null)
binding.checkedAt.setText(getString(R.string.instance_health_checkedat, Helper.dateToString(instanceSocial.checked_at)));
binding.values.setText(getString(R.string.instance_health_indication, instanceSocial.version, Helper.withSuffix(instanceSocial.active_users), Helper.withSuffix(instanceSocial.statuses)));
binding.instanceContainer.setVisibility(View.VISIBLE);
} else {
binding.instanceContainer.setVisibility(View.VISIBLE);
binding.mainContainer.setVisibility(View.GONE);
binding.noInstance.setVisibility(View.VISIBLE);
}
binding.loader.setVisibility(View.GONE);
});
}
}

View file

@ -1,127 +0,0 @@
package app.fedilab.android.activities;
/* Copyright 2022 Thomas Schneider
*
* This file is a part of Fedilab
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */
import static androidx.core.text.HtmlCompat.FROM_HTML_MODE_LEGACY;
import android.os.Build;
import android.os.Bundle;
import android.text.Html;
import android.text.SpannableString;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager;
import java.util.ArrayList;
import java.util.List;
import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R;
import app.fedilab.android.client.entities.api.Account;
import app.fedilab.android.databinding.ActivityInstanceProfileBinding;
import app.fedilab.android.helper.Helper;
import app.fedilab.android.helper.ThemeHelper;
import app.fedilab.android.ui.drawer.AccountAdapter;
import app.fedilab.android.viewmodel.mastodon.NodeInfoVM;
import app.fedilab.android.viewmodel.mastodon.SearchVM;
import es.dmoral.toasty.Toasty;
public class InstanceProfileActivity extends BaseActivity {
private String instance;
private ActivityInstanceProfileBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ThemeHelper.applyThemeDialog(this);
binding = ActivityInstanceProfileBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
getWindow().setLayout(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
Bundle b = getIntent().getExtras();
if (getSupportActionBar() != null)
getSupportActionBar().hide();
if (b != null)
instance = b.getString(Helper.ARG_INSTANCE, null);
if (instance == null) {
finish();
}
Button close = findViewById(R.id.close);
close.setOnClickListener(view -> finish());
NodeInfoVM nodeInfoVM = new ViewModelProvider(InstanceProfileActivity.this).get(NodeInfoVM.class);
nodeInfoVM.getNodeInfo(instance).observe(InstanceProfileActivity.this, nodeInfo -> {
if (nodeInfo == null) {
Toasty.error(InstanceProfileActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show();
finish();
return;
}
binding.name.setText(nodeInfo.metadata != null ? nodeInfo.metadata.nodeName : instance);
SpannableString descriptionSpan;
if (nodeInfo.metadata != null && nodeInfo.metadata.nodeDescription != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
descriptionSpan = new SpannableString(Html.fromHtml(nodeInfo.metadata.nodeDescription, FROM_HTML_MODE_LEGACY));
else
descriptionSpan = new SpannableString(Html.fromHtml(nodeInfo.metadata.nodeDescription));
binding.description.setText(descriptionSpan, TextView.BufferType.SPANNABLE);
}
binding.userCount.setText(Helper.withSuffix((nodeInfo.usage.users.total)));
binding.statusCount.setText(Helper.withSuffix(((nodeInfo.usage.localPosts))));
String softwareStr = nodeInfo.software.name + " - ";
binding.software.setText(softwareStr);
binding.version.setText(nodeInfo.software.version);
if (nodeInfo.metadata != null && nodeInfo.metadata.staffAccounts != null && nodeInfo.metadata.staffAccounts.size() > 0) {
SearchVM searchVM = new ViewModelProvider(InstanceProfileActivity.this).get(SearchVM.class);
List<Account> accounts = new ArrayList<>();
for (String accountURL : nodeInfo.metadata.staffAccounts) {
searchVM.search(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, accountURL, null, "accounts", false, true, false, 0, null, null, 1)
.observe(InstanceProfileActivity.this, results -> {
if (results.accounts != null && results.accounts.size() > 0) {
accounts.add(results.accounts.get(0));
}
if (accounts.size() == nodeInfo.metadata.staffAccounts.size()) {
AccountAdapter accountsListAdapter = new AccountAdapter(accounts);
binding.lvAccounts.setAdapter(accountsListAdapter);
final LinearLayoutManager mLayoutManager;
mLayoutManager = new LinearLayoutManager(InstanceProfileActivity.this);
binding.lvAccounts.setLayoutManager(mLayoutManager);
}
});
}
}
binding.instanceContainer.setVisibility(View.VISIBLE);
binding.loader.setVisibility(View.GONE);
});
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
}

View file

@ -15,11 +15,15 @@ package app.fedilab.android.activities;
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import static app.fedilab.android.helper.MastodonHelper.REDIRECT_CONTENT_WEB; import static app.fedilab.android.mastodon.helper.Helper.PREF_USER_SOFTWARE;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.widget.FrameLayout; import android.widget.FrameLayout;
@ -32,14 +36,18 @@ import org.jetbrains.annotations.NotNull;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.client.entities.app.Account; import app.fedilab.android.mastodon.activities.BaseActivity;
import app.fedilab.android.helper.Helper; import app.fedilab.android.mastodon.activities.ProxyActivity;
import app.fedilab.android.helper.ThemeHelper; import app.fedilab.android.mastodon.client.entities.app.Account;
import app.fedilab.android.ui.fragment.login.FragmentLoginMain; import app.fedilab.android.mastodon.exception.DBException;
import app.fedilab.android.viewmodel.mastodon.AccountsVM; import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.viewmodel.mastodon.AdminVM; import app.fedilab.android.mastodon.helper.MastodonHelper;
import app.fedilab.android.viewmodel.mastodon.OauthVM; import app.fedilab.android.mastodon.viewmodel.mastodon.AccountsVM;
import app.fedilab.android.mastodon.viewmodel.mastodon.AdminVM;
import app.fedilab.android.mastodon.viewmodel.mastodon.OauthVM;
import app.fedilab.android.ui.fragment.FragmentLoginMain;
import es.dmoral.toasty.Toasty; import es.dmoral.toasty.Toasty;
@ -48,12 +56,40 @@ public class LoginActivity extends BaseActivity {
public static Account.API apiLogin; public static Account.API apiLogin;
public static String currentInstanceLogin, client_idLogin, client_secretLogin, softwareLogin; public static String currentInstanceLogin, client_idLogin, client_secretLogin, softwareLogin;
private final int PICK_IMPORT = 5557; public static boolean requestedAdmin;
private boolean requestedAdmin;
@SuppressLint("ApplySharedPref")
public void proceedLogin(Activity activity, Account account) {
new Thread(() -> {
try {
//update the database
new Account(activity).insertOrUpdate(account);
Handler mainHandler = new Handler(Looper.getMainLooper());
BaseMainActivity.currentToken = account.token;
BaseMainActivity.currentUserID = account.user_id;
BaseMainActivity.api = Account.API.MASTODON;
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(activity);
SharedPreferences.Editor editor = sharedpreferences.edit();
editor.putString(Helper.PREF_USER_TOKEN, account.token);
editor.putString(PREF_USER_SOFTWARE, BaseMainActivity.api.name());
editor.commit();
//The user is now authenticated, it will be redirected to MainActivity
Runnable myRunnable = () -> {
Intent mainActivity = new Intent(activity, MainActivity.class);
startActivity(mainActivity);
finish();
};
mainHandler.post(myRunnable);
} catch (DBException e) {
e.printStackTrace();
}
}).start();
}
private void manageItent(Intent intent) { private void manageItent(Intent intent) {
if (intent != null && intent.getData() != null && intent.getData().toString().contains(REDIRECT_CONTENT_WEB + "?code=")) { if (intent != null && intent.getData() != null && intent.getData().toString().contains(MastodonHelper.REDIRECT_CONTENT_WEB + "?code=")) {
String url = intent.getData().toString(); String url = intent.getData().toString();
Matcher matcher = Helper.codePattern.matcher(url); Matcher matcher = Helper.codePattern.matcher(url);
if (!matcher.find()) { if (!matcher.find()) {
@ -67,6 +103,7 @@ public class LoginActivity extends BaseActivity {
String scope = requestedAdmin ? Helper.OAUTH_SCOPES_ADMIN : Helper.OAUTH_SCOPES; String scope = requestedAdmin ? Helper.OAUTH_SCOPES_ADMIN : Helper.OAUTH_SCOPES;
oauthVM.createToken(currentInstanceLogin, "authorization_code", client_idLogin, client_secretLogin, Helper.REDIRECT_CONTENT_WEB, scope, code) oauthVM.createToken(currentInstanceLogin, "authorization_code", client_idLogin, client_secretLogin, Helper.REDIRECT_CONTENT_WEB, scope, code)
.observe(LoginActivity.this, tokenObj -> { .observe(LoginActivity.this, tokenObj -> {
if (tokenObj != null) {
Account account = new Account(); Account account = new Account();
account.client_id = client_idLogin; account.client_id = client_idLogin;
account.client_secret = client_secretLogin; account.client_secret = client_secretLogin;
@ -77,6 +114,7 @@ public class LoginActivity extends BaseActivity {
//API call to retrieve account information for the new token //API call to retrieve account information for the new token
AccountsVM accountsVM = new ViewModelProvider(LoginActivity.this).get(AccountsVM.class); AccountsVM accountsVM = new ViewModelProvider(LoginActivity.this).get(AccountsVM.class);
accountsVM.getConnectedAccount(currentInstanceLogin, account.token).observe(LoginActivity.this, mastodonAccount -> { accountsVM.getConnectedAccount(currentInstanceLogin, account.token).observe(LoginActivity.this, mastodonAccount -> {
if (mastodonAccount != null) {
account.mastodon_account = mastodonAccount; account.mastodon_account = mastodonAccount;
account.user_id = mastodonAccount.id; account.user_id = mastodonAccount.id;
//We check if user have really moderator rights //We check if user have really moderator rights
@ -84,16 +122,23 @@ public class LoginActivity extends BaseActivity {
AdminVM adminVM = new ViewModelProvider(LoginActivity.this).get(AdminVM.class); AdminVM adminVM = new ViewModelProvider(LoginActivity.this).get(AdminVM.class);
adminVM.getAccount(account.instance, account.token, account.user_id).observe(LoginActivity.this, adminAccount -> { adminVM.getAccount(account.instance, account.token, account.user_id).observe(LoginActivity.this, adminAccount -> {
account.admin = adminAccount != null; account.admin = adminAccount != null;
WebviewConnectActivity.proceedLogin(LoginActivity.this, account); proceedLogin(LoginActivity.this, account);
}); });
} else { } else {
WebviewConnectActivity.proceedLogin(LoginActivity.this, account); proceedLogin(LoginActivity.this, account);
}
});
});
} }
} else {
Toasty.error(LoginActivity.this, getString(R.string.toast_token), Toast.LENGTH_LONG).show();
} }
});
} else {
Toasty.error(LoginActivity.this, getString(R.string.toast_token), Toast.LENGTH_LONG).show();
}
});
}
}
@Override @Override
protected void onNewIntent(Intent intent) { protected void onNewIntent(Intent intent) {
@ -104,24 +149,18 @@ public class LoginActivity extends BaseActivity {
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
ThemeHelper.applyTheme(this);
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(LoginActivity.this); SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(LoginActivity.this);
setContentView(new FrameLayout(this)); setContentView(new FrameLayout(this));
FragmentLoginMain fragmentLoginMain = new FragmentLoginMain(); FragmentLoginMain fragmentLoginMain = new FragmentLoginMain();
Helper.addFragment(getSupportFragmentManager(), android.R.id.content, fragmentLoginMain, null, null, null); Helper.addFragment(getSupportFragmentManager(), android.R.id.content, fragmentLoginMain, null, null, null);
requestedAdmin = false;
//The activity handles a redirect URI, it will extract token code and will proceed to authentication //The activity handles a redirect URI, it will extract token code and will proceed to authentication
//That happens when the user wants to use an external browser //That happens when the user wants to use an external browser
manageItent(getIntent()); manageItent(getIntent());
} }
public boolean requestedAdmin() {
return requestedAdmin;
}
public boolean setAdmin(boolean askAdmin) {
return requestedAdmin = askAdmin;
}
@Override @Override
protected void onResume() { protected void onResume() {
@ -133,9 +172,6 @@ public class LoginActivity extends BaseActivity {
public boolean onCreateOptionsMenu(@NotNull Menu menu) { public boolean onCreateOptionsMenu(@NotNull Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present. // Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main_login, menu); getMenuInflater().inflate(R.menu.main_login, menu);
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(LoginActivity.this);
boolean embedded_browser = sharedpreferences.getBoolean(getString(R.string.SET_EMBEDDED_BROWSER), true);
menu.findItem(R.id.action_custom_tabs).setChecked(!embedded_browser);
return true; return true;
} }
@ -145,17 +181,8 @@ public class LoginActivity extends BaseActivity {
// automatically handle clicks on the Home/Up button, so long // automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml. // as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId(); int id = item.getItemId();
if (id == R.id.action_proxy) { if (id == R.id.action_proxy) {
Intent intent = new Intent(LoginActivity.this, ProxyActivity.class); (new ProxyActivity()).show(getSupportFragmentManager(), null);
startActivity(intent);
} else if (id == R.id.action_custom_tabs) {
item.setChecked(!item.isChecked());
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(LoginActivity.this);
SharedPreferences.Editor editor = sharedpreferences.edit();
editor.putBoolean(getString(R.string.SET_EMBEDDED_BROWSER), !item.isChecked());
editor.apply();
return false;
} else if (id == R.id.action_request_admin) { } else if (id == R.id.action_request_admin) {
item.setChecked(!item.isChecked()); item.setChecked(!item.isChecked());
requestedAdmin = item.isChecked(); requestedAdmin = item.isChecked();
@ -164,20 +191,5 @@ public class LoginActivity extends BaseActivity {
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }
@Override
protected void onActivityResult(int requestCode, int resultCode,
Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == PICK_IMPORT && resultCode == RESULT_OK) {
if (data == null || data.getData() == null) {
Toasty.error(LoginActivity.this, getString(R.string.toot_select_file_error), Toast.LENGTH_LONG).show();
return;
}
// String filename = Helper.getFilePathFromURI(LoginActivity.this, data.getData());
// Sqlite.importDB(LoginActivity.this, filename);
} else {
Toasty.error(LoginActivity.this, getString(R.string.toot_select_file_error), Toast.LENGTH_LONG).show();
}
}
} }

View file

@ -1,56 +0,0 @@
package app.fedilab.android.activities
/* Copyright 2022 Thomas Schneider
*
* This file is a part of Fedilab
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */
import android.os.Bundle
import android.view.MenuItem
import androidx.navigation.findNavController
import androidx.navigation.ui.AppBarConfiguration
import androidx.navigation.ui.navigateUp
import androidx.navigation.ui.setupActionBarWithNavController
import app.fedilab.android.R
import app.fedilab.android.databinding.ActivitySettingsBinding
import app.fedilab.android.helper.ThemeHelper
class SettingsActivity : BaseActivity() {
private lateinit var binding: ActivitySettingsBinding
private lateinit var appBarConfiguration: AppBarConfiguration
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
ThemeHelper.applyThemeBar(this)
binding = ActivitySettingsBinding.inflate(layoutInflater)
setContentView(binding.root)
val navController = findNavController(R.id.fragment_container)
appBarConfiguration = AppBarConfiguration.Builder().build()
setupActionBarWithNavController(navController, appBarConfiguration)
}
override fun onSupportNavigateUp(): Boolean {
val navController = findNavController(R.id.fragment_container)
return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
val navController = findNavController(R.id.fragment_container)
if (item.itemId == android.R.id.home && navController.currentDestination?.id == R.id.FragmentSettingsCategories) {
finish()
}
return super.onOptionsItemSelected(item)
}
}

View file

@ -1,326 +0,0 @@
/* Copyright 2021 Thomas Schneider
*
* This file is a part of Fedilab
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */
package app.fedilab.android.activities;
import static app.fedilab.android.activities.LoginActivity.apiLogin;
import static app.fedilab.android.activities.LoginActivity.client_idLogin;
import static app.fedilab.android.activities.LoginActivity.client_secretLogin;
import static app.fedilab.android.activities.LoginActivity.currentInstanceLogin;
import static app.fedilab.android.activities.LoginActivity.softwareLogin;
import static app.fedilab.android.helper.Helper.PREF_USER_TOKEN;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.View;
import android.webkit.CookieManager;
import android.webkit.CookieSyncManager;
import android.webkit.WebChromeClient;
import android.webkit.WebResourceRequest;
import android.webkit.WebResourceResponse;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.ProgressBar;
import androidx.appcompat.app.AlertDialog;
import androidx.lifecycle.ViewModelProvider;
import androidx.preference.PreferenceManager;
import java.util.Iterator;
import java.util.Map;
import java.util.regex.Matcher;
import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R;
import app.fedilab.android.client.entities.app.Account;
import app.fedilab.android.databinding.ActivityWebviewConnectBinding;
import app.fedilab.android.exception.DBException;
import app.fedilab.android.helper.Helper;
import app.fedilab.android.helper.ThemeHelper;
import app.fedilab.android.viewmodel.mastodon.AccountsVM;
import app.fedilab.android.viewmodel.mastodon.AdminVM;
import app.fedilab.android.viewmodel.mastodon.OauthVM;
import es.dmoral.toasty.Toasty;
public class WebviewConnectActivity extends BaseActivity {
private ActivityWebviewConnectBinding binding;
private AlertDialog alert;
private String login_url;
private boolean requestedAdmin;
@SuppressWarnings("deprecation")
public static void clearCookies(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
CookieManager.getInstance().removeAllCookies(null);
CookieManager.getInstance().flush();
} else {
CookieSyncManager cookieSyncMngr = CookieSyncManager.createInstance(context);
cookieSyncMngr.startSync();
CookieManager cookieManager = CookieManager.getInstance();
cookieManager.removeAllCookie();
cookieManager.removeSessionCookie();
cookieSyncMngr.stopSync();
cookieSyncMngr.sync();
}
}
@SuppressLint("ApplySharedPref")
public static void proceedLogin(Activity activity, Account account) {
new Thread(() -> {
try {
//update the database
new Account(activity).insertOrUpdate(account);
Handler mainHandler = new Handler(Looper.getMainLooper());
BaseMainActivity.currentToken = account.token;
BaseMainActivity.currentUserID = account.user_id;
BaseMainActivity.api = Account.API.MASTODON;
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(activity);
SharedPreferences.Editor editor = sharedpreferences.edit();
editor.putString(PREF_USER_TOKEN, account.token);
editor.commit();
//The user is now authenticated, it will be redirected to MainActivity
Runnable myRunnable = () -> {
Intent mainActivity = new Intent(activity, MainActivity.class);
activity.startActivity(mainActivity);
activity.finish();
};
mainHandler.post(myRunnable);
} catch (DBException e) {
e.printStackTrace();
}
}).start();
}
@SuppressLint("SetJavaScriptEnabled")
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ThemeHelper.applyTheme(this);
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(WebviewConnectActivity.this);
binding = ActivityWebviewConnectBinding.inflate(getLayoutInflater());
View rootView = binding.getRoot();
setContentView(rootView);
Bundle b = getIntent().getExtras();
if (b != null) {
login_url = b.getString("login_url");
requestedAdmin = b.getBoolean("requestedAdmin", false);
}
if (login_url == null)
finish();
clearCookies(WebviewConnectActivity.this);
binding.webviewConnect.getSettings().setJavaScriptEnabled(true);
String user_agent = sharedpreferences.getString(getString(R.string.SET_CUSTOM_USER_AGENT), Helper.USER_AGENT);
binding.webviewConnect.getSettings().setUserAgentString(user_agent);
binding.webviewConnect.getSettings().setDomStorageEnabled(true);
CookieManager.getInstance().setAcceptThirdPartyCookies(binding.webviewConnect, true);
final ProgressBar pbar = findViewById(R.id.progress_bar);
binding.webviewConnect.setWebChromeClient(new WebChromeClient() {
@Override
public void onProgressChanged(WebView view, int progress) {
if (progress < 100 && pbar.getVisibility() == ProgressBar.GONE) {
pbar.setVisibility(ProgressBar.VISIBLE);
}
pbar.setProgress(progress);
if (progress == 100) {
pbar.setVisibility(ProgressBar.GONE);
}
}
});
binding.webviewConnect.setWebViewClient(new WebViewClient() {
/* @Override
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
String x_xsrf_token = null;
String x_csrf_token = null;
if (request.getUrl().toString().contains("accounts/verify_credentials")) {
String cookies = CookieManager.getInstance().getCookie(request.getUrl().toString());
Map<String, String> requestHeaders = request.getRequestHeaders();
Iterator<Map.Entry<String, String>> it = requestHeaders.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, String> pair = it.next();
if (pair.getKey().compareTo("X-XSRF-TOKEN") == 0) {
x_xsrf_token = pair.getValue();
}
if (pair.getKey().compareTo("X-CSRF-TOKEN") == 0) {
x_csrf_token = pair.getValue();
}
it.remove();
}
if (x_xsrf_token != null && x_csrf_token != null) {
String finalX_xsrf_token = x_xsrf_token;
String finalX_csrf_token = x_csrf_token;
new Handler(Looper.getMainLooper()).post(() -> {
view.stopLoading();
SharedPreferences sharedpreferences1 = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedpreferences1.edit();
String token = "X-XSRF-TOKEN= " + finalX_xsrf_token + ";X-CSRF-TOKEN= " + finalX_csrf_token + "|" + cookies;
editor.putString(Helper.PREF_KEY_OAUTH_TOKEN, token);
editor.commit();
view.setVisibility(View.GONE);
//Update the account with the token;
new UpdateAccountInfoAsyncTask(WebviewConnectActivity.this, token, clientId, clientSecret, null, instance, social);
});
}
}
return super.shouldInterceptRequest(view, request);
}*/
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
if (request.getUrl().toString().contains(currentInstanceLogin + "/api/v1")) {
request.getRequestHeaders();
Map<String, String> requestHeaders = request.getRequestHeaders();
Iterator<Map.Entry<String, String>> it = requestHeaders.entrySet().iterator();
String token = null;
while (it.hasNext()) {
Map.Entry<String, String> pair = it.next();
if (pair.getKey().equals("Authorization")) {
token = pair.getValue();
break;
}
it.remove();
}
if (token != null) {
AccountsVM accountsVM = new ViewModelProvider(WebviewConnectActivity.this).get(AccountsVM.class);
String finalToken = token;
Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> {
accountsVM.getConnectedAccount(currentInstanceLogin, finalToken).observe(WebviewConnectActivity.this, mastodonAccount -> {
if (mastodonAccount != null) {
Account account = new Account();
account.client_id = client_idLogin;
account.client_secret = client_secretLogin;
account.token = finalToken;
account.api = apiLogin;
account.software = softwareLogin;
account.instance = currentInstanceLogin;
account.mastodon_account = mastodonAccount;
account.user_id = mastodonAccount.id;
//We check if user have really moderator rights
if (requestedAdmin) {
AdminVM adminVM = new ViewModelProvider(WebviewConnectActivity.this).get(AdminVM.class);
adminVM.getAccount(account.instance, account.token, account.user_id).observe(WebviewConnectActivity.this, adminAccount -> {
account.admin = adminAccount != null;
proceedLogin(WebviewConnectActivity.this, account);
});
} else {
proceedLogin(WebviewConnectActivity.this, account);
}
} else {
Toasty.error(WebviewConnectActivity.this, getString(R.string.toast_error), Toasty.LENGTH_SHORT).show();
}
});
};
mainHandler.post(myRunnable);
}
}
return super.shouldInterceptRequest(view, request);
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
super.shouldOverrideUrlLoading(view, url);
if (url.contains(Helper.REDIRECT_CONTENT_WEB)) {
Matcher matcher = Helper.codePattern.matcher(url);
if (!matcher.find()) {
return false;
}
String code = matcher.group(1);
OauthVM oauthVM = new ViewModelProvider(WebviewConnectActivity.this).get(OauthVM.class);
//API call to get the user token
String scope = requestedAdmin ? Helper.OAUTH_SCOPES_ADMIN : Helper.OAUTH_SCOPES;
oauthVM.createToken(currentInstanceLogin, "authorization_code", client_idLogin, client_secretLogin, Helper.REDIRECT_CONTENT_WEB, scope, code)
.observe(WebviewConnectActivity.this, tokenObj -> {
if (tokenObj != null) {
Account account = new Account();
account.client_id = client_idLogin;
account.client_secret = client_secretLogin;
account.token = tokenObj.token_type + " " + tokenObj.access_token;
account.api = apiLogin;
account.software = softwareLogin;
account.instance = currentInstanceLogin;
//API call to retrieve account information for the new token
AccountsVM accountsVM = new ViewModelProvider(WebviewConnectActivity.this).get(AccountsVM.class);
accountsVM.getConnectedAccount(currentInstanceLogin, account.token).observe(WebviewConnectActivity.this, mastodonAccount -> {
if (mastodonAccount != null) {
account.mastodon_account = mastodonAccount;
account.user_id = mastodonAccount.id;
//We check if user have really moderator rights
if (requestedAdmin) {
AdminVM adminVM = new ViewModelProvider(WebviewConnectActivity.this).get(AdminVM.class);
adminVM.getAccount(account.instance, account.token, account.user_id).observe(WebviewConnectActivity.this, adminAccount -> {
account.admin = adminAccount != null;
proceedLogin(WebviewConnectActivity.this, account);
});
} else {
proceedLogin(WebviewConnectActivity.this, account);
}
} else {
Toasty.error(WebviewConnectActivity.this, getString(R.string.toast_error), Toasty.LENGTH_SHORT).show();
}
});
} else {
Toasty.error(WebviewConnectActivity.this, getString(R.string.toast_token), Toasty.LENGTH_SHORT).show();
}
});
return true;
} else {
return false;
}
}
});
binding.webviewConnect.loadUrl(login_url);
}
@Override
public void onBackPressed() {
if (binding.webviewConnect.canGoBack()) {
binding.webviewConnect.goBack();
} else {
super.onBackPressed();
}
}
@Override
public void onDestroy() {
super.onDestroy();
if (alert != null) {
alert.dismiss();
alert = null;
}
binding.webviewConnect.destroy();
}
}

View file

@ -1,132 +0,0 @@
package app.fedilab.android.client.entities.app;
import android.content.ContentValues;
import android.content.Context;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import androidx.preference.PreferenceManager;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.HttpsURLConnection;
import app.fedilab.android.exception.DBException;
import app.fedilab.android.helper.Helper;
import app.fedilab.android.sqlite.Sqlite;
public class DomainsBlock {
public static final String LAST_DATE_OF_UPDATE = "LAST_DATE_OF_UPDATE";
public static List<String> trackingDomains = null;
private static void getDomains(Context context) {
if (trackingDomains == null) {
try {
SQLiteDatabase db = Sqlite.getInstance(context.getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
Cursor c = db.query(Sqlite.TABLE_DOMAINS_TRACKING, null, null, null, null, null, null, null);
trackingDomains = cursorToDomain(c);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void updateDomains(Context _mContext) {
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(_mContext);
String last_date = sharedpreferences.getString(LAST_DATE_OF_UPDATE, null);
Date dateUpdate = new Date(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(10));
Date dateLastUpdate = Helper.stringToDate(_mContext, last_date);
SQLiteDatabase db = Sqlite.getInstance(_mContext.getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
if (last_date == null || dateUpdate.after(dateLastUpdate)) {
new Thread(() -> {
try {
HttpsURLConnection connection = (HttpsURLConnection) new URL("https://hosts.fedilab.app/hosts").openConnection();
if (connection.getResponseCode() > HttpsURLConnection.HTTP_MOVED_TEMP) {
return;
}
BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String line;
List<String> domains = new ArrayList<>();
while ((line = br.readLine()) != null) {
if (line.startsWith("0.0.0.0 ")) {
try {
domains.add(line.replace("0.0.0.0 ", "").trim());
} catch (Exception e) {
return;
}
}
}
br.close();
connection.disconnect();
insertDomains(db, domains);
SharedPreferences.Editor editor = sharedpreferences.edit();
editor.putString(LAST_DATE_OF_UPDATE, Helper.dateToString(new Date()));
editor.apply();
} catch (IOException | DBException e) {
e.printStackTrace();
}
}).start();
} else {
getDomains(_mContext);
}
}
/**
* Insert a domains in db
*
* @param domains {@link List<String>}
* @throws DBException exception with database
*/
private static void insertDomains(SQLiteDatabase db, List<String> domains) throws DBException {
if (db == null) {
throw new DBException("db is null. Wrong initialization.");
}
db.delete(Sqlite.TABLE_DOMAINS_TRACKING, null, null);
DomainsBlock.trackingDomains = new ArrayList<>();
for (String domain : domains) {
ContentValues values = new ContentValues();
values.put(Sqlite.COL_DOMAIN, domain);
//Inserts token
try {
db.insertOrThrow(Sqlite.TABLE_DOMAINS_TRACKING, null, values);
} catch (Exception e) {
e.printStackTrace();
}
DomainsBlock.trackingDomains.add(domain);
}
}
/***
* Method to hydrate domain from database
* @param c Cursor
* @return List<String>
*/
private static List<String> cursorToDomain(Cursor c) {
//No element found
if (c.getCount() == 0) {
c.close();
return null;
}
List<String> domains = new ArrayList<>();
while (c.moveToNext()) {
domains.add(c.getString(c.getColumnIndexOrThrow(Sqlite.COL_DOMAIN)));
}
//Close the cursor
c.close();
//domains list is returned
return domains;
}
}

View file

@ -1,123 +0,0 @@
package app.fedilab.android.helper;
/* Copyright 2021 Thomas Schneider
*
* This file is a part of Fedilab
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.core.content.res.ResourcesCompat;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
import app.fedilab.android.R;
import app.fedilab.android.client.entities.api.Status;
import app.fedilab.android.ui.drawer.ComposeAdapter;
public class DividerDecorationSimple extends RecyclerView.ItemDecoration {
private final Context _mContext;
private final List<Status> statusList;
public DividerDecorationSimple(Context context, List<Status> statuses) {
_mContext = context;
statusList = statuses;
}
@Override
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
int position = ((RecyclerView.LayoutParams) view.getLayoutParams()).getViewAdapterPosition();
ComposeAdapter composeAdapter = ((ComposeAdapter) parent.getAdapter());
if (composeAdapter != null && composeAdapter.getItemCount() > position && position >= 0) {
Status status = composeAdapter.getItem(position);
if (status != null) {
int start = (int) Helper.convertDpToPixel(
4 * CommentDecorationHelper.getIndentation(status.in_reply_to_id, statusList, 15),
_mContext);
if (parent.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR) {
outRect.set(start, 0, 0, 0);
} else {
outRect.set(0, 0, start, 0);
}
}
}
}
@Override
public void onDraw(@NonNull Canvas c, RecyclerView parent, @NonNull RecyclerView.State state) {
for (int i = 0; i < parent.getChildCount(); i++) {
View view = parent.getChildAt(i);
int position = parent.getChildAdapterPosition(view);
ComposeAdapter composeAdapter = ((ComposeAdapter) parent.getAdapter());
if (composeAdapter != null && composeAdapter.getItemCount() > position && position >= 0) {
Status status = composeAdapter.getItem(position);
if (status != null) {
int indentation = CommentDecorationHelper.getIndentation(status.in_reply_to_id, statusList, 15);
if (indentation > 0) {
Paint paint = new Paint();
paint.setDither(false);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(Helper.convertDpToPixel(2F, _mContext));
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setColor(ResourcesCompat.getColor(_mContext.getResources(), R.color.cyanea_accent, _mContext.getTheme()));
if (indentation == 15) {
paint.setPathEffect(new DashPathEffect(
new float[]{Helper.convertDpToPixel(3, _mContext), Helper.convertDpToPixel(3, _mContext)},
0));
}
float startDp = 12 + 4 * (indentation - 1);
if (i > 0) startDp = startDp - 6;
float endDp = startDp + 4;
if (i > 0) endDp = endDp + 4;
float startPx = Helper.convertDpToPixel(startDp, _mContext);
if (parent.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
startPx = c.getWidth() - startPx;
}
float topPx = view.getTop() - Helper.convertDpToPixel(12, _mContext);
if (i > 0) {
View aboveView = parent.getChildAt(i - 1);
topPx = topPx - (aboveView.getHeight() / 2F);
}
float bottomPx = view.getBottom() - view.getHeight() / 2F;
float endPx = Helper.convertDpToPixel(endDp, _mContext);
Path path = new Path();
path.moveTo(startPx, topPx);
path.lineTo(startPx, bottomPx);
path.lineTo(endPx, bottomPx);
c.drawPath(path, paint);
}
}
}
}
}
}

View file

@ -1,134 +0,0 @@
package app.fedilab.android.helper;
/* Copyright 2022 Thomas Schneider
*
* This file is a part of Fedilab
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */
import static app.fedilab.android.BaseMainActivity.currentAccount;
import static app.fedilab.android.helper.LogoHelper.getMainLogo;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Environment;
import androidx.preference.PreferenceManager;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Date;
import java.util.Map;
import app.fedilab.android.R;
//From https://stackoverflow.com/a/10864463
public class SettingsStorage {
public static boolean saveSharedPreferencesToFile(Context context) {
boolean res = false;
ObjectOutputStream output = null;
String fileName = "Fedilab_settings_export_" + Helper.dateFileToString(context, new Date()) + ".txt";
String filePath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath();
String fullPath = filePath + "/" + fileName;
File dst = new File(fullPath);
try {
output = new ObjectOutputStream(new FileOutputStream(dst));
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context);
output.writeObject(sharedpreferences.getAll());
res = true;
String message = context.getString(R.string.data_export_settings_success);
Intent intentOpen = new Intent();
intentOpen.setAction(android.content.Intent.ACTION_VIEW);
Uri uri = Uri.parse("file://" + fullPath);
intentOpen.setDataAndType(uri, "text/txt");
String title = context.getString(R.string.data_export_settings);
Helper.notify_user(context, currentAccount, intentOpen, BitmapFactory.decodeResource(context.getResources(),
getMainLogo(context)), Helper.NotifType.BACKUP, title, message);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (output != null) {
output.flush();
output.close();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
return res;
}
@SuppressLint("ApplySharedPref")
@SuppressWarnings({"unchecked", "UnnecessaryUnboxing"})
public static boolean loadSharedPreferencesFromFile(Context context, Uri srcUri) {
boolean res = false;
ObjectInputStream input = null;
try {
input = new ObjectInputStream(context.getContentResolver().openInputStream(srcUri));
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context);
SharedPreferences.Editor prefEdit = sharedpreferences.edit();
prefEdit.clear();
Map<String, ?> entries = (Map<String, ?>) input.readObject();
for (Map.Entry<String, ?> entry : entries.entrySet()) {
Object v = entry.getValue();
String key = entry.getKey();
//We skip some values
if (key.compareTo(Helper.PREF_USER_ID) == 0) {
continue;
}
if (key.compareTo(Helper.PREF_INSTANCE) == 0) {
continue;
}
if (key.compareTo(Helper.PREF_USER_INSTANCE) == 0) {
continue;
}
if (key.compareTo(Helper.PREF_USER_TOKEN) == 0) {
continue;
}
if (v instanceof Boolean)
prefEdit.putBoolean(key, ((Boolean) v).booleanValue());
else if (v instanceof Float)
prefEdit.putFloat(key, ((Float) v).floatValue());
else if (v instanceof Integer)
prefEdit.putInt(key, ((Integer) v).intValue());
else if (v instanceof Long)
prefEdit.putLong(key, ((Long) v).longValue());
else if (v instanceof String)
prefEdit.putString(key, ((String) v));
}
prefEdit.commit();
res = true;
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
} finally {
try {
if (input != null) {
input.close();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
return res;
}
}

View file

@ -1,952 +0,0 @@
package app.fedilab.android.helper;
/* Copyright 2021 Thomas Schneider
*
* This file is a part of Fedilab
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */
import static app.fedilab.android.BaseMainActivity.currentAccount;
import static app.fedilab.android.helper.Helper.USER_AGENT;
import static app.fedilab.android.helper.Helper.urlPattern;
import static app.fedilab.android.helper.ThemeHelper.linkColor;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.text.Html;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextPaint;
import android.text.style.ClickableSpan;
import android.text.style.QuoteSpan;
import android.text.style.URLSpan;
import android.view.LayoutInflater;
import android.view.View;
import android.webkit.URLUtil;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.core.content.ContextCompat;
import androidx.preference.PreferenceManager;
import com.bumptech.glide.Glide;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.net.ssl.HttpsURLConnection;
import app.fedilab.android.R;
import app.fedilab.android.activities.ContextActivity;
import app.fedilab.android.activities.HashTagActivity;
import app.fedilab.android.activities.ProfileActivity;
import app.fedilab.android.client.entities.api.Account;
import app.fedilab.android.client.entities.api.Announcement;
import app.fedilab.android.client.entities.api.Attachment;
import app.fedilab.android.client.entities.api.Emoji;
import app.fedilab.android.client.entities.api.Mention;
import app.fedilab.android.client.entities.api.Status;
import app.fedilab.android.databinding.PopupLinksBinding;
import es.dmoral.toasty.Toasty;
public class SpannableHelper {
public static final String CLICKABLE_SPAN = "CLICKABLE_SPAN";
public static Spannable convert(Context context, String text,
Status status, Account account, Announcement announcement,
boolean convertHtml,
WeakReference<View> viewWeakReference) {
SpannableString initialContent;
if (text == null) {
return null;
}
text = text.replaceAll("((<\\s?p\\s?>|<\\s?br\\s?\\/?>)&gt;(((?!([<])).)*))", "$2<blockquote>$3</blockquote>");
Pattern imgPattern = Pattern.compile("<img [^>]*src=\"([^\"]+)\"[^>]*>");
Matcher matcherImg = imgPattern.matcher(text);
HashMap<String, String> imagesToReplace = new LinkedHashMap<>();
int inc = 0;
while (matcherImg.find()) {
String replacement = "[FEDI_IMG_" + inc + "]";
imagesToReplace.put(replacement, matcherImg.group(1));
inc++;
text = text.replaceAll(Pattern.quote(matcherImg.group()), replacement);
}
SpannableStringBuilder content;
View view = viewWeakReference.get();
List<Mention> mentionList = null;
List<Emoji> emojiList = null;
if (status != null) {
mentionList = status.mentions;
emojiList = status.emojis;
} else if (account != null) {
emojiList = account.emojis;
} else if (announcement != null) {
emojiList = announcement.emojis;
}
HashMap<String, String> urlDetails = new HashMap<>();
if (convertHtml) {
Matcher matcherALink = Helper.aLink.matcher(text);
//We stock details
while (matcherALink.find()) {
String urlText = matcherALink.group(3);
String url = matcherALink.group(2);
if (urlText != null && urlText.startsWith(">")) {
urlText = urlText.substring(1);
}
if (url != null && urlText != null && !url.equals(urlText) && !urlText.contains("<span")) {
urlDetails.put(url, urlText);
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
initialContent = new SpannableString(Html.fromHtml(text, Html.FROM_HTML_MODE_LEGACY));
else
initialContent = new SpannableString(Html.fromHtml(text));
content = new SpannableStringBuilder(initialContent);
URLSpan[] urls = content.getSpans(0, (content.length() - 1), URLSpan.class);
for (URLSpan span : urls) {
content.removeSpan(span);
}
//Make tags, mentions, groups
interaction(context, content, mentionList);
//Make all links
linkify(context, content, urlDetails);
linkifyURL(context, content, urlDetails);
emails(context, content);
replaceQuoteSpans(context, content);
} else {
content = new SpannableStringBuilder(text);
}
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context);
boolean animate = !sharedpreferences.getBoolean(context.getString(R.string.SET_DISABLE_ANIMATED_EMOJI), false);
if (emojiList != null && emojiList.size() > 0) {
for (Emoji emoji : emojiList) {
Matcher matcher = Pattern.compile(":" + emoji.shortcode + ":", Pattern.LITERAL)
.matcher(content);
while (matcher.find()) {
CustomEmoji customEmoji = new CustomEmoji(new WeakReference<>(view));
content.setSpan(customEmoji, matcher.start(), matcher.end(), 0);
Glide.with(view)
.asDrawable()
.load(animate ? emoji.url : emoji.static_url)
.into(customEmoji.getTarget(animate));
}
}
}
if (imagesToReplace.size() > 0) {
for (Map.Entry<String, String> entry : imagesToReplace.entrySet()) {
String key = entry.getKey();
String url = entry.getValue();
Matcher matcher = Pattern.compile(key, Pattern.LITERAL)
.matcher(content);
while (matcher.find()) {
CustomEmoji customEmoji = new CustomEmoji(new WeakReference<>(view));
content.setSpan(customEmoji, matcher.start(), matcher.end(), 0);
Glide.with(view)
.asDrawable()
.load(url)
.into(customEmoji.getTarget(animate));
}
}
}
return trimSpannable(new SpannableStringBuilder(content));
}
private static void linkify(Context context, SpannableStringBuilder content, HashMap<String, String> urlDetails) {
//--- URLs ----
Matcher matcherLink = urlPattern.matcher(content);
int offSetTruncate = 0;
while (matcherLink.find()) {
int matchStart = matcherLink.start() - offSetTruncate;
int matchEnd = matchStart + matcherLink.group().length();
if (matchEnd > content.toString().length()) {
matchEnd = content.toString().length();
}
if (content.toString().length() < matchEnd || matchStart < 0 || matchStart > matchEnd) {
continue;
}
final String url = content.toString().substring(matchStart, matchEnd);
String newURL = Helper.transformURL(context, url);
//If URL has been transformed
if (newURL.compareTo(url) != 0) {
content.replace(matchStart, matchEnd, newURL);
offSetTruncate -= (newURL.length() - url.length());
matchEnd = matchStart + newURL.length();
//The transformed URL was in the list of URLs having a different names
if (urlDetails.containsKey(url)) {
urlDetails.put(newURL, urlDetails.get(url));
}
}
//Truncate URL if needed
//TODO: add an option to disable truncated URLs
String urlText = newURL;
if (newURL.length() > 30 && !urlDetails.containsKey(urlText)) {
urlText = urlText.substring(0, 30);
urlText += "";
content.replace(matchStart, matchEnd, urlText);
matchEnd = matchStart + 31;
offSetTruncate += (newURL.length() - urlText.length());
} /*else if (urlDetails.containsKey(urlText) && urlDetails.get(urlText) != null) {
urlText = urlDetails.get(urlText);
if (urlText != null) {
content.replace(matchStart, matchEnd, urlText);
matchEnd = matchStart + urlText.length();
offSetTruncate += (newURL.length() - urlText.length());
}
}*/
if (matchEnd <= content.length() && matchEnd >= matchStart) {
content.setSpan(new LongClickableSpan() {
@Override
public void onLongClick(View view) {
Context mContext = view.getContext();
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(mContext, Helper.dialogStyle());
PopupLinksBinding popupLinksBinding = PopupLinksBinding.inflate(LayoutInflater.from(context));
dialogBuilder.setView(popupLinksBinding.getRoot());
AlertDialog alertDialog = dialogBuilder.create();
alertDialog.show();
String finalURl = url;
String uniqueUrl = url.endsWith("") ? url : url + "";
if (urlDetails.containsValue(uniqueUrl)) {
finalURl = Helper.getKeyByValue(urlDetails, uniqueUrl);
}
String finalURl1 = finalURl;
popupLinksBinding.displayFullLink.setOnClickListener(v -> {
AlertDialog.Builder builder = new AlertDialog.Builder(mContext, Helper.dialogStyle());
builder.setMessage(finalURl1);
builder.setTitle(context.getString(R.string.display_full_link));
builder.setPositiveButton(R.string.close, (dialog, which) -> dialog.dismiss())
.show();
alertDialog.dismiss();
});
popupLinksBinding.shareLink.setOnClickListener(v -> {
Intent sendIntent = new Intent(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_SUBJECT, context.getString(R.string.shared_via));
sendIntent.putExtra(Intent.EXTRA_TEXT, finalURl1);
sendIntent.setType("text/plain");
sendIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Intent intentChooser = Intent.createChooser(sendIntent, context.getString(R.string.share_with));
intentChooser.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intentChooser);
alertDialog.dismiss();
});
popupLinksBinding.openOtherApp.setOnClickListener(v -> {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(finalURl1));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
context.startActivity(intent);
} catch (Exception e) {
Toasty.error(context, context.getString(R.string.toast_error), Toast.LENGTH_LONG).show();
}
alertDialog.dismiss();
});
popupLinksBinding.copyLink.setOnClickListener(v -> {
ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText(Helper.CLIP_BOARD, finalURl1);
if (clipboard != null) {
clipboard.setPrimaryClip(clip);
Toasty.info(context, context.getString(R.string.clipboard_url), Toast.LENGTH_LONG).show();
}
alertDialog.dismiss();
});
popupLinksBinding.checkRedirect.setOnClickListener(v -> {
try {
URL finalUrlCheck = new URL(finalURl1);
new Thread(() -> {
try {
String redirect = null;
HttpsURLConnection httpsURLConnection = (HttpsURLConnection) finalUrlCheck.openConnection();
httpsURLConnection.setConnectTimeout(10 * 1000);
httpsURLConnection.setRequestProperty("http.keepAlive", "false");
httpsURLConnection.setRequestProperty("User-Agent", USER_AGENT);
httpsURLConnection.setRequestMethod("HEAD");
httpsURLConnection.setInstanceFollowRedirects(false);
if (httpsURLConnection.getResponseCode() == 301 || httpsURLConnection.getResponseCode() == 302) {
Map<String, List<String>> map = httpsURLConnection.getHeaderFields();
for (Map.Entry<String, List<String>> entry : map.entrySet()) {
if (entry.toString().toLowerCase().startsWith("location")) {
Matcher matcher = urlPattern.matcher(entry.toString());
if (matcher.find()) {
redirect = matcher.group(1);
}
}
}
}
httpsURLConnection.getInputStream().close();
if (redirect != null && finalURl1 != null && redirect.compareTo(finalURl1) != 0) {
URL redirectURL = new URL(redirect);
String host = redirectURL.getHost();
String protocol = redirectURL.getProtocol();
if (protocol == null || host == null) {
redirect = null;
}
}
Handler mainHandler = new Handler(context.getMainLooper());
String finalRedirect = redirect;
Runnable myRunnable = () -> {
AlertDialog.Builder builder1 = new AlertDialog.Builder(view.getContext(), Helper.dialogStyle());
if (finalRedirect != null) {
builder1.setMessage(context.getString(R.string.redirect_detected, finalURl1, finalRedirect));
builder1.setNegativeButton(R.string.copy_link, (dialog, which) -> {
ClipboardManager clipboard1 = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
ClipData clip1 = ClipData.newPlainText(Helper.CLIP_BOARD, finalRedirect);
if (clipboard1 != null) {
clipboard1.setPrimaryClip(clip1);
Toasty.info(context, context.getString(R.string.clipboard_url), Toast.LENGTH_LONG).show();
}
dialog.dismiss();
});
builder1.setNeutralButton(R.string.share_link, (dialog, which) -> {
Intent sendIntent1 = new Intent(Intent.ACTION_SEND);
sendIntent1.putExtra(Intent.EXTRA_SUBJECT, context.getString(R.string.shared_via));
sendIntent1.putExtra(Intent.EXTRA_TEXT, finalURl1);
sendIntent1.setType("text/plain");
context.startActivity(Intent.createChooser(sendIntent1, context.getString(R.string.share_with)));
dialog.dismiss();
});
} else {
builder1.setMessage(R.string.no_redirect);
}
builder1.setTitle(context.getString(R.string.check_redirect));
builder1.setPositiveButton(R.string.close, (dialog, which) -> dialog.dismiss())
.show();
};
mainHandler.post(myRunnable);
} catch (IOException e) {
e.printStackTrace();
}
}).start();
} catch (MalformedURLException e) {
e.printStackTrace();
}
alertDialog.dismiss();
});
}
@Override
public void onClick(@NonNull View textView) {
String finalURl = newURL;
String finalURl2 = url;
String uniqueNewURL = newURL.endsWith("") ? newURL : newURL + "";
if (urlDetails.containsValue(uniqueNewURL)) {
finalURl = Helper.getKeyByValue(urlDetails, uniqueNewURL);
}
String uniqueUrl = url.endsWith("") ? url : url + "";
if (urlDetails.containsValue(uniqueUrl)) {
finalURl2 = Helper.getKeyByValue(urlDetails, uniqueUrl);
}
textView.setTag(CLICKABLE_SPAN);
Pattern link = Pattern.compile("https?://([\\da-z.-]+\\.[a-z.]{2,10})/(@[\\w._-]*[0-9]*)(/[0-9]+)?$");
Matcher matcherLink = null;
if (finalURl2 != null) {
matcherLink = link.matcher(finalURl2);
}
if (finalURl2 != null && matcherLink.find() && !finalURl2.contains("medium.com")) {
if (matcherLink.group(3) != null && Objects.requireNonNull(matcherLink.group(3)).length() > 0) { //It's a toot
CrossActionHelper.fetchRemoteStatus(context, currentAccount, finalURl2, new CrossActionHelper.Callback() {
@Override
public void federatedStatus(Status status) {
Intent intent = new Intent(context, ContextActivity.class);
intent.putExtra(Helper.ARG_STATUS, status);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
@Override
public void federatedAccount(Account account) {
}
});
} else {//It's an account
CrossActionHelper.fetchRemoteAccount(context, currentAccount, matcherLink.group(2) + "@" + matcherLink.group(1), new CrossActionHelper.Callback() {
@Override
public void federatedStatus(Status status) {
}
@Override
public void federatedAccount(Account account) {
Intent intent = new Intent(context, ProfileActivity.class);
Bundle b = new Bundle();
b.putSerializable(Helper.ARG_ACCOUNT, account);
intent.putExtras(b);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
});
}
} else {
Helper.openBrowser(context, finalURl);
}
}
@Override
public void updateDrawState(@NonNull TextPaint ds) {
super.updateDrawState(ds);
ds.setUnderlineText(false);
ds.setColor(linkColor);
}
}, matchStart, matchEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
}
}
}
private static void linkifyURL(Context context, SpannableStringBuilder content, HashMap<String, String> urlDetails) {
for (Map.Entry<String, String> entry : urlDetails.entrySet()) {
String value = entry.getValue();
if (value.startsWith("@") || value.startsWith("#") || !URLUtil.isValidUrl(value)) {
continue;
}
SpannableString contentUrl;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
contentUrl = new SpannableString(Html.fromHtml(value, Html.FROM_HTML_MODE_LEGACY));
else
contentUrl = new SpannableString(Html.fromHtml(value));
Pattern word = Pattern.compile(contentUrl.toString());
Matcher matcherLink = word.matcher(content);
while (matcherLink.find()) {
String url = entry.getKey();
int matchStart = matcherLink.start();
int matchEnd = matchStart + matcherLink.group().length();
if (matchEnd > content.toString().length()) {
matchEnd = content.toString().length();
}
if (content.toString().length() < matchEnd || matchStart < 0 || matchStart > matchEnd) {
continue;
}
if (matchEnd <= content.length()) {
content.setSpan(new LongClickableSpan() {
@Override
public void onLongClick(View view) {
Context mContext = view.getContext();
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(mContext, Helper.dialogStyle());
PopupLinksBinding popupLinksBinding = PopupLinksBinding.inflate(LayoutInflater.from(context));
dialogBuilder.setView(popupLinksBinding.getRoot());
AlertDialog alertDialog = dialogBuilder.create();
alertDialog.show();
String finalURl = url;
if (urlDetails.containsValue(url)) {
finalURl = Helper.getKeyByValue(urlDetails, url);
}
String finalURl1 = finalURl;
popupLinksBinding.displayFullLink.setOnClickListener(v -> {
AlertDialog.Builder builder = new AlertDialog.Builder(mContext, Helper.dialogStyle());
builder.setMessage(finalURl1);
builder.setTitle(context.getString(R.string.display_full_link));
builder.setPositiveButton(R.string.close, (dialog, which) -> dialog.dismiss())
.show();
alertDialog.dismiss();
});
popupLinksBinding.shareLink.setOnClickListener(v -> {
Intent sendIntent = new Intent(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_SUBJECT, context.getString(R.string.shared_via));
sendIntent.putExtra(Intent.EXTRA_TEXT, finalURl1);
sendIntent.setType("text/plain");
sendIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Intent intentChooser = Intent.createChooser(sendIntent, context.getString(R.string.share_with));
intentChooser.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intentChooser);
alertDialog.dismiss();
});
popupLinksBinding.openOtherApp.setOnClickListener(v -> {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(finalURl1));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
context.startActivity(intent);
} catch (Exception e) {
Toasty.error(context, context.getString(R.string.toast_error), Toast.LENGTH_LONG).show();
}
alertDialog.dismiss();
});
popupLinksBinding.copyLink.setOnClickListener(v -> {
ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText(Helper.CLIP_BOARD, finalURl1);
if (clipboard != null) {
clipboard.setPrimaryClip(clip);
Toasty.info(context, context.getString(R.string.clipboard_url), Toast.LENGTH_LONG).show();
}
alertDialog.dismiss();
});
popupLinksBinding.checkRedirect.setOnClickListener(v -> {
try {
URL finalUrlCheck = new URL(finalURl1);
new Thread(() -> {
try {
String redirect = null;
HttpsURLConnection httpsURLConnection = (HttpsURLConnection) finalUrlCheck.openConnection();
httpsURLConnection.setConnectTimeout(10 * 1000);
httpsURLConnection.setRequestProperty("http.keepAlive", "false");
httpsURLConnection.setRequestProperty("User-Agent", USER_AGENT);
httpsURLConnection.setRequestMethod("HEAD");
httpsURLConnection.setInstanceFollowRedirects(false);
if (httpsURLConnection.getResponseCode() == 301 || httpsURLConnection.getResponseCode() == 302) {
Map<String, List<String>> map = httpsURLConnection.getHeaderFields();
for (Map.Entry<String, List<String>> entry : map.entrySet()) {
if (entry.toString().toLowerCase().startsWith("location")) {
Matcher matcher = urlPattern.matcher(entry.toString());
if (matcher.find()) {
redirect = matcher.group(1);
}
}
}
}
httpsURLConnection.getInputStream().close();
if (redirect != null && finalURl1 != null && redirect.compareTo(finalURl1) != 0) {
URL redirectURL = new URL(redirect);
String host = redirectURL.getHost();
String protocol = redirectURL.getProtocol();
if (protocol == null || host == null) {
redirect = null;
}
}
Handler mainHandler = new Handler(context.getMainLooper());
String finalRedirect = redirect;
Runnable myRunnable = () -> {
AlertDialog.Builder builder1 = new AlertDialog.Builder(view.getContext(), Helper.dialogStyle());
if (finalRedirect != null) {
builder1.setMessage(context.getString(R.string.redirect_detected, finalURl1, finalRedirect));
builder1.setNegativeButton(R.string.copy_link, (dialog, which) -> {
ClipboardManager clipboard1 = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
ClipData clip1 = ClipData.newPlainText(Helper.CLIP_BOARD, finalRedirect);
if (clipboard1 != null) {
clipboard1.setPrimaryClip(clip1);
Toasty.info(context, context.getString(R.string.clipboard_url), Toast.LENGTH_LONG).show();
}
dialog.dismiss();
});
builder1.setNeutralButton(R.string.share_link, (dialog, which) -> {
Intent sendIntent1 = new Intent(Intent.ACTION_SEND);
sendIntent1.putExtra(Intent.EXTRA_SUBJECT, context.getString(R.string.shared_via));
sendIntent1.putExtra(Intent.EXTRA_TEXT, finalURl1);
sendIntent1.setType("text/plain");
context.startActivity(Intent.createChooser(sendIntent1, context.getString(R.string.share_with)));
dialog.dismiss();
});
} else {
builder1.setMessage(R.string.no_redirect);
}
builder1.setTitle(context.getString(R.string.check_redirect));
builder1.setPositiveButton(R.string.close, (dialog, which) -> dialog.dismiss())
.show();
};
mainHandler.post(myRunnable);
} catch (IOException e) {
e.printStackTrace();
}
}).start();
} catch (MalformedURLException e) {
e.printStackTrace();
}
alertDialog.dismiss();
});
}
@Override
public void onClick(@NonNull View textView) {
String finalURl = url;
if (urlDetails.containsValue(url)) {
finalURl = Helper.getKeyByValue(urlDetails, url);
}
textView.setTag(CLICKABLE_SPAN);
Pattern link = Pattern.compile("https?://([\\da-z.-]+\\.[a-z.]{2,10})/(@[\\w._-]*[0-9]*)(/[0-9]+)?$");
Matcher matcherLink = null;
if (finalURl != null) {
matcherLink = link.matcher(finalURl);
}
if (finalURl != null && matcherLink.find() && !finalURl.contains("medium.com")) {
if (matcherLink.group(3) != null && Objects.requireNonNull(matcherLink.group(3)).length() > 0) { //It's a toot
CrossActionHelper.fetchRemoteStatus(context, currentAccount, finalURl, new CrossActionHelper.Callback() {
@Override
public void federatedStatus(Status status) {
Intent intent = new Intent(context, ContextActivity.class);
intent.putExtra(Helper.ARG_STATUS, status);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
@Override
public void federatedAccount(Account account) {
}
});
} else {//It's an account
CrossActionHelper.fetchRemoteAccount(context, currentAccount, matcherLink.group(2) + "@" + matcherLink.group(1), new CrossActionHelper.Callback() {
@Override
public void federatedStatus(Status status) {
}
@Override
public void federatedAccount(Account account) {
Intent intent = new Intent(context, ProfileActivity.class);
Bundle b = new Bundle();
b.putSerializable(Helper.ARG_ACCOUNT, account);
intent.putExtras(b);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
});
}
} else {
Helper.openBrowser(context, finalURl);
}
}
@Override
public void updateDrawState(@NonNull TextPaint ds) {
super.updateDrawState(ds);
ds.setUnderlineText(false);
ds.setColor(linkColor);
}
}, matchStart, matchEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
}
}
}
}
private static void emails(Context context, Spannable content) {
// --- For all patterns defined in Helper class ---
Pattern pattern = Helper.emailPattern;
Matcher matcher = pattern.matcher(content);
while (matcher.find()) {
int matchStart = matcher.start();
int matchEnd = matcher.end();
String email = content.toString().substring(matchStart, matchEnd);
if (matchStart >= 0 && matchEnd <= content.toString().length() && matchEnd >= matchStart) {
ClickableSpan[] clickableSpans = content.getSpans(matchStart, matchEnd, ClickableSpan.class);
if (clickableSpans != null) {
for (ClickableSpan clickableSpan : clickableSpans) {
content.removeSpan(clickableSpan);
}
}
content.removeSpan(clickableSpans);
content.setSpan(new ClickableSpan() {
@Override
public void onClick(@NonNull View textView) {
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("plain/text");
intent.putExtra(Intent.EXTRA_EMAIL, new String[]{email});
context.startActivity(Intent.createChooser(intent, null));
}
@Override
public void updateDrawState(@NonNull TextPaint ds) {
super.updateDrawState(ds);
ds.setUnderlineText(false);
ds.setColor(linkColor);
}
}, matchStart, matchEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
}
}
}
private static void interaction(Context context, Spannable content, List<Mention> mentions) {
// --- For all patterns defined in Helper class ---
for (Map.Entry<Helper.PatternType, Pattern> entry : Helper.patternHashMap.entrySet()) {
Helper.PatternType patternType = entry.getKey();
Pattern pattern = entry.getValue();
Matcher matcher = pattern.matcher(content);
if (pattern == Helper.mentionPattern && mentions == null) {
continue;
} else if (pattern == Helper.mentionLongPattern && mentions == null) {
continue;
}
while (matcher.find()) {
int matchStart = matcher.start();
int matchEnd = matcher.end();
String word = content.toString().substring(matchStart, matchEnd);
if (matchStart >= 0 && matchEnd <= content.toString().length() && matchEnd >= matchStart) {
URLSpan[] span = content.getSpans(matchStart, matchEnd, URLSpan.class);
content.removeSpan(span);
content.setSpan(new ClickableSpan() {
@Override
public void onClick(@NonNull View textView) {
textView.setTag(CLICKABLE_SPAN);
switch (patternType) {
case TAG:
Intent intent = new Intent(context, HashTagActivity.class);
Bundle b = new Bundle();
b.putString(Helper.ARG_SEARCH_KEYWORD, word.trim());
intent.putExtras(b);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
break;
case GROUP:
break;
case MENTION:
intent = new Intent(context, ProfileActivity.class);
b = new Bundle();
Mention targetedMention = null;
HashMap<String, Integer> countUsername = new HashMap<>();
if (mentions != null) {
for (Mention mention : mentions) {
Integer count = countUsername.get(mention.username);
if (count == null) {
count = 0;
}
if (countUsername.containsKey(mention.username)) {
countUsername.put(mention.username, count + 1);
} else {
countUsername.put(mention.username, 1);
}
}
for (Mention mention : mentions) {
Integer count = countUsername.get(mention.username);
if (count == null) {
count = 0;
}
if (word.trim().compareToIgnoreCase("@" + mention.username) == 0 && count == 1) {
targetedMention = mention;
break;
}
}
}
if (targetedMention != null) {
b.putString(Helper.ARG_USER_ID, targetedMention.id);
} else {
b.putString(Helper.ARG_MENTION, word.trim());
}
intent.putExtras(b);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
break;
case MENTION_LONG:
intent = new Intent(context, ProfileActivity.class);
b = new Bundle();
targetedMention = null;
if (mentions != null) {
for (Mention mention : mentions) {
if (word.trim().substring(1).compareToIgnoreCase("@" + mention.acct) == 0) {
targetedMention = mention;
break;
}
}
}
if (targetedMention != null) {
b.putString(Helper.ARG_USER_ID, targetedMention.id);
} else {
b.putString(Helper.ARG_MENTION, word.trim());
}
intent.putExtras(b);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
break;
}
}
@Override
public void updateDrawState(@NonNull TextPaint ds) {
super.updateDrawState(ds);
ds.setUnderlineText(false);
ds.setColor(linkColor);
}
}, matchStart, matchEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
}
}
}
}
/**
* Convert HTML content to text. Also, it handles click on link
* This needs to be run asynchronously
*
* @param status {@link Status} - Status concerned by the spannable transformation
* @param content String - text to convert, it can be content, spoiler, poll items, etc.
* @return Spannable string
*/
private static void convertOuich(@NonNull Status status, SpannableStringBuilder content) {
Matcher matcher = Helper.ouichesPattern.matcher(content);
while (matcher.find()) {
Attachment attachment = new Attachment();
attachment.type = "audio";
String tag = matcher.group(1);
attachment.id = tag;
if (tag == null) {
continue;
}
attachment.remote_url = "http://ouich.es/mp3/" + tag + ".mp3";
attachment.url = "http://ouich.es/mp3/" + tag + ".mp3";
if (status.media_attachments == null) {
status.media_attachments = new ArrayList<>();
}
boolean alreadyAdded = false;
for (Attachment at : status.media_attachments) {
if (tag.compareTo(at.id) == 0) {
alreadyAdded = true;
break;
}
}
if (!alreadyAdded) {
status.media_attachments.add(attachment);
}
}
}
private static void replaceQuoteSpans(Context context, Spannable spannable) {
QuoteSpan[] quoteSpans = spannable.getSpans(0, spannable.length(), QuoteSpan.class);
for (QuoteSpan quoteSpan : quoteSpans) {
int start = spannable.getSpanStart(quoteSpan);
int end = spannable.getSpanEnd(quoteSpan);
int flags = spannable.getSpanFlags(quoteSpan);
spannable.removeSpan(quoteSpan);
int colord = ContextCompat.getColor(context, R.color.cyanea_accent_reference);
spannable.setSpan(new CustomQuoteSpan(
ContextCompat.getColor(context, R.color.transparent),
colord,
10,
20),
start,
end,
flags);
}
}
/**
* Convert HTML content to text. Also, it handles click on link
* This needs to be run asynchronously
*
* @param text String - text to convert, it can be content, spoiler, poll items, etc.
* @return Spannable string
*/
public static Spannable convertNitter(String text) {
SpannableString initialContent;
if (text == null) {
return null;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
initialContent = new SpannableString(Html.fromHtml(text, Html.FROM_HTML_MODE_LEGACY));
else
initialContent = new SpannableString(Html.fromHtml(text));
return initialContent;
}
/**
* Remove extra carriage returns at the bottom due to <p> tags in toots
*
* @param spannable SpannableStringBuilder
* @return SpannableStringBuilder
*/
private static SpannableStringBuilder trimSpannable(SpannableStringBuilder spannable) {
int trimStart = 0;
int trimEnd = 0;
String text = spannable.toString();
while (text.length() > 0 && text.startsWith("\n")) {
text = text.substring(1);
trimStart += 1;
}
while (text.length() > 0 && text.endsWith("\n")) {
text = text.substring(0, text.length() - 1);
trimEnd += 1;
}
return spannable.delete(0, trimStart).delete(spannable.length() - trimEnd, spannable.length());
}
/**
* Makes the move to account clickable
*
* @param context Context
* @return SpannableString
*/
public static SpannableString moveToText(final Context context, Account account) {
SpannableString spannableString = null;
if (account.moved != null) {
spannableString = new SpannableString(context.getString(R.string.account_moved_to, account.acct, "@" + account.moved.acct));
int startPosition = spannableString.toString().indexOf("@" + account.moved.acct);
int endPosition = startPosition + ("@" + account.moved.acct).length();
if (startPosition >= 0 && endPosition <= spannableString.toString().length() && endPosition >= startPosition)
spannableString.setSpan(new ClickableSpan() {
@Override
public void onClick(@NonNull View textView) {
Intent intent = new Intent(context, ProfileActivity.class);
Bundle b = new Bundle();
b.putSerializable(Helper.ARG_ACCOUNT, account.moved);
intent.putExtras(b);
context.startActivity(intent);
}
@Override
public void updateDrawState(@NonNull TextPaint ds) {
super.updateDrawState(ds);
}
},
startPosition, endPosition,
Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
}
return spannableString;
}
}

View file

@ -1,244 +0,0 @@
package app.fedilab.android.helper;
/* Copyright 2022 Thomas Schneider
*
* This file is a part of Fedilab
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Build;
import android.text.Html;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.ViewModelProvider;
import androidx.lifecycle.ViewModelStoreOwner;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.client.endpoints.MastodonAccountsService;
import app.fedilab.android.client.entities.api.Filter;
import app.fedilab.android.client.entities.api.Notification;
import app.fedilab.android.client.entities.api.Status;
import app.fedilab.android.client.entities.app.Timeline;
import app.fedilab.android.viewmodel.mastodon.AccountsVM;
import okhttp3.OkHttpClient;
import retrofit2.Call;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
public class TimelineHelper {
private static MastodonAccountsService init(Context context) {
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.readTimeout(60, TimeUnit.SECONDS)
.connectTimeout(60, TimeUnit.SECONDS)
.proxy(Helper.getProxy(context))
.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://" + BaseMainActivity.currentInstance + "/api/v1/")
.addConverterFactory(GsonConverterFactory.create())
.client(okHttpClient)
.build();
return retrofit.create(MastodonAccountsService.class);
}
/**
* Allows to filter statuses, should be called in API calls (background)
*
* @param context - Context
* @param statuses - List of {@link Status}
* @param filterTimeLineType - {@link Timeline.TimeLineEnum}
* @return filtered List<Status>
*/
public static List<Status> filterStatus(Context context, List<Status> statuses, Timeline.TimeLineEnum filterTimeLineType) {
//A security to make sure filters have been fetched before displaying messages
List<Status> statusesToRemove = new ArrayList<>();
if (!BaseMainActivity.filterFetched) {
MastodonAccountsService mastodonAccountsService = init(context);
List<Filter> filterList;
Call<List<Filter>> getFiltersCall = mastodonAccountsService.getFilters(BaseMainActivity.currentToken);
if (getFiltersCall != null) {
try {
Response<List<Filter>> getFiltersResponse = getFiltersCall.execute();
if (getFiltersResponse.isSuccessful()) {
BaseMainActivity.filterFetched = true;
filterList = getFiltersResponse.body();
BaseMainActivity.mainFilters = filterList;
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
//If there are filters:
if (BaseMainActivity.mainFilters != null && BaseMainActivity.mainFilters.size() > 0 && statuses != null && statuses.size() > 0) {
for (Filter filter : BaseMainActivity.mainFilters) {
if (filter.irreversible) { //Dealt by the server
continue;
}
for (String filterContext : filter.context) {
if (filterTimeLineType.getValue().equalsIgnoreCase(filterContext)) {
if (filter.whole_word) {
Pattern p = Pattern.compile("(^" + Pattern.quote(filter.phrase) + "\\b|\\b" + Pattern.quote(filter.phrase) + "$)", Pattern.CASE_INSENSITIVE);
for (Status status : statuses) {
String content;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
content = Html.fromHtml(status.reblog != null ? status.reblog.content : status.content, Html.FROM_HTML_MODE_LEGACY).toString();
else
content = Html.fromHtml(status.reblog != null ? status.reblog.content : status.content).toString();
Matcher m = p.matcher(content);
if (m.find()) {
statusesToRemove.add(status);
continue;
}
if (status.spoiler_text != null) {
String spoilerText;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
spoilerText = Html.fromHtml(status.reblog != null ? status.reblog.spoiler_text : status.spoiler_text, Html.FROM_HTML_MODE_LEGACY).toString();
else
spoilerText = Html.fromHtml(status.reblog != null ? status.reblog.spoiler_text : status.spoiler_text).toString();
Matcher ms = p.matcher(spoilerText);
if (ms.find()) {
statusesToRemove.add(status);
}
}
}
} else {
for (Status status : statuses) {
String content;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
content = Html.fromHtml(status.reblog != null ? status.reblog.content : status.content, Html.FROM_HTML_MODE_LEGACY).toString();
else
content = Html.fromHtml(status.reblog != null ? status.reblog.content : status.content).toString();
if (content.contains(filter.phrase)) {
statusesToRemove.add(status);
continue;
}
if (status.spoiler_text != null) {
String spoilerText;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
spoilerText = Html.fromHtml(status.reblog != null ? status.reblog.spoiler_text : status.spoiler_text, Html.FROM_HTML_MODE_LEGACY).toString();
else
spoilerText = Html.fromHtml(status.reblog != null ? status.reblog.spoiler_text : status.spoiler_text).toString();
if (spoilerText.contains(filter.phrase)) {
statusesToRemove.add(status);
}
}
}
}
}
}
}
}
if (statuses != null) {
statuses.removeAll(statusesToRemove);
}
return statuses;
}
/**
* Allows to filter notifications, should be called in API calls (background)
*
* @param context - Context
* @param notifications - List of {@link Notification}
* @return filtered List<Status>
*/
public static List<Notification> filterNotification(Context context, List<Notification> notifications, boolean cached) {
//A security to make sure filters have been fetched before displaying messages
List<Notification> notificationToRemove = new ArrayList<>();
if (!BaseMainActivity.filterFetched) {
try {
AccountsVM accountsVM = new ViewModelProvider((ViewModelStoreOwner) context).get(AccountsVM.class);
accountsVM.getFilters(BaseMainActivity.currentInstance, BaseMainActivity.currentToken).observe((LifecycleOwner) context, filters -> {
BaseMainActivity.filterFetched = true;
BaseMainActivity.mainFilters = filters;
});
} catch (Exception e) {
return notifications;
}
}
//If there are filters:
if (BaseMainActivity.mainFilters != null && BaseMainActivity.mainFilters.size() > 0) {
for (Filter filter : BaseMainActivity.mainFilters) {
if (filter.irreversible) { //Dealt by the server
continue;
}
for (String filterContext : filter.context) {
if (Timeline.TimeLineEnum.NOTIFICATION.getValue().equalsIgnoreCase(filterContext)) {
if (filter.whole_word) {
Pattern p = Pattern.compile("(^" + Pattern.quote(filter.phrase) + "\\b|\\b" + Pattern.quote(filter.phrase) + "$)", Pattern.CASE_INSENSITIVE);
for (Notification notification : notifications) {
notification.cached = cached;
if (notification.status != null) {
String content;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
content = Html.fromHtml(notification.status.content, Html.FROM_HTML_MODE_LEGACY).toString();
else
content = Html.fromHtml(notification.status.content).toString();
Matcher m = p.matcher(content);
if (m.find()) {
notificationToRemove.add(notification);
}
}
}
} else {
for (Notification notification : notifications) {
String content;
notification.cached = cached;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
content = Html.fromHtml(notification.status.content, Html.FROM_HTML_MODE_LEGACY).toString();
else
content = Html.fromHtml(notification.status.content).toString();
if (content.contains(filter.phrase)) {
notificationToRemove.add(notification);
}
}
}
} else {
for (Notification notification : notifications) {
notification.cached = cached;
}
}
}
}
}
notifications.removeAll(notificationToRemove);
return notifications;
}
/**
* Check if WIFI is opened
*
* @param context Context
* @return boolean
*/
public static boolean isOnWIFI(Context context) {
ConnectivityManager connManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
if (connManager != null) {
NetworkInfo activeNetwork = connManager.getActiveNetworkInfo();
return (activeNetwork != null && activeNetwork.getType() == ConnectivityManager.TYPE_WIFI);
}
return false;
}
}

View file

@ -0,0 +1,353 @@
package app.fedilab.android.mastodon.activities;
/* Copyright 2022 Thomas Schneider
*
* This file is a part of Fedilab
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */
import android.app.Activity;
import android.graphics.PorterDuff;
import android.os.Bundle;
import android.view.MenuItem;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.Toast;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager;
import java.util.ArrayList;
import app.fedilab.android.R;
import app.fedilab.android.activities.MainActivity;
import app.fedilab.android.databinding.ActivityAdminReportBinding;
import app.fedilab.android.mastodon.client.entities.api.Status;
import app.fedilab.android.mastodon.client.entities.api.admin.AdminAccount;
import app.fedilab.android.mastodon.client.entities.api.admin.AdminReport;
import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.mastodon.helper.ThemeHelper;
import app.fedilab.android.mastodon.ui.drawer.StatusReportAdapter;
import app.fedilab.android.mastodon.viewmodel.mastodon.AdminVM;
import es.dmoral.toasty.Toasty;
public class AccountReportActivity extends BaseBarActivity {
private String account_id;
private AdminReport report;
private ActivityAdminReportBinding binding;
private AdminVM adminVM;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityAdminReportBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
report = null;
AdminAccount targeted_account = null;
Bundle b = getIntent().getExtras();
if (b != null) {
account_id = b.getString(Helper.ARG_ACCOUNT_ID, null);
targeted_account = (AdminAccount) b.getSerializable(Helper.ARG_ACCOUNT);
report = (AdminReport) b.getSerializable(Helper.ARG_REPORT);
}
binding.allow.getBackground().setColorFilter(ThemeHelper.getAttColor(this, R.attr.colorPrimary), PorterDuff.Mode.MULTIPLY);
binding.reject.getBackground().setColorFilter(ThemeHelper.getAttColor(this, R.attr.colorError), PorterDuff.Mode.MULTIPLY);
if (account_id == null && report == null && targeted_account == null) {
Toasty.error(AccountReportActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show();
finish();
}
binding.assign.setVisibility(View.GONE);
binding.status.setVisibility(View.GONE);
adminVM = new ViewModelProvider(this).get(AdminVM.class);
if (account_id != null) {
adminVM.getAccount(MainActivity.currentInstance, MainActivity.currentToken, account_id).observe(this, account -> fillReport(account, null));
return;
}
if (report != null) {
ArrayList<String> contents = new ArrayList<>();
for (Status status : report.statuses) {
contents.add(status.content);
}
binding.lvStatuses.setLayoutManager(new LinearLayoutManager(this));
StatusReportAdapter adapter = new StatusReportAdapter(contents);
binding.lvStatuses.setAdapter(adapter);
binding.statusesGroup.setVisibility(View.VISIBLE);
targeted_account = report.target_account;
}
if (targeted_account != null) {
account_id = targeted_account.id;
fillReport(targeted_account, null);
account_id = targeted_account.username;
}
}
private void fillReport(AdminAccount accountAdmin, actionType type) {
if (accountAdmin == null) {
Toasty.error(AccountReportActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show();
return;
}
if (!accountAdmin.approved && (accountAdmin.domain == null || accountAdmin.domain.equals("null"))) {
binding.allowRejectGroup.setVisibility(View.VISIBLE);
}
if (!accountAdmin.silenced) {
binding.silence.setText(getString(R.string.silence));
} else {
binding.silence.setText(getString(R.string.unsilence));
}
if (!accountAdmin.disabled) {
binding.disable.setText(getString(R.string.disable));
} else {
binding.disable.setText(getString(R.string.undisable));
}
if (!accountAdmin.suspended) {
binding.suspend.setText(getString(R.string.suspend));
} else {
binding.suspend.setText(getString(R.string.unsuspend));
}
binding.reject.setOnClickListener(view -> adminVM.reject(MainActivity.currentInstance, MainActivity.currentToken, account_id).observe(this, account -> fillReport(account, actionType.REJECT)));
binding.allow.setOnClickListener(view -> adminVM.approve(MainActivity.currentInstance, MainActivity.currentToken, account_id).observe(this, account -> fillReport(account, actionType.APPROVE)));
binding.warn.setOnClickListener(view -> {
adminVM.performAction(MainActivity.currentInstance, MainActivity.currentToken, account_id, "none", null, null, binding.comment.getText().toString().trim(), binding.emailUser.isChecked());
fillReport(accountAdmin, actionType.NONE);
});
binding.silence.setOnClickListener(view -> {
if (!accountAdmin.silenced) {
adminVM.performAction(MainActivity.currentInstance, MainActivity.currentToken, account_id, "silence", null, null, binding.comment.getText().toString().trim(), binding.emailUser.isChecked());
accountAdmin.silenced = true;
fillReport(accountAdmin, actionType.SILENCE);
} else {
adminVM.unsilence(MainActivity.currentInstance, MainActivity.currentToken, account_id).observe(this, account -> fillReport(account, actionType.UNSILENCE));
}
});
binding.disable.setOnClickListener(view -> {
if (!accountAdmin.disabled) {
adminVM.performAction(MainActivity.currentInstance, MainActivity.currentToken, account_id, "disable", null, null, binding.comment.getText().toString().trim(), binding.emailUser.isChecked());
accountAdmin.disabled = true;
fillReport(accountAdmin, actionType.DISABLE);
} else {
adminVM.enable(MainActivity.currentInstance, MainActivity.currentToken, account_id).observe(this, account -> fillReport(account, actionType.ENABLE));
}
});
binding.suspend.setOnClickListener(view -> {
if (!accountAdmin.suspended) {
adminVM.performAction(MainActivity.currentInstance, MainActivity.currentToken, account_id, "suspend", null, null, binding.comment.getText().toString().trim(), binding.emailUser.isChecked());
accountAdmin.suspended = true;
fillReport(accountAdmin, actionType.SUSPEND);
} else {
adminVM.unsuspend(MainActivity.currentInstance, MainActivity.currentToken, account_id).observe(this, account -> fillReport(account, actionType.UNSUSPEND));
}
});
if (type != null) {
String message = null;
switch (type) {
case SILENCE:
message = getString(R.string.account_silenced);
break;
case UNSILENCE:
message = getString(R.string.account_unsilenced);
break;
case DISABLE:
message = getString(R.string.account_disabled);
break;
case ENABLE:
message = getString(R.string.account_undisabled);
break;
case SUSPEND:
message = getString(R.string.account_suspended);
break;
case UNSUSPEND:
message = getString(R.string.account_unsuspended);
break;
case NONE:
message = getString(R.string.account_warned);
break;
case APPROVE:
binding.allowRejectGroup.setVisibility(View.GONE);
message = getString(R.string.account_approved);
break;
case REJECT:
binding.allowRejectGroup.setVisibility(View.GONE);
message = getString(R.string.account_rejected);
break;
}
if (message != null) {
Toasty.success(AccountReportActivity.this, message, Toast.LENGTH_LONG).show();
}
binding.comment.setText("");
InputMethodManager imm = (InputMethodManager) getSystemService(Activity.INPUT_METHOD_SERVICE);
assert imm != null;
imm.hideSoftInputFromWindow(binding.comment.getWindowToken(), 0);
}
if (accountAdmin.account != null) {
binding.username.setText(String.format("@%s", accountAdmin.account.acct));
}
binding.email.setText(accountAdmin.email);
if (accountAdmin.email == null || accountAdmin.email.trim().equals("")) {
binding.email.setVisibility(View.GONE);
binding.emailLabel.setVisibility(View.GONE);
}
if (accountAdmin.ip == null || accountAdmin.ip.trim().equals("")) {
binding.recentIp.setVisibility(View.GONE);
binding.recentIpLabel.setVisibility(View.GONE);
}
if (accountAdmin.created_at == null) {
binding.joined.setVisibility(View.GONE);
binding.joinedLabel.setVisibility(View.GONE);
}
if (accountAdmin.disabled) {
binding.loginStatus.setText(getString(R.string.disabled));
} else if (accountAdmin.silenced) {
binding.loginStatus.setText(getString(R.string.silenced));
} else if (accountAdmin.suspended) {
binding.loginStatus.setText(getString(R.string.suspended));
} else {
binding.loginStatus.setText(getString(R.string.active));
}
if (accountAdmin.domain == null || accountAdmin.domain.trim().equalsIgnoreCase("null")) {
binding.warn.setVisibility(View.VISIBLE);
binding.emailUser.setVisibility(View.VISIBLE);
binding.commentLabel.setVisibility(View.VISIBLE);
binding.comment.setVisibility(View.VISIBLE);
binding.recentIp.setText(accountAdmin.ip != null ? accountAdmin.ip : "");
binding.disable.setVisibility(View.VISIBLE);
binding.suspend.setVisibility(View.VISIBLE);
} else {
binding.warn.setVisibility(View.GONE);
binding.emailUser.setVisibility(View.GONE);
binding.emailUser.setChecked(false);
binding.comment.setVisibility(View.GONE);
binding.recentIp.setText("-");
binding.permissions.setText("-");
binding.email.setText("-");
binding.disable.setVisibility(View.GONE);
binding.suspend.setVisibility(View.VISIBLE);
binding.commentLabel.setVisibility(View.GONE);
}
if (accountAdmin.role != null) {
binding.permissions.setText(AdminAccount.permissions.get(accountAdmin.role.permissions));
binding.permissions.setText(getString(R.string.user));
if (accountAdmin.role.permissions == 1 || accountAdmin.role.permissions == 400) {
binding.warn.setVisibility(View.GONE);
binding.suspend.setVisibility(View.GONE);
binding.silence.setVisibility(View.GONE);
binding.disable.setVisibility(View.GONE);
binding.emailUser.setVisibility(View.GONE);
binding.emailUser.setChecked(false);
binding.comment.setVisibility(View.GONE);
binding.commentLabel.setVisibility(View.GONE);
}
binding.emailStatus.setText(accountAdmin.confirmed ? getString(R.string.confirmed) : getString(R.string.unconfirmed));
}
binding.joined.setText(Helper.dateToString(accountAdmin.created_at));
if (report != null) {
binding.assign.setVisibility(View.VISIBLE);
binding.status.setVisibility(View.VISIBLE);
if (report.assigned_account == null) {
binding.assign.setText(getString(R.string.assign_to_me));
} else {
binding.assign.setText(getString(R.string.unassign));
}
binding.assign.setOnClickListener(view -> {
if (report.assigned_account == null) {
adminVM.assignToSelf(MainActivity.currentInstance, MainActivity.currentToken, report.id).observe(this, adminReport -> {
report = adminReport;
fillReport(accountAdmin, null);
});
} else {
adminVM.unassign(MainActivity.currentInstance, MainActivity.currentToken, report.id).observe(this, adminReport -> {
report = adminReport;
fillReport(accountAdmin, null);
});
}
});
if (report.action_taken) {
binding.status.setText(getString(R.string.mark_unresolved));
} else {
binding.status.setText(getString(R.string.mark_resolved));
}
binding.status.setOnClickListener(view -> {
if (report.action_taken) {
adminVM.reopen(MainActivity.currentInstance, MainActivity.currentToken, report.id).observe(this, adminReport -> {
report = adminReport;
fillReport(accountAdmin, null);
});
} else {
adminVM.resolved(MainActivity.currentInstance, MainActivity.currentToken, report.id).observe(this, adminReport -> {
report = adminReport;
fillReport(accountAdmin, null);
});
}
});
} else {
binding.assign.setVisibility(View.GONE);
binding.status.setVisibility(View.GONE);
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
public enum actionType {
ENABLE,
APPROVE,
REJECT,
NONE,
SILENCE,
DISABLE,
UNSILENCE,
SUSPEND,
UNSUSPEND,
ASSIGN_TO_SELF,
UNASSIGN,
REOPEN,
RESOLVE,
GET_ACCOUNTS,
GET_ONE_ACCOUNT,
GET_REPORTS,
GET_ONE_REPORT
}
}

View file

@ -1,4 +1,4 @@
package app.fedilab.android.activities; package app.fedilab.android.mastodon.activities;
/* Copyright 2022 Thomas Schneider /* Copyright 2022 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -14,51 +14,52 @@ package app.fedilab.android.activities;
* You should have received a copy of the GNU General Public License along with Fedilab; if not, * You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle; import android.os.Bundle;
import android.view.MenuItem; import android.view.MenuItem;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction; import androidx.fragment.app.FragmentTransaction;
import app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.client.entities.app.Timeline;
import app.fedilab.android.databinding.ActivityActionsBinding; import app.fedilab.android.databinding.ActivityActionsBinding;
import app.fedilab.android.helper.Helper; import app.fedilab.android.mastodon.client.entities.app.Timeline;
import app.fedilab.android.helper.ThemeHelper; import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.ui.fragment.timeline.FragmentMastodonAccount; import app.fedilab.android.mastodon.helper.ThemeHelper;
import app.fedilab.android.ui.fragment.timeline.FragmentMastodonTimeline; import app.fedilab.android.mastodon.ui.fragment.timeline.FragmentMastodonAccount;
import app.fedilab.android.mastodon.ui.fragment.timeline.FragmentMastodonDomainBlock;
import app.fedilab.android.mastodon.ui.fragment.timeline.FragmentMastodonTimeline;
public class ActionActivity extends BaseActivity { public class ActionActivity extends BaseBarActivity {
private ActivityActionsBinding binding; private ActivityActionsBinding binding;
private boolean canGoBack; private boolean canGoBack;
private FragmentMastodonTimeline fragmentMastodonTimeline; private FragmentMastodonTimeline fragmentMastodonTimeline;
private FragmentMastodonAccount fragmentMastodonAccount; private FragmentMastodonAccount fragmentMastodonAccount;
private FragmentMastodonDomainBlock fragmentMastodonDomainBlock;
@Override @Override
protected void onCreate(@Nullable Bundle savedInstanceState) { protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
ThemeHelper.applyThemeBar(this);
binding = ActivityActionsBinding.inflate(getLayoutInflater()); binding = ActivityActionsBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); setContentView(binding.getRoot());
if (getSupportActionBar() != null) { if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
} }
canGoBack = false; canGoBack = false;
binding.favourites.setOnClickListener(v -> displayTimeline(Timeline.TimeLineEnum.FAVOURITE_TIMELINE)); binding.favourites.setOnClickListener(v -> displayTimeline(Timeline.TimeLineEnum.FAVOURITE_TIMELINE));
binding.bookmarks.setOnClickListener(v -> displayTimeline(Timeline.TimeLineEnum.BOOKMARK_TIMELINE)); binding.bookmarks.setOnClickListener(v -> displayTimeline(Timeline.TimeLineEnum.BOOKMARK_TIMELINE));
binding.muted.setOnClickListener(v -> displayTimeline(Timeline.TimeLineEnum.MUTED_TIMELINE)); binding.muted.setOnClickListener(v -> displayTimeline(Timeline.TimeLineEnum.MUTED_TIMELINE));
binding.blocked.setOnClickListener(v -> displayTimeline(Timeline.TimeLineEnum.BLOCKED_TIMELINE)); binding.blocked.setOnClickListener(v -> displayTimeline(Timeline.TimeLineEnum.BLOCKED_TIMELINE));
binding.domainBlock.setOnClickListener(v -> displayTimeline(Timeline.TimeLineEnum.BLOCKED_DOMAIN_TIMELINE));
binding.mutedHome.setOnClickListener(v -> displayTimeline(Timeline.TimeLineEnum.MUTED_TIMELINE_HOME));
} }
private void displayTimeline(Timeline.TimeLineEnum type) { private void displayTimeline(Timeline.TimeLineEnum type) {
canGoBack = true; canGoBack = true;
if (type == Timeline.TimeLineEnum.MUTED_TIMELINE || type == Timeline.TimeLineEnum.BLOCKED_TIMELINE) { if (type == Timeline.TimeLineEnum.MUTED_TIMELINE || type == Timeline.TimeLineEnum.BLOCKED_TIMELINE || type == Timeline.TimeLineEnum.MUTED_TIMELINE_HOME) {
ThemeHelper.slideViewsToLeft(binding.buttonContainer, binding.fragmentContainer, () -> { ThemeHelper.slideViewsToLeft(binding.buttonContainer, binding.fragmentContainer, () -> {
fragmentMastodonAccount = new FragmentMastodonAccount(); fragmentMastodonAccount = new FragmentMastodonAccount();
@ -73,6 +74,15 @@ public class ActionActivity extends BaseActivity {
fragmentTransaction.commit(); fragmentTransaction.commit();
}); });
} else if (type == Timeline.TimeLineEnum.BLOCKED_DOMAIN_TIMELINE) {
ThemeHelper.slideViewsToLeft(binding.buttonContainer, binding.fragmentContainer, () -> {
fragmentMastodonDomainBlock = new FragmentMastodonDomainBlock();
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction =
fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.fragment_container, fragmentMastodonDomainBlock);
fragmentTransaction.commit();
});
} else { } else {
ThemeHelper.slideViewsToLeft(binding.buttonContainer, binding.fragmentContainer, () -> { ThemeHelper.slideViewsToLeft(binding.buttonContainer, binding.fragmentContainer, () -> {
@ -102,6 +112,12 @@ public class ActionActivity extends BaseActivity {
case BOOKMARK_TIMELINE: case BOOKMARK_TIMELINE:
setTitle(R.string.bookmarks); setTitle(R.string.bookmarks);
break; break;
case BLOCKED_DOMAIN_TIMELINE:
setTitle(R.string.blocked_domains);
break;
case MUTED_TIMELINE_HOME:
setTitle(R.string.muted_menu_home);
break;
} }
} }
@ -116,6 +132,9 @@ public class ActionActivity extends BaseActivity {
if (fragmentMastodonAccount != null) { if (fragmentMastodonAccount != null) {
fragmentMastodonAccount.onDestroyView(); fragmentMastodonAccount.onDestroyView();
} }
if (fragmentMastodonDomainBlock != null) {
fragmentMastodonDomainBlock.onDestroyView();
}
}); });
setTitle(R.string.interactions); setTitle(R.string.interactions);
} else { } else {

View file

@ -1,4 +1,4 @@
package app.fedilab.android.activities; package app.fedilab.android.mastodon.activities;
/* Copyright 2022 Thomas Schneider /* Copyright 2022 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -19,22 +19,19 @@ import static app.fedilab.android.BaseMainActivity.currentInstance;
import static app.fedilab.android.BaseMainActivity.emojis; import static app.fedilab.android.BaseMainActivity.emojis;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle; import android.os.Bundle;
import android.util.TypedValue; import android.util.TypedValue;
import android.view.MenuItem; import android.view.MenuItem;
import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.ActionBar;
import androidx.core.content.ContextCompat;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.client.entities.api.EmojiInstance;
import app.fedilab.android.databinding.ActivityAnnouncementBinding; import app.fedilab.android.databinding.ActivityAnnouncementBinding;
import app.fedilab.android.exception.DBException; import app.fedilab.android.mastodon.client.entities.api.EmojiInstance;
import app.fedilab.android.helper.Helper; import app.fedilab.android.mastodon.exception.DBException;
import app.fedilab.android.helper.ThemeHelper; import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.ui.fragment.timeline.FragmentMastodonAnnouncement; import app.fedilab.android.mastodon.ui.fragment.timeline.FragmentMastodonAnnouncement;
public class AnnouncementActivity extends BaseActivity { public class AnnouncementActivity extends BaseActivity {
@ -43,7 +40,7 @@ public class AnnouncementActivity extends BaseActivity {
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
ThemeHelper.applyTheme(this);
ActivityAnnouncementBinding binding = ActivityAnnouncementBinding.inflate(getLayoutInflater()); ActivityAnnouncementBinding binding = ActivityAnnouncementBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); setContentView(binding.getRoot());
@ -53,7 +50,6 @@ public class AnnouncementActivity extends BaseActivity {
//Remove title //Remove title
if (actionBar != null) { if (actionBar != null) {
actionBar.setDisplayShowTitleEnabled(false); actionBar.setDisplayShowTitleEnabled(false);
actionBar.setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
} }
binding.title.setText(R.string.action_announcements); binding.title.setText(R.string.action_announcements);
if (getSupportActionBar() != null) { if (getSupportActionBar() != null) {

View file

@ -0,0 +1,173 @@
package app.fedilab.android.mastodon.activities;
/* Copyright 2021 Thomas Schneider
*
* This file is a part of Fedilab
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import android.view.Window;
import android.view.WindowManager;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.preference.PreferenceManager;
import com.google.android.material.color.DynamicColors;
import com.vanniktech.emoji.EmojiManager;
import com.vanniktech.emoji.one.EmojiOneProvider;
import org.conscrypt.Conscrypt;
import java.security.Security;
import app.fedilab.android.R;
import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.mastodon.helper.ThemeHelper;
@SuppressLint("Registered")
public class BaseActivity extends AppCompatActivity {
public static int currentThemeId;
static {
EmojiManager.install(new EmojiOneProvider());
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
boolean patch_provider = true;
final SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(this);
try {
patch_provider = sharedpreferences.getBoolean(Helper.SET_SECURITY_PROVIDER, true);
} catch (Exception ignored) {
}
if (patch_provider) {
try {
Security.insertProviderAt(Conscrypt.newProvider(), 1);
} catch (Exception ignored) {
}
}
String currentTheme = sharedpreferences.getString(getString(R.string.SET_THEME_BASE), getString(R.string.SET_DEFAULT_THEME));
//Default automatic switch
if (currentTheme.equals(getString(R.string.SET_DEFAULT_THEME))) {
int currentNightMode = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
switch (currentNightMode) {
case Configuration.UI_MODE_NIGHT_NO:
String defaultLight = sharedpreferences.getString(getString(R.string.SET_THEME_DEFAULT_LIGHT), "LIGHT");
switch (defaultLight) {
case "LIGHT":
setTheme(R.style.AppTheme);
currentThemeId = R.style.AppTheme;
break;
case "SOLARIZED_LIGHT":
setTheme(R.style.SolarizedAppTheme);
currentThemeId = R.style.SolarizedAppTheme;
break;
}
break;
case Configuration.UI_MODE_NIGHT_YES:
String defaultDark = sharedpreferences.getString(getString(R.string.SET_THEME_DEFAULT_DARK), "DARK");
switch (defaultDark) {
case "DARK":
setTheme(R.style.AppTheme);
currentThemeId = R.style.AppTheme;
break;
case "SOLARIZED_DARK":
setTheme(R.style.SolarizedAppTheme);
currentThemeId = R.style.SolarizedAppTheme;
break;
case "BLACK":
setTheme(R.style.BlackAppTheme);
currentThemeId = R.style.BlackAppTheme;
break;
case "DRACULA":
setTheme(R.style.DraculaAppTheme);
currentThemeId = R.style.DraculaAppTheme;
break;
}
break;
}
} else {
switch (currentTheme) {
case "LIGHT":
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
setTheme(R.style.AppTheme);
currentThemeId = R.style.AppTheme;
break;
case "DARK":
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
setTheme(R.style.AppTheme);
currentThemeId = R.style.AppTheme;
break;
case "SOLARIZED_LIGHT":
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
setTheme(R.style.SolarizedAppTheme);
currentThemeId = R.style.SolarizedAppTheme;
break;
case "SOLARIZED_DARK":
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
setTheme(R.style.SolarizedAppTheme);
currentThemeId = R.style.SolarizedAppTheme;
break;
case "BLACK":
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
setTheme(R.style.BlackAppTheme);
currentThemeId = R.style.BlackAppTheme;
break;
case "DRACULA":
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
setTheme(R.style.DraculaAppTheme);
currentThemeId = R.style.DraculaAppTheme;
break;
}
}
super.onCreate(savedInstanceState);
boolean dynamicColor = sharedpreferences.getBoolean(getString(R.string.SET_DYNAMICCOLOR), false);
if (dynamicColor) {
DynamicColors.applyToActivityIfAvailable(this);
}
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N) {
ThemeHelper.adjustFontScale(this, getResources().getConfiguration());
}
if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
Window window = getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(Color.BLACK);
window.setNavigationBarColor(Color.BLACK);
}
Helper.setLocale(this);
}
@Override
protected void attachBaseContext(Context newBase) {
if (android.os.Build.VERSION.SDK_INT > Build.VERSION_CODES.N) {
final Configuration override = new Configuration(newBase.getResources().getConfiguration());
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(newBase);
override.fontScale = prefs.getFloat(newBase.getString(R.string.SET_FONT_SCALE), 1.1f);
applyOverrideConfiguration(override);
}
super.attachBaseContext(newBase);
}
}

View file

@ -0,0 +1,161 @@
package app.fedilab.android.mastodon.activities;
/* Copyright 2022 Thomas Schneider
*
* This file is a part of Fedilab
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import android.view.Window;
import android.view.WindowManager;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.preference.PreferenceManager;
import com.google.android.material.color.DynamicColors;
import com.vanniktech.emoji.EmojiManager;
import com.vanniktech.emoji.one.EmojiOneProvider;
import org.conscrypt.Conscrypt;
import java.security.Security;
import app.fedilab.android.R;
import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.mastodon.helper.ThemeHelper;
@SuppressLint("Registered")
public class BaseBarActivity extends AppCompatActivity {
static {
EmojiManager.install(new EmojiOneProvider());
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
final SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(this);
boolean patch_provider = true;
try {
patch_provider = sharedpreferences.getBoolean(Helper.SET_SECURITY_PROVIDER, true);
} catch (Exception ignored) {
}
if (patch_provider) {
try {
Security.insertProviderAt(Conscrypt.newProvider(), 1);
} catch (Exception ignored) {
}
}
String currentTheme = sharedpreferences.getString(getString(R.string.SET_THEME_BASE), getString(R.string.SET_DEFAULT_THEME));
//Default automatic switch
if (currentTheme.equals(getString(R.string.SET_DEFAULT_THEME))) {
int currentNightMode = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
switch (currentNightMode) {
case Configuration.UI_MODE_NIGHT_NO:
String defaultLight = sharedpreferences.getString(getString(R.string.SET_THEME_DEFAULT_LIGHT), "LIGHT");
switch (defaultLight) {
case "LIGHT":
setTheme(R.style.AppThemeBar);
break;
case "SOLARIZED_LIGHT":
setTheme(R.style.SolarizedAppThemeBar);
break;
}
break;
case Configuration.UI_MODE_NIGHT_YES:
String defaultDark = sharedpreferences.getString(getString(R.string.SET_THEME_DEFAULT_DARK), "DARK");
switch (defaultDark) {
case "DARK":
setTheme(R.style.AppThemeBar);
break;
case "SOLARIZED_DARK":
setTheme(R.style.SolarizedAppThemeBar);
break;
case "BLACK":
setTheme(R.style.BlackAppThemeBar);
break;
case "DRACULA":
setTheme(R.style.DraculaAppThemeBar);
break;
}
break;
}
} else {
switch (currentTheme) {
case "LIGHT":
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
setTheme(R.style.AppThemeBar);
break;
case "DARK":
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
setTheme(R.style.AppThemeBar);
break;
case "SOLARIZED_LIGHT":
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
setTheme(R.style.SolarizedAppThemeBar);
break;
case "SOLARIZED_DARK":
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
setTheme(R.style.SolarizedAppThemeBar);
break;
case "BLACK":
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
setTheme(R.style.BlackAppThemeBar);
break;
case "DRACULA":
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
setTheme(R.style.DraculaAppThemeBar);
break;
}
}
super.onCreate(savedInstanceState);
boolean dynamicColor = sharedpreferences.getBoolean(getString(R.string.SET_DYNAMICCOLOR), false);
if (dynamicColor) {
DynamicColors.applyToActivityIfAvailable(this);
}
if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
Window window = getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(Color.BLACK);
window.setNavigationBarColor(Color.BLACK);
}
if (android.os.Build.VERSION.SDK_INT <= Build.VERSION_CODES.N) {
ThemeHelper.adjustFontScale(this, getResources().getConfiguration());
}
Helper.setLocale(this);
}
@Override
protected void attachBaseContext(Context newBase) {
if (android.os.Build.VERSION.SDK_INT > Build.VERSION_CODES.N) {
final Configuration override = new Configuration(newBase.getResources().getConfiguration());
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(newBase);
override.fontScale = prefs.getFloat(newBase.getString(R.string.SET_FONT_SCALE), 1.1f);
applyOverrideConfiguration(override);
}
super.attachBaseContext(newBase);
}
}

View file

@ -0,0 +1,161 @@
package app.fedilab.android.mastodon.activities;
/* Copyright 2022 Thomas Schneider
*
* This file is a part of Fedilab
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import android.view.Window;
import android.view.WindowManager;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.preference.PreferenceManager;
import com.google.android.material.color.DynamicColors;
import com.vanniktech.emoji.EmojiManager;
import com.vanniktech.emoji.one.EmojiOneProvider;
import org.conscrypt.Conscrypt;
import java.security.Security;
import app.fedilab.android.R;
import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.mastodon.helper.ThemeHelper;
@SuppressLint("Registered")
public class BaseTransparentActivity extends AppCompatActivity {
static {
EmojiManager.install(new EmojiOneProvider());
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
final SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(this);
boolean patch_provider = true;
try {
patch_provider = sharedpreferences.getBoolean(Helper.SET_SECURITY_PROVIDER, true);
} catch (Exception ignored) {
}
if (patch_provider) {
try {
Security.insertProviderAt(Conscrypt.newProvider(), 1);
} catch (Exception ignored) {
}
}
String currentTheme = sharedpreferences.getString(getString(R.string.SET_THEME_BASE), getString(R.string.SET_DEFAULT_THEME));
//Default automatic switch
if (currentTheme.equals(getString(R.string.SET_DEFAULT_THEME))) {
int currentNightMode = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
switch (currentNightMode) {
case Configuration.UI_MODE_NIGHT_NO:
String defaultLight = sharedpreferences.getString(getString(R.string.SET_THEME_DEFAULT_LIGHT), "LIGHT");
switch (defaultLight) {
case "LIGHT":
setTheme(R.style.Transparent);
break;
case "SOLARIZED_LIGHT":
setTheme(R.style.TransparentSolarized);
break;
}
break;
case Configuration.UI_MODE_NIGHT_YES:
String defaultDark = sharedpreferences.getString(getString(R.string.SET_THEME_DEFAULT_DARK), "DARK");
switch (defaultDark) {
case "DARK":
setTheme(R.style.Transparent);
break;
case "SOLARIZED_DARK":
setTheme(R.style.TransparentSolarized);
break;
case "BLACK":
setTheme(R.style.TransparentBlack);
break;
case "DRACULA":
setTheme(R.style.TransparentDracula);
break;
}
break;
}
} else {
switch (currentTheme) {
case "LIGHT":
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
setTheme(R.style.Transparent);
break;
case "DARK":
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
setTheme(R.style.Transparent);
break;
case "SOLARIZED_LIGHT":
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
setTheme(R.style.TransparentSolarized);
break;
case "SOLARIZED_DARK":
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
setTheme(R.style.TransparentSolarized);
break;
case "BLACK":
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
setTheme(R.style.TransparentBlack);
break;
case "DRACULA":
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
setTheme(R.style.TransparentDracula);
break;
}
}
super.onCreate(savedInstanceState);
boolean dynamicColor = sharedpreferences.getBoolean(getString(R.string.SET_DYNAMICCOLOR), false);
if (dynamicColor) {
DynamicColors.applyToActivityIfAvailable(this);
}
if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
Window window = getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(Color.BLACK);
window.setNavigationBarColor(Color.BLACK);
}
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N) {
ThemeHelper.adjustFontScale(this, getResources().getConfiguration());
}
Helper.setLocale(this);
}
@Override
protected void attachBaseContext(Context newBase) {
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N) {
final Configuration override = new Configuration(newBase.getResources().getConfiguration());
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(newBase);
override.fontScale = prefs.getFloat(newBase.getString(R.string.SET_FONT_SCALE), 1.1f);
applyOverrideConfiguration(override);
}
super.attachBaseContext(newBase);
}
}

View file

@ -1,4 +1,4 @@
package app.fedilab.android.activities; package app.fedilab.android.mastodon.activities;
/* Copyright 2022 Thomas Schneider /* Copyright 2022 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -15,7 +15,6 @@ package app.fedilab.android.activities;
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
@ -25,27 +24,27 @@ import android.view.MenuItem;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.client.entities.app.Account;
import app.fedilab.android.client.entities.app.BaseAccount;
import app.fedilab.android.client.entities.app.CacheAccount;
import app.fedilab.android.client.entities.app.StatusCache;
import app.fedilab.android.client.entities.app.StatusDraft;
import app.fedilab.android.databinding.ActivityCacheBinding; import app.fedilab.android.databinding.ActivityCacheBinding;
import app.fedilab.android.exception.DBException; import app.fedilab.android.mastodon.client.entities.app.Account;
import app.fedilab.android.helper.CacheHelper; import app.fedilab.android.mastodon.client.entities.app.BaseAccount;
import app.fedilab.android.helper.Helper; import app.fedilab.android.mastodon.client.entities.app.CacheAccount;
import app.fedilab.android.helper.ThemeHelper; import app.fedilab.android.mastodon.client.entities.app.StatusCache;
import app.fedilab.android.ui.drawer.CacheAdapter; import app.fedilab.android.mastodon.client.entities.app.StatusDraft;
import app.fedilab.android.mastodon.exception.DBException;
import app.fedilab.android.mastodon.helper.CacheHelper;
import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.mastodon.ui.drawer.CacheAdapter;
public class CacheActivity extends BaseActivity { public class CacheActivity extends BaseBarActivity {
private ActivityCacheBinding binding; private ActivityCacheBinding binding;
private List<CacheAccount> cacheAccounts; private List<CacheAccount> cacheAccounts;
@ -54,12 +53,11 @@ public class CacheActivity extends BaseActivity {
@Override @Override
protected void onCreate(@Nullable Bundle savedInstanceState) { protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
ThemeHelper.applyThemeBar(this);
binding = ActivityCacheBinding.inflate(getLayoutInflater()); binding = ActivityCacheBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); setContentView(binding.getRoot());
if (getSupportActionBar() != null) { if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
} }
CacheHelper.getCacheValues(CacheActivity.this, size -> { CacheHelper.getCacheValues(CacheActivity.this, size -> {
if (size > 0) { if (size > 0) {
@ -123,7 +121,7 @@ public class CacheActivity extends BaseActivity {
finish(); finish();
return true; return true;
} else if (item.getItemId() == R.id.action_clear) { } else if (item.getItemId() == R.id.action_clear) {
AlertDialog.Builder deleteConfirm = new AlertDialog.Builder(CacheActivity.this, Helper.dialogStyle()); AlertDialog.Builder deleteConfirm = new MaterialAlertDialogBuilder(CacheActivity.this);
deleteConfirm.setTitle(getString(R.string.delete_cache)); deleteConfirm.setTitle(getString(R.string.delete_cache));
deleteConfirm.setMessage(getString(R.string.delete_cache_message)); deleteConfirm.setMessage(getString(R.string.delete_cache_message));
deleteConfirm.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss()); deleteConfirm.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
@ -133,7 +131,21 @@ public class CacheActivity extends BaseActivity {
size = size / 1000000.0f; size = size / 1000000.0f;
} }
binding.fileCacheSize.setText(String.format("%s %s", String.format(Locale.getDefault(), "%.2f", size), getString(R.string.cache_units))); binding.fileCacheSize.setText(String.format("%s %s", String.format(Locale.getDefault(), "%.2f", size), getString(R.string.cache_units)));
cacheAdapter.notifyDataSetChanged(); AlertDialog.Builder restartBuilder = new MaterialAlertDialogBuilder(CacheActivity.this);
restartBuilder.setMessage(getString(R.string.restart_the_app));
restartBuilder.setNegativeButton(R.string.no, (dialogRestart, whichRestart) -> {
recreate();
dialogRestart.dismiss();
});
restartBuilder.setPositiveButton(R.string.restart, (dialogRestart, whichRestart) -> {
dialogRestart.dismiss();
Helper.restart(CacheActivity.this);
});
AlertDialog alertDialog = restartBuilder.create();
if (!isFinishing()) {
alertDialog.show();
}
})); }));
dialog.dismiss(); dialog.dismiss();
}); });

View file

@ -1,4 +1,4 @@
package app.fedilab.android.activities; package app.fedilab.android.mastodon.activities;
/* Copyright 2021 Thomas Schneider /* Copyright 2021 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -18,7 +18,6 @@ package app.fedilab.android.activities;
import static app.fedilab.android.BaseMainActivity.currentAccount; import static app.fedilab.android.BaseMainActivity.currentAccount;
import static app.fedilab.android.BaseMainActivity.currentInstance; import static app.fedilab.android.BaseMainActivity.currentInstance;
import static app.fedilab.android.BaseMainActivity.emojis; import static app.fedilab.android.BaseMainActivity.emojis;
import static app.fedilab.android.ui.drawer.ComposeAdapter.prepareDraft;
import android.Manifest; import android.Manifest;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
@ -28,7 +27,6 @@ import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.graphics.drawable.ColorDrawable;
import android.net.Uri; import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
@ -57,6 +55,8 @@ import androidx.work.OneTimeWorkRequest;
import androidx.work.OutOfQuotaPolicy; import androidx.work.OutOfQuotaPolicy;
import androidx.work.WorkManager; import androidx.work.WorkManager;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import java.io.File; import java.io.File;
import java.text.ParseException; import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
@ -64,41 +64,42 @@ import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Set;
import java.util.Timer; import java.util.Timer;
import java.util.TimerTask; import java.util.TimerTask;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import app.fedilab.android.BaseMainActivity; import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.client.entities.api.Attachment; import app.fedilab.android.activities.MainActivity;
import app.fedilab.android.client.entities.api.Context;
import app.fedilab.android.client.entities.api.EmojiInstance;
import app.fedilab.android.client.entities.api.Instance;
import app.fedilab.android.client.entities.api.Mention;
import app.fedilab.android.client.entities.api.ScheduledStatus;
import app.fedilab.android.client.entities.api.Status;
import app.fedilab.android.client.entities.app.BaseAccount;
import app.fedilab.android.client.entities.app.Languages;
import app.fedilab.android.client.entities.app.StatusDraft;
import app.fedilab.android.databinding.ActivityPaginationBinding; import app.fedilab.android.databinding.ActivityPaginationBinding;
import app.fedilab.android.databinding.PopupContactBinding; import app.fedilab.android.databinding.PopupContactBinding;
import app.fedilab.android.exception.DBException; import app.fedilab.android.mastodon.client.entities.api.Account;
import app.fedilab.android.helper.DividerDecorationSimple; import app.fedilab.android.mastodon.client.entities.api.Attachment;
import app.fedilab.android.helper.Helper; import app.fedilab.android.mastodon.client.entities.api.Context;
import app.fedilab.android.helper.MastodonHelper; import app.fedilab.android.mastodon.client.entities.api.EmojiInstance;
import app.fedilab.android.helper.MediaHelper; import app.fedilab.android.mastodon.client.entities.api.Instance;
import app.fedilab.android.helper.ThemeHelper; import app.fedilab.android.mastodon.client.entities.api.Mention;
import app.fedilab.android.interfaces.OnDownloadInterface; import app.fedilab.android.mastodon.client.entities.api.ScheduledStatus;
import app.fedilab.android.jobs.ComposeWorker; import app.fedilab.android.mastodon.client.entities.api.Status;
import app.fedilab.android.jobs.ScheduleThreadWorker; import app.fedilab.android.mastodon.client.entities.app.BaseAccount;
import app.fedilab.android.services.ThreadMessageService; import app.fedilab.android.mastodon.client.entities.app.StatusDraft;
import app.fedilab.android.ui.drawer.AccountsReplyAdapter; import app.fedilab.android.mastodon.exception.DBException;
import app.fedilab.android.ui.drawer.ComposeAdapter; import app.fedilab.android.mastodon.helper.DividerDecorationSimple;
import app.fedilab.android.viewmodel.mastodon.AccountsVM; import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.viewmodel.mastodon.StatusesVM; import app.fedilab.android.mastodon.helper.MastodonHelper;
import app.fedilab.android.mastodon.helper.MediaHelper;
import app.fedilab.android.mastodon.interfaces.OnDownloadInterface;
import app.fedilab.android.mastodon.jobs.ComposeWorker;
import app.fedilab.android.mastodon.jobs.ScheduleThreadWorker;
import app.fedilab.android.mastodon.services.ThreadMessageService;
import app.fedilab.android.mastodon.ui.drawer.AccountsReplyAdapter;
import app.fedilab.android.mastodon.ui.drawer.ComposeAdapter;
import app.fedilab.android.mastodon.viewmodel.mastodon.AccountsVM;
import app.fedilab.android.mastodon.viewmodel.mastodon.StatusesVM;
import es.dmoral.toasty.Toasty; import es.dmoral.toasty.Toasty;
public class ComposeActivity extends BaseActivity implements ComposeAdapter.ManageDrafts, AccountsReplyAdapter.ActionDone { public class ComposeActivity extends BaseActivity implements ComposeAdapter.ManageDrafts, AccountsReplyAdapter.ActionDone, ComposeAdapter.promptDraftListener {
public static final int MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE = 754; public static final int MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE = 754;
@ -107,11 +108,9 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
public static final int TAKE_PHOTO = 5600; public static final int TAKE_PHOTO = 5600;
private final Timer timer = new Timer(); private final Timer timer = new Timer();
private List<Status> statusList; private List<Status> statusList;
private Status statusReply, statusMention; private Status statusReply, statusMention, statusQuoted;
private StatusDraft statusDraft; private StatusDraft statusDraft;
private ComposeAdapter composeAdapter; private ComposeAdapter composeAdapter;
private final BroadcastReceiver imageReceiver = new BroadcastReceiver() { private final BroadcastReceiver imageReceiver = new BroadcastReceiver() {
@Override @Override
public void onReceive(android.content.Context context, Intent intent) { public void onReceive(android.content.Context context, Intent intent) {
@ -127,6 +126,7 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
if (focusX != -2) { if (focusX != -2) {
attachment.focus = focusX + "," + focusY; attachment.focus = focusX + "," + focusY;
} }
composeAdapter.notifyItemChanged(position); composeAdapter.notifyItemChanged(position);
break; break;
} }
@ -137,18 +137,18 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
} }
} }
}; };
private boolean promptSaveDraft;
private boolean restoredDraft;
private List<Attachment> sharedAttachments;
private ActivityPaginationBinding binding; private ActivityPaginationBinding binding;
private BaseAccount account; private BaseAccount account;
private String instance, token; private String instance, token;
private Uri photoFileUri; private Uri photoFileUri;
private ScheduledStatus scheduledStatus; private ScheduledStatus scheduledStatus;
private String visibility; private String visibility;
private app.fedilab.android.client.entities.api.Account accountMention; private Account accountMention;
private String statusReplyId; private String statusReplyId;
private app.fedilab.android.client.entities.api.Account mentionBooster; private Account mentionBooster;
private ArrayList<Uri> sharedUriList = new ArrayList<>();
private Uri sharedUri;
private String sharedSubject, sharedContent, sharedTitle, sharedDescription, shareURL, sharedUrlMedia; private String sharedSubject, sharedContent, sharedTitle, sharedDescription, shareURL, sharedUrlMedia;
private String editMessageId; private String editMessageId;
@ -211,10 +211,11 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
private void storeDraftWarning() { private void storeDraftWarning() {
if (statusDraft == null) { if (statusDraft == null) {
statusDraft = prepareDraft(statusList, composeAdapter, account.instance, account.user_id); statusDraft = ComposeAdapter.prepareDraft(statusList, composeAdapter, account.instance, account.user_id);
} }
if (canBeSent(statusDraft)) { if (canBeSent(statusDraft)) {
AlertDialog.Builder alt_bld = new AlertDialog.Builder(ComposeActivity.this, Helper.dialogStyle()); if (promptSaveDraft) {
AlertDialog.Builder alt_bld = new MaterialAlertDialogBuilder(ComposeActivity.this);
alt_bld.setMessage(R.string.save_draft); alt_bld.setMessage(R.string.save_draft);
alt_bld.setPositiveButton(R.string.save, (dialog, id) -> { alt_bld.setPositiveButton(R.string.save, (dialog, id) -> {
dialog.dismiss(); dialog.dismiss();
@ -223,11 +224,26 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
}); });
alt_bld.setNegativeButton(R.string.no, (dialog, id) -> { alt_bld.setNegativeButton(R.string.no, (dialog, id) -> {
try {
new StatusDraft(ComposeActivity.this).removeDraft(statusDraft);
} catch (DBException e) {
e.printStackTrace();
}
dialog.dismiss(); dialog.dismiss();
finish(); finish();
}); });
AlertDialog alert = alt_bld.create(); AlertDialog alert = alt_bld.create();
alert.show(); alert.show();
} else {
if (!restoredDraft) {
try {
new StatusDraft(ComposeActivity.this).removeDraft(statusDraft);
} catch (DBException e) {
e.printStackTrace();
}
}
finish();
}
} else { } else {
finish(); finish();
} }
@ -274,6 +290,7 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
statusList.add(initialStatus); statusList.add(initialStatus);
statusList.add(statusDraft.statusDraftList.get(0)); statusList.add(statusDraft.statusDraftList.get(0));
composeAdapter = new ComposeAdapter(statusList, context.ancestors.size(), account, accountMention, visibility, editMessageId); composeAdapter = new ComposeAdapter(statusList, context.ancestors.size(), account, accountMention, visibility, editMessageId);
composeAdapter.promptDraftListener = this;
composeAdapter.manageDrafts = this; composeAdapter.manageDrafts = this;
LinearLayoutManager mLayoutManager = new LinearLayoutManager(ComposeActivity.this); LinearLayoutManager mLayoutManager = new LinearLayoutManager(ComposeActivity.this);
binding.recyclerView.setLayoutManager(mLayoutManager); binding.recyclerView.setLayoutManager(mLayoutManager);
@ -299,7 +316,7 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
} else if (item.getItemId() == R.id.action_photo_camera) { } else if (item.getItemId() == R.id.action_photo_camera) {
photoFileUri = MediaHelper.dispatchTakePictureIntent(ComposeActivity.this); photoFileUri = MediaHelper.dispatchTakePictureIntent(ComposeActivity.this);
} else if (item.getItemId() == R.id.action_contacts) { } else if (item.getItemId() == R.id.action_contacts) {
AlertDialog.Builder builderSingle = new AlertDialog.Builder(ComposeActivity.this, Helper.dialogStyle()); AlertDialog.Builder builderSingle = new MaterialAlertDialogBuilder(ComposeActivity.this);
builderSingle.setTitle(getString(R.string.select_accounts)); builderSingle.setTitle(getString(R.string.select_accounts));
PopupContactBinding popupContactBinding = PopupContactBinding.inflate(getLayoutInflater(), new LinearLayout(ComposeActivity.this), false); PopupContactBinding popupContactBinding = PopupContactBinding.inflate(getLayoutInflater(), new LinearLayout(ComposeActivity.this), false);
@ -375,70 +392,31 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
} }
} else if (item.getItemId() == R.id.action_schedule) { } else if (item.getItemId() == R.id.action_schedule) {
if (statusDraft == null) { if (statusDraft == null) {
statusDraft = prepareDraft(statusList, composeAdapter, account.instance, account.user_id); statusDraft = ComposeAdapter.prepareDraft(statusList, composeAdapter, account.instance, account.user_id);
} }
if (canBeSent(statusDraft)) { if (canBeSent(statusDraft)) {
MediaHelper.scheduleMessage(ComposeActivity.this, date -> storeDraft(true, date)); MediaHelper.scheduleMessage(ComposeActivity.this, date -> storeDraft(true, date));
} else { } else {
Toasty.info(ComposeActivity.this, getString(R.string.toot_error_no_content), Toasty.LENGTH_SHORT).show(); Toasty.info(ComposeActivity.this, getString(R.string.toot_error_no_content), Toasty.LENGTH_SHORT).show();
} }
} else if (item.getItemId() == R.id.action_language) {
final SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(ComposeActivity.this);
List<Languages.Language> languages = Languages.get(ComposeActivity.this);
String[] codesArr = new String[0];
String[] languagesArr = new String[0];
String currentCode = sharedpreferences.getString(getString(R.string.SET_COMPOSE_LANGUAGE) + account.user_id + account.instance, null);
int selection = 0;
if (languages != null) {
codesArr = new String[languages.size()];
languagesArr = new String[languages.size()];
int i = 0;
for (Languages.Language language : languages) {
codesArr[i] = language.code;
languagesArr[i] = language.language;
if (currentCode != null && currentCode.equalsIgnoreCase(language.code)) {
selection = i;
}
i++;
}
}
SharedPreferences.Editor editor = sharedpreferences.edit();
AlertDialog.Builder builder = new AlertDialog.Builder(ComposeActivity.this, Helper.dialogStyle());
builder.setTitle(getString(R.string.message_language));
builder.setSingleChoiceItems(languagesArr, selection, null);
String[] finalCodesArr = codesArr;
builder.setPositiveButton(R.string.validate, (dialog, which) -> {
int selectedPosition = ((AlertDialog) dialog).getListView().getCheckedItemPosition();
editor.putString(getString(R.string.SET_COMPOSE_LANGUAGE) + account.user_id + account.instance, finalCodesArr[selectedPosition]);
editor.apply();
dialog.dismiss();
});
builder.setNegativeButton(R.string.reset, (dialog, which) -> {
editor.putString(getString(R.string.SET_COMPOSE_LANGUAGE) + account.user_id + account.instance, null);
editor.apply();
dialog.dismiss();
});
builder.create().show();
} }
return true; return true;
} }
private void onRetrieveContact(PopupContactBinding binding, List<app.fedilab.android.client.entities.api.Account> accounts) { private void onRetrieveContact(PopupContactBinding popupContactBinding, List<Account> accounts) {
binding.loader.setVisibility(View.GONE); popupContactBinding.loader.setVisibility(View.GONE);
if (accounts == null) { if (accounts == null) {
accounts = new ArrayList<>(); accounts = new ArrayList<>();
} }
List<Boolean> checkedValues = new ArrayList<>(); List<Boolean> checkedValues = new ArrayList<>();
List<app.fedilab.android.client.entities.api.Account> contacts = new ArrayList<>(accounts); List<Account> contacts = new ArrayList<>(accounts);
for (app.fedilab.android.client.entities.api.Account account : contacts) { for (Account account : contacts) {
checkedValues.add(composeAdapter.getLastComposeContent().contains("@" + account.acct)); checkedValues.add(composeAdapter.getLastComposeContent().contains("@" + account.acct));
} }
AccountsReplyAdapter contactAdapter = new AccountsReplyAdapter(contacts, checkedValues); AccountsReplyAdapter contactAdapter = new AccountsReplyAdapter(contacts, checkedValues);
binding.lvAccountsSearch.setAdapter(contactAdapter); contactAdapter.actionDone = ComposeActivity.this;
binding.lvAccountsSearch.setLayoutManager(new LinearLayoutManager(ComposeActivity.this)); popupContactBinding.lvAccountsSearch.setAdapter(contactAdapter);
popupContactBinding.lvAccountsSearch.setLayoutManager(new LinearLayoutManager(ComposeActivity.this));
} }
@Override @Override
@ -466,15 +444,16 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
ThemeHelper.applyTheme(this);
binding = ActivityPaginationBinding.inflate(getLayoutInflater()); binding = ActivityPaginationBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); setContentView(binding.getRoot());
setSupportActionBar(binding.toolbar); setSupportActionBar(binding.toolbar);
promptSaveDraft = false;
restoredDraft = false;
ActionBar actionBar = getSupportActionBar(); ActionBar actionBar = getSupportActionBar();
//Remove title //Remove title
if (actionBar != null) { if (actionBar != null) {
actionBar.setDisplayShowTitleEnabled(false); actionBar.setDisplayShowTitleEnabled(false);
actionBar.setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
} }
if (getSupportActionBar() != null) { if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
@ -487,6 +466,7 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
Bundle b = getIntent().getExtras(); Bundle b = getIntent().getExtras();
if (b != null) { if (b != null) {
statusReply = (Status) b.getSerializable(Helper.ARG_STATUS_REPLY); statusReply = (Status) b.getSerializable(Helper.ARG_STATUS_REPLY);
statusQuoted = (Status) b.getSerializable(Helper.ARG_QUOTED_MESSAGE);
statusDraft = (StatusDraft) b.getSerializable(Helper.ARG_STATUS_DRAFT); statusDraft = (StatusDraft) b.getSerializable(Helper.ARG_STATUS_DRAFT);
scheduledStatus = (ScheduledStatus) b.getSerializable(Helper.ARG_STATUS_SCHEDULED); scheduledStatus = (ScheduledStatus) b.getSerializable(Helper.ARG_STATUS_SCHEDULED);
statusReplyId = b.getString(Helper.ARG_STATUS_REPLY_ID); statusReplyId = b.getString(Helper.ARG_STATUS_REPLY_ID);
@ -501,11 +481,10 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
} else if (visibility == null && currentAccount != null && currentAccount.mastodon_account != null && currentAccount.mastodon_account.source != null) { } else if (visibility == null && currentAccount != null && currentAccount.mastodon_account != null && currentAccount.mastodon_account.source != null) {
visibility = currentAccount.mastodon_account.source.privacy; visibility = currentAccount.mastodon_account.source.privacy;
} }
mentionBooster = (app.fedilab.android.client.entities.api.Account) b.getSerializable(Helper.ARG_MENTION_BOOSTER); mentionBooster = (Account) b.getSerializable(Helper.ARG_MENTION_BOOSTER);
accountMention = (app.fedilab.android.client.entities.api.Account) b.getSerializable(Helper.ARG_ACCOUNT_MENTION); accountMention = (Account) b.getSerializable(Helper.ARG_ACCOUNT_MENTION);
//Shared elements //Shared elements
sharedUriList = b.getParcelableArrayList(Helper.ARG_SHARE_URI_LIST); sharedAttachments = (ArrayList<Attachment>) b.getSerializable(Helper.ARG_MEDIA_ATTACHMENTS);
sharedUri = b.getParcelable(Helper.ARG_SHARE_URI);
sharedUrlMedia = b.getString(Helper.ARG_SHARE_URL_MEDIA); sharedUrlMedia = b.getString(Helper.ARG_SHARE_URL_MEDIA);
sharedSubject = b.getString(Helper.ARG_SHARE_SUBJECT, null); sharedSubject = b.getString(Helper.ARG_SHARE_SUBJECT, null);
sharedContent = b.getString(Helper.ARG_SHARE_CONTENT, null); sharedContent = b.getString(Helper.ARG_SHARE_CONTENT, null);
@ -520,7 +499,6 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
if (sharedTitle != null && sharedSubject != null && sharedSubject.length() > sharedTitle.length()) { if (sharedTitle != null && sharedSubject != null && sharedSubject.length() > sharedTitle.length()) {
sharedTitle = sharedSubject; sharedTitle = sharedSubject;
} }
binding.toolbar.setPopupTheme(Helper.popupStyle());
//Edit a scheduled status from server //Edit a scheduled status from server
if (scheduledStatus != null) { if (scheduledStatus != null) {
statusDraft = new StatusDraft(); statusDraft = new StatusDraft();
@ -560,10 +538,10 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
if (token == null) { if (token == null) {
token = account.token; token = account.token;
} }
if (emojis == null || !emojis.containsKey(currentInstance)) { if (emojis == null || !emojis.containsKey(instance)) {
new Thread(() -> { new Thread(() -> {
try { try {
emojis.put(currentInstance, new EmojiInstance(ComposeActivity.this).getEmojiList(currentInstance)); emojis.put(instance, new EmojiInstance(ComposeActivity.this).getEmojiList(instance));
} catch (DBException e) { } catch (DBException e) {
e.printStackTrace(); e.printStackTrace();
} }
@ -581,6 +559,9 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
List<Status> statusDraftList = new ArrayList<>(); List<Status> statusDraftList = new ArrayList<>();
Status status = new Status(); Status status = new Status();
status.id = Helper.generateIdString(); status.id = Helper.generateIdString();
if (statusQuoted != null) {
status.quote_id = statusQuoted.id;
}
statusDraftList.add(status); statusDraftList.add(status);
if (statusReplyId != null && statusDraft != null) {//Delete and redraft if (statusReplyId != null && statusDraft != null) {//Delete and redraft
@ -600,6 +581,7 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
} }
}); });
} else if (statusDraft != null) {//Restore a draft with all messages } else if (statusDraft != null) {//Restore a draft with all messages
restoredDraft = true;
if (statusDraft.statusReplyList != null) { if (statusDraft.statusReplyList != null) {
statusList.addAll(statusDraft.statusReplyList); statusList.addAll(statusDraft.statusReplyList);
binding.recyclerView.addItemDecoration(new DividerDecorationSimple(ComposeActivity.this, statusList)); binding.recyclerView.addItemDecoration(new DividerDecorationSimple(ComposeActivity.this, statusList));
@ -608,6 +590,7 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
statusList.addAll(statusDraft.statusDraftList); statusList.addAll(statusDraft.statusDraftList);
composeAdapter = new ComposeAdapter(statusList, statusCount, account, accountMention, visibility, editMessageId); composeAdapter = new ComposeAdapter(statusList, statusCount, account, accountMention, visibility, editMessageId);
composeAdapter.manageDrafts = this; composeAdapter.manageDrafts = this;
composeAdapter.promptDraftListener = this;
LinearLayoutManager mLayoutManager = new LinearLayoutManager(ComposeActivity.this); LinearLayoutManager mLayoutManager = new LinearLayoutManager(ComposeActivity.this);
binding.recyclerView.setLayoutManager(mLayoutManager); binding.recyclerView.setLayoutManager(mLayoutManager);
binding.recyclerView.setAdapter(composeAdapter); binding.recyclerView.setAdapter(composeAdapter);
@ -655,21 +638,54 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
} }
if (statusReply.spoiler_text != null) { if (statusReply.spoiler_text != null) {
statusDraftList.get(0).spoiler_text = statusReply.spoiler_text; statusDraftList.get(0).spoiler_text = statusReply.spoiler_text;
if (statusReply.spoiler_text.trim().length() > 0) {
statusDraftList.get(0).spoilerChecked = true;
}
}
if (statusReply.language != null && !statusReply.language.isEmpty()) {
Set<String> storedLanguages = sharedpreferences.getStringSet(getString(R.string.SET_SELECTED_LANGUAGE), null);
if (storedLanguages == null || storedLanguages.size() == 0) {
statusDraftList.get(0).language = statusReply.language;
} else {
if (storedLanguages.contains(statusReply.language)) {
statusDraftList.get(0).language = statusReply.language;
} else {
String currentCode = sharedpreferences.getString(getString(R.string.SET_COMPOSE_LANGUAGE) + account.user_id + account.instance, Locale.getDefault().getLanguage());
if (currentCode.isEmpty()) {
currentCode = "EN";
}
statusDraftList.get(0).language = currentCode;
}
}
} }
//StatusDraftList at this point should only have one element //StatusDraftList at this point should only have one element
statusList.addAll(statusDraftList); statusList.addAll(statusDraftList);
composeAdapter = new ComposeAdapter(statusList, statusCount, account, accountMention, visibility, editMessageId); composeAdapter = new ComposeAdapter(statusList, statusCount, account, accountMention, visibility, editMessageId);
composeAdapter.manageDrafts = this; composeAdapter.manageDrafts = this;
composeAdapter.promptDraftListener = this;
LinearLayoutManager mLayoutManager = new LinearLayoutManager(ComposeActivity.this); LinearLayoutManager mLayoutManager = new LinearLayoutManager(ComposeActivity.this);
binding.recyclerView.setLayoutManager(mLayoutManager); binding.recyclerView.setLayoutManager(mLayoutManager);
binding.recyclerView.setAdapter(composeAdapter); binding.recyclerView.setAdapter(composeAdapter);
statusesVM.getContext(currentInstance, BaseMainActivity.currentToken, statusReply.id) statusesVM.getContext(currentInstance, BaseMainActivity.currentToken, statusReply.id)
.observe(ComposeActivity.this, this::initializeContextView); .observe(ComposeActivity.this, this::initializeContextView);
} else if (statusQuoted != null) {
statusList.add(statusQuoted);
int statusCount = statusList.size();
statusDraftList.get(0).quote_id = statusQuoted.id;
//StatusDraftList at this point should only have one element
statusList.addAll(statusDraftList);
composeAdapter = new ComposeAdapter(statusList, statusCount, account, accountMention, visibility, editMessageId);
composeAdapter.manageDrafts = this;
composeAdapter.promptDraftListener = this;
LinearLayoutManager mLayoutManager = new LinearLayoutManager(ComposeActivity.this);
binding.recyclerView.setLayoutManager(mLayoutManager);
binding.recyclerView.setAdapter(composeAdapter);
} else { } else {
//Compose without replying //Compose without replying
statusList.addAll(statusDraftList); statusList.addAll(statusDraftList);
composeAdapter = new ComposeAdapter(statusList, 0, account, accountMention, visibility, editMessageId); composeAdapter = new ComposeAdapter(statusList, 0, account, accountMention, visibility, editMessageId);
composeAdapter.manageDrafts = this; composeAdapter.manageDrafts = this;
composeAdapter.promptDraftListener = this;
LinearLayoutManager mLayoutManager = new LinearLayoutManager(ComposeActivity.this); LinearLayoutManager mLayoutManager = new LinearLayoutManager(ComposeActivity.this);
binding.recyclerView.setLayoutManager(mLayoutManager); binding.recyclerView.setLayoutManager(mLayoutManager);
binding.recyclerView.setAdapter(composeAdapter); binding.recyclerView.setAdapter(composeAdapter);
@ -686,26 +702,27 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
timer.scheduleAtFixedRate(new TimerTask() { timer.scheduleAtFixedRate(new TimerTask() {
@Override @Override
public void run() { public void run() {
if (promptSaveDraft) {
storeDraft(false); storeDraft(false);
} }
}
}, 0, 10000); }, 0, 10000);
} }
if (sharedUriList != null && sharedUriList.size() > 0) { if (sharedAttachments != null && sharedAttachments.size() > 0) {
for (Attachment attachment : sharedAttachments) {
Handler handler = new Handler(); composeAdapter.addAttachment(-1, attachment);
handler.postDelayed(() -> { }
List<Uri> uris = new ArrayList<>(sharedUriList); } /*else if (sharedUri != null && !sharedUri.toString().startsWith("http")) {
composeAdapter.addAttachment(-1, uris);
}, 1000);
} else if (sharedUri != null && !sharedUri.toString().startsWith("http")) {
Handler handler = new Handler();
handler.postDelayed(() -> {
List<Uri> uris = new ArrayList<>(); List<Uri> uris = new ArrayList<>();
uris.add(sharedUri); uris.add(sharedUri);
composeAdapter.addAttachment(-1, uris); Helper.createAttachmentFromUri(ComposeActivity.this, uris, attachments -> {
}, 1000); for(Attachment attachment: attachments) {
} else if (shareURL != null) { composeAdapter.addAttachment(-1, attachment);
}
});
} */ else if (shareURL != null) {
Helper.download(ComposeActivity.this, sharedUrlMedia, new OnDownloadInterface() { Helper.download(ComposeActivity.this, sharedUrlMedia, new OnDownloadInterface() {
@Override @Override
public void onDownloaded(String saveFilePath, String downloadUrl, Error error) { public void onDownloaded(String saveFilePath, String downloadUrl, Error error) {
@ -731,18 +748,20 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
Status status = new Status(); Status status = new Status();
status.id = Helper.generateIdString(); status.id = Helper.generateIdString();
status.mentions = statusList.get(position).mentions; status.mentions = statusList.get(position - 1).mentions;
status.visibility = statusList.get(position).visibility; status.visibility = statusList.get(position - 1).visibility;
final SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(ComposeActivity.this); final SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(ComposeActivity.this);
boolean unlistedReplies = sharedpreferences.getBoolean(getString(R.string.SET_UNLISTED_REPLIES), true); boolean unlistedReplies = sharedpreferences.getBoolean(getString(R.string.SET_UNLISTED_REPLIES), true);
if (status.visibility.equalsIgnoreCase("public") && unlistedReplies) { if (status.visibility.equalsIgnoreCase("public") && unlistedReplies) {
status.visibility = "unlisted"; status.visibility = "unlisted";
} }
status.spoiler_text = statusList.get(position).spoiler_text; status.spoiler_text = statusList.get(position - 1).spoiler_text;
status.sensitive = statusList.get(position).sensitive; status.sensitive = statusList.get(position - 1).sensitive;
statusList.add(status); statusList.add(status);
composeAdapter.notifyItemInserted(position + 1); composeAdapter.notifyItemInserted(position);
binding.recyclerView.smoothScrollToPosition(position + 1); composeAdapter.notifyItemRangeChanged(0, statusList.size());
binding.recyclerView.smoothScrollToPosition(statusList.size());
} }
@Override @Override
@ -846,6 +865,11 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
WorkManager.getInstance(ComposeActivity.this).enqueue(oneTimeWorkRequest); WorkManager.getInstance(ComposeActivity.this).enqueue(oneTimeWorkRequest);
statusDraft.workerUuid = oneTimeWorkRequest.getId(); statusDraft.workerUuid = oneTimeWorkRequest.getId();
statusDraft.scheduled_at = date; statusDraft.scheduled_at = date;
try {
new StatusDraft(ComposeActivity.this).updateStatusDraft(statusDraft);
} catch (DBException e) {
e.printStackTrace();
}
Handler mainHandler = new Handler(Looper.getMainLooper()); Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> { Runnable myRunnable = () -> {
Toasty.info(ComposeActivity.this, getString(R.string.toot_scheduled), Toasty.LENGTH_LONG).show(); Toasty.info(ComposeActivity.this, getString(R.string.toot_scheduled), Toasty.LENGTH_LONG).show();
@ -886,10 +910,14 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
private boolean canBeSent(StatusDraft statusDraft) { private boolean canBeSent(StatusDraft statusDraft) {
if (statusDraft == null || statusDraft.statusDraftList == null || statusDraft.statusDraftList.isEmpty()) { if (statusDraft == null) {
return false; return false;
} }
Status statusCheck = statusDraft.statusDraftList.get(0); List<Status> statuses = statusDraft.statusDraftList;
if (statuses == null || statuses.size() == 0) {
return false;
}
Status statusCheck = statuses.get(0);
if (statusCheck == null) { if (statusCheck == null) {
return false; return false;
} }
@ -905,6 +933,11 @@ public class ComposeActivity extends BaseActivity implements ComposeAdapter.Mana
composeAdapter.updateContent(isChecked, acct); composeAdapter.updateContent(isChecked, acct);
} }
@Override
public void promptDraft() {
promptSaveDraft = true;
}
public enum mediaType { public enum mediaType {
PHOTO, PHOTO,

View file

@ -0,0 +1,240 @@
package app.fedilab.android.mastodon.activities;
/* Copyright 2021 Thomas Schneider
*
* This file is a part of Fedilab
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */
import static app.fedilab.android.BaseMainActivity.currentAccount;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.TypedValue;
import android.view.Menu;
import android.view.MenuItem;
import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBar;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.preference.PreferenceManager;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R;
import app.fedilab.android.activities.MainActivity;
import app.fedilab.android.databinding.ActivityConversationBinding;
import app.fedilab.android.mastodon.client.entities.api.Status;
import app.fedilab.android.mastodon.client.entities.app.StatusCache;
import app.fedilab.android.mastodon.exception.DBException;
import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.mastodon.helper.MastodonHelper;
import app.fedilab.android.mastodon.ui.drawer.StatusAdapter;
import app.fedilab.android.mastodon.ui.fragment.timeline.FragmentMastodonContext;
import app.fedilab.android.mastodon.viewmodel.mastodon.StatusesVM;
import es.dmoral.toasty.Toasty;
public class ContextActivity extends BaseActivity implements FragmentMastodonContext.FirstMessage {
public static boolean expand;
public static boolean displayCW;
public static Resources.Theme theme;
Fragment currentFragment;
private Status firstMessage;
private String remote_instance;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityConversationBinding binding = ActivityConversationBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
setSupportActionBar(binding.toolbar);
ActionBar actionBar = getSupportActionBar();
//Remove title
if (actionBar != null) {
actionBar.setDisplayShowTitleEnabled(false);
}
binding.title.setText(R.string.context_conversation);
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(this);
float scale = sharedpreferences.getFloat(getString(R.string.SET_FONT_SCALE), 1.1f);
binding.title.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18 * 1.1f / scale);
if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true);
}
Bundle b = getIntent().getExtras();
displayCW = sharedpreferences.getBoolean(getString(R.string.SET_EXPAND_CW), false);
Status focusedStatus = null; // or other values
if (b != null) {
focusedStatus = (Status) b.getSerializable(Helper.ARG_STATUS);
remote_instance = b.getString(Helper.ARG_REMOTE_INSTANCE, null);
}
if (focusedStatus == null || currentAccount == null || currentAccount.mastodon_account == null) {
finish();
return;
}
MastodonHelper.loadPPMastodon(binding.profilePicture, currentAccount.mastodon_account);
Bundle bundle = new Bundle();
bundle.putSerializable(Helper.ARG_STATUS, focusedStatus);
bundle.putString(Helper.ARG_REMOTE_INSTANCE, remote_instance);
FragmentMastodonContext fragmentMastodonContext = new FragmentMastodonContext();
fragmentMastodonContext.firstMessage = this;
currentFragment = Helper.addFragment(getSupportFragmentManager(), R.id.nav_host_fragment_content_main, fragmentMastodonContext, bundle, null, null);
if (remote_instance == null) {
StatusesVM timelinesVM = new ViewModelProvider(ContextActivity.this).get(StatusesVM.class);
timelinesVM.getStatus(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, focusedStatus.id).observe(ContextActivity.this, status -> {
if (status != null) {
StatusCache statusCache = new StatusCache();
statusCache.instance = BaseMainActivity.currentInstance;
statusCache.user_id = BaseMainActivity.currentUserID;
statusCache.status = status;
statusCache.status_id = status.id;
//Update cache
new Thread(() -> {
try {
new StatusCache(getApplication()).updateIfExists(statusCache);
Handler mainHandler = new Handler(Looper.getMainLooper());
//Update UI
Runnable myRunnable = () -> StatusAdapter.sendAction(ContextActivity.this, Helper.ARG_STATUS_ACTION, status, null);
mainHandler.post(myRunnable);
} catch (DBException e) {
e.printStackTrace();
}
}).start();
}
});
}
}
@Override
public boolean onCreateOptionsMenu(@NonNull Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_context, menu);
MenuItem itemExpand = menu.findItem(R.id.action_expand);
if (expand) {
itemExpand.setIcon(R.drawable.ic_baseline_expand_less_24);
} else {
itemExpand.setIcon(R.drawable.ic_baseline_expand_more_24);
}
MenuItem itemDisplayCW = menu.findItem(R.id.action_show_cw);
if (displayCW) {
itemDisplayCW.setIcon(R.drawable.ic_baseline_remove_red_eye_24);
} else {
itemDisplayCW.setIcon(R.drawable.ic_outline_remove_red_eye_24);
}
MenuItem action_remote = menu.findItem(R.id.action_remote);
if (remote_instance != null) {
action_remote.setVisible(false);
} else {
if (firstMessage != null && !firstMessage.visibility.equalsIgnoreCase("direct") && !firstMessage.visibility.equalsIgnoreCase("private")) {
Pattern pattern = Helper.statusIdInUrl;
Matcher matcher = pattern.matcher(firstMessage.uri);
action_remote.setVisible(matcher.find());
} else {
action_remote.setVisible(false);
}
}
return true;
}
public void setCurrentFragment(FragmentMastodonContext fragmentMastodonContext) {
currentFragment = fragmentMastodonContext;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
finish();
return true;
} else if (item.getItemId() == R.id.action_expand) {
expand = !expand;
if (currentFragment != null && currentFragment instanceof FragmentMastodonContext) {
((FragmentMastodonContext) currentFragment).redraw();
}
invalidateOptionsMenu();
} else if (item.getItemId() == R.id.action_show_cw) {
displayCW = !displayCW;
if (currentFragment != null && currentFragment instanceof FragmentMastodonContext) {
((FragmentMastodonContext) currentFragment).refresh();
}
invalidateOptionsMenu();
} else if (item.getItemId() == R.id.action_remote) {
if (firstMessage == null) {
Toasty.warning(ContextActivity.this, getString(R.string.toast_try_later), Toasty.LENGTH_SHORT).show();
return true;
}
if (firstMessage.account.acct != null) {
String instance = null;
try {
URL url = new URL(firstMessage.uri);
instance = url.getHost();
} catch (MalformedURLException e) {
e.printStackTrace();
}
if (instance == null) {
Toasty.info(ContextActivity.this, getString(R.string.toast_error_fetch_message), Toasty.LENGTH_SHORT).show();
return true;
}
if (instance.equalsIgnoreCase(MainActivity.currentInstance)) {
Toasty.info(ContextActivity.this, getString(R.string.toast_on_your_instance), Toasty.LENGTH_SHORT).show();
return true;
}
Pattern pattern = Helper.statusIdInUrl;
Matcher matcher = pattern.matcher(firstMessage.uri);
String remoteId = null;
if (matcher.find()) {
remoteId = matcher.group(1);
}
if (remoteId != null) {
StatusesVM statusesVM = new ViewModelProvider(ContextActivity.this).get(StatusesVM.class);
String finalInstance = instance;
statusesVM.getStatus(instance, null, remoteId).observe(ContextActivity.this, status -> {
if (status != null) {
Intent intentContext = new Intent(ContextActivity.this, ContextActivity.class);
intentContext.putExtra(Helper.ARG_STATUS, status);
intentContext.putExtra(Helper.ARG_REMOTE_INSTANCE, finalInstance);
intentContext.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intentContext);
} else {
Toasty.warning(ContextActivity.this, getString(R.string.toast_error_fetch_message), Toasty.LENGTH_SHORT).show();
}
});
} else {
Toasty.warning(ContextActivity.this, getString(R.string.toast_error_fetch_message), Toasty.LENGTH_SHORT).show();
}
} else {
Toasty.warning(ContextActivity.this, getString(R.string.toast_error_fetch_message), Toasty.LENGTH_SHORT).show();
}
}
return true;
}
@Override
public void get(Status status) {
firstMessage = status;
invalidateOptionsMenu();
}
}

View file

@ -1,4 +1,4 @@
package app.fedilab.android.activities; package app.fedilab.android.mastodon.activities;
/* Copyright 2022 Thomas Schneider /* Copyright 2022 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -17,7 +17,6 @@ package app.fedilab.android.activities;
import static app.fedilab.android.BaseMainActivity.currentAccount; import static app.fedilab.android.BaseMainActivity.currentAccount;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.graphics.drawable.ColorDrawable;
import android.net.Uri; import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
@ -26,23 +25,21 @@ import android.text.TextUtils;
import android.view.MenuItem; import android.view.MenuItem;
import android.widget.Toast; import android.widget.Toast;
import androidx.core.content.ContextCompat;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.client.entities.api.Attachment;
import app.fedilab.android.client.entities.api.Emoji;
import app.fedilab.android.client.entities.api.Status;
import app.fedilab.android.client.entities.api.Tag;
import app.fedilab.android.databinding.ActivityCustomSharingBinding; import app.fedilab.android.databinding.ActivityCustomSharingBinding;
import app.fedilab.android.helper.Helper; import app.fedilab.android.mastodon.client.entities.api.Attachment;
import app.fedilab.android.helper.ThemeHelper; import app.fedilab.android.mastodon.client.entities.api.Emoji;
import app.fedilab.android.helper.customsharing.CustomSharingAsyncTask; import app.fedilab.android.mastodon.client.entities.api.Status;
import app.fedilab.android.helper.customsharing.CustomSharingResponse; import app.fedilab.android.mastodon.client.entities.api.Tag;
import app.fedilab.android.helper.customsharing.OnCustomSharingInterface; import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.mastodon.helper.customsharing.CustomSharingAsyncTask;
import app.fedilab.android.mastodon.helper.customsharing.CustomSharingResponse;
import app.fedilab.android.mastodon.helper.customsharing.OnCustomSharingInterface;
import es.dmoral.toasty.Toasty; import es.dmoral.toasty.Toasty;
@ -51,7 +48,7 @@ import es.dmoral.toasty.Toasty;
* Share status metadata to remote content aggregators * Share status metadata to remote content aggregators
*/ */
public class CustomSharingActivity extends BaseActivity implements OnCustomSharingInterface { public class CustomSharingActivity extends BaseBarActivity implements OnCustomSharingInterface {
private String title, keywords, custom_sharing_url, encodedCustomSharingURL; private String title, keywords, custom_sharing_url, encodedCustomSharingURL;
private String bundle_url; private String bundle_url;
@ -66,14 +63,13 @@ public class CustomSharingActivity extends BaseActivity implements OnCustomShari
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
ThemeHelper.applyThemeBar(this);
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(CustomSharingActivity.this); SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(CustomSharingActivity.this);
binding = ActivityCustomSharingBinding.inflate(getLayoutInflater()); binding = ActivityCustomSharingBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); setContentView(binding.getRoot());
if (getSupportActionBar() != null) { if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true); getSupportActionBar().setDisplayShowHomeEnabled(true);
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
} }
Bundle b = getIntent().getExtras(); Bundle b = getIntent().getExtras();
status = null; status = null;

View file

@ -0,0 +1,81 @@
package app.fedilab.android.mastodon.activities;
/* Copyright 2022 Thomas Schneider
*
* This file is a part of Fedilab
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */
import static app.fedilab.android.mastodon.client.entities.app.Timeline.TimeLineEnum.ACCOUNT_DIRECTORY;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import org.jetbrains.annotations.NotNull;
import app.fedilab.android.R;
import app.fedilab.android.databinding.ActivityDirectoryBinding;
import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.mastodon.ui.fragment.timeline.FragmentMastodonAccount;
public class DirectoryActivity extends BaseBarActivity {
private static boolean local = false;
private static String order = "active";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityDirectoryBinding binding = ActivityDirectoryBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
Bundle bundle = new Bundle();
bundle.putBoolean(Helper.ARG_DIRECTORY_LOCAL, local);
bundle.putString(Helper.ARG_DIRECTORY_ORDER, order);
bundle.putSerializable(Helper.ARG_TIMELINE_TYPE, ACCOUNT_DIRECTORY);
Helper.addFragment(getSupportFragmentManager(), R.id.nav_host_fragment_directory, new FragmentMastodonAccount(), bundle, null, null);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_directory, menu);
if (order.equals("active")) {
menu.findItem(R.id.order_active).setChecked(true);
} else {
menu.findItem(R.id.order_new).setChecked(true);
}
menu.findItem(R.id.action_local).setChecked(local);
return true;
}
@Override
public boolean onOptionsItemSelected(@NotNull MenuItem item) {
if (item.getItemId() == android.R.id.home) {
finish();
return true;
} else if (item.getItemId() == R.id.action_local) {
item.setChecked(!item.isChecked());
local = item.isChecked();
} else if (item.getItemId() == R.id.order_active) {
order = "active";
} else if (item.getItemId() == R.id.order_new) {
order = "new";
}
recreate();
return super.onOptionsItemSelected(item);
}
}

View file

@ -1,4 +1,4 @@
package app.fedilab.android.activities; package app.fedilab.android.mastodon.activities;
/* Copyright 2022 Thomas Schneider /* Copyright 2022 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -17,7 +17,6 @@ package app.fedilab.android.activities;
import static app.fedilab.android.BaseMainActivity.currentAccount; import static app.fedilab.android.BaseMainActivity.currentAccount;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
@ -30,25 +29,24 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import java.io.File; import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.client.entities.api.Attachment;
import app.fedilab.android.client.entities.api.Status;
import app.fedilab.android.client.entities.app.StatusDraft;
import app.fedilab.android.databinding.ActivityDraftsBinding; import app.fedilab.android.databinding.ActivityDraftsBinding;
import app.fedilab.android.exception.DBException; import app.fedilab.android.mastodon.client.entities.api.Attachment;
import app.fedilab.android.helper.Helper; import app.fedilab.android.mastodon.client.entities.api.Status;
import app.fedilab.android.helper.ThemeHelper; import app.fedilab.android.mastodon.client.entities.app.StatusDraft;
import app.fedilab.android.ui.drawer.StatusDraftAdapter; import app.fedilab.android.mastodon.exception.DBException;
import app.fedilab.android.viewmodel.mastodon.TimelinesVM; import app.fedilab.android.mastodon.ui.drawer.StatusDraftAdapter;
import app.fedilab.android.mastodon.viewmodel.mastodon.TimelinesVM;
public class DraftActivity extends BaseActivity implements StatusDraftAdapter.DraftActions { public class DraftActivity extends BaseActivity implements StatusDraftAdapter.DraftActions {
@ -62,20 +60,16 @@ public class DraftActivity extends BaseActivity implements StatusDraftAdapter.Dr
@Override @Override
protected void onCreate(@Nullable Bundle savedInstanceState) { protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
ThemeHelper.applyTheme(this);
binding = ActivityDraftsBinding.inflate(getLayoutInflater()); binding = ActivityDraftsBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); setContentView(binding.getRoot());
setSupportActionBar(binding.toolbar); setSupportActionBar(binding.toolbar);
ActionBar actionBar = getSupportActionBar(); ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
}
//Remove title //Remove title
if (actionBar != null) { if (actionBar != null) {
actionBar.setDisplayShowTitleEnabled(false); actionBar.setDisplayShowTitleEnabled(false);
} }
binding.toolbar.setPopupTheme(Helper.popupStyle());
binding.title.setText(R.string.drafts); binding.title.setText(R.string.drafts);
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(this); SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(this);
float scale = sharedpreferences.getFloat(getString(R.string.SET_FONT_SCALE), 1.1f); float scale = sharedpreferences.getFloat(getString(R.string.SET_FONT_SCALE), 1.1f);
@ -105,7 +99,7 @@ public class DraftActivity extends BaseActivity implements StatusDraftAdapter.Dr
finish(); finish();
return true; return true;
} else if (item.getItemId() == R.id.action_delete) { } else if (item.getItemId() == R.id.action_delete) {
AlertDialog.Builder unfollowConfirm = new AlertDialog.Builder(DraftActivity.this, Helper.dialogStyle()); AlertDialog.Builder unfollowConfirm = new MaterialAlertDialogBuilder(DraftActivity.this);
unfollowConfirm.setTitle(getString(R.string.delete_all)); unfollowConfirm.setTitle(getString(R.string.delete_all));
unfollowConfirm.setMessage(getString(R.string.remove_draft)); unfollowConfirm.setMessage(getString(R.string.remove_draft));
unfollowConfirm.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss()); unfollowConfirm.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss());

View file

@ -1,4 +1,4 @@
package app.fedilab.android.activities; package app.fedilab.android.mastodon.activities;
/* Copyright 2022 Thomas Schneider /* Copyright 2022 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -18,7 +18,6 @@ import static app.fedilab.android.BaseMainActivity.currentAccount;
import static app.fedilab.android.BaseMainActivity.instanceInfo; import static app.fedilab.android.BaseMainActivity.instanceInfo;
import android.content.Intent; import android.content.Intent;
import android.graphics.drawable.ColorDrawable;
import android.net.Uri; import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
@ -31,11 +30,11 @@ import android.view.View;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import androidx.localbroadcastmanager.content.LocalBroadcastManager; import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import com.bumptech.glide.Glide; import com.bumptech.glide.Glide;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.textfield.TextInputEditText; import com.google.android.material.textfield.TextInputEditText;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
@ -44,18 +43,17 @@ import java.util.Locale;
import app.fedilab.android.BaseMainActivity; import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.client.entities.api.Account;
import app.fedilab.android.client.entities.api.Field;
import app.fedilab.android.databinding.AccountFieldItemBinding; import app.fedilab.android.databinding.AccountFieldItemBinding;
import app.fedilab.android.databinding.ActivityEditProfileBinding; import app.fedilab.android.databinding.ActivityEditProfileBinding;
import app.fedilab.android.exception.DBException; import app.fedilab.android.mastodon.client.entities.api.Account;
import app.fedilab.android.helper.Helper; import app.fedilab.android.mastodon.client.entities.api.Field;
import app.fedilab.android.helper.MastodonHelper; import app.fedilab.android.mastodon.exception.DBException;
import app.fedilab.android.helper.ThemeHelper; import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.viewmodel.mastodon.AccountsVM; import app.fedilab.android.mastodon.helper.MastodonHelper;
import app.fedilab.android.mastodon.viewmodel.mastodon.AccountsVM;
import es.dmoral.toasty.Toasty; import es.dmoral.toasty.Toasty;
public class EditProfileActivity extends BaseActivity { public class EditProfileActivity extends BaseBarActivity {
public static final int PICK_MEDIA_AVATAR = 5705; public static final int PICK_MEDIA_AVATAR = 5705;
public static final int PICK_MEDIA_HEADER = 5706; public static final int PICK_MEDIA_HEADER = 5706;
@ -65,12 +63,11 @@ public class EditProfileActivity extends BaseActivity {
@Override @Override
protected void onCreate(@Nullable Bundle savedInstanceState) { protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
ThemeHelper.applyThemeBar(this);
binding = ActivityEditProfileBinding.inflate(getLayoutInflater()); binding = ActivityEditProfileBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); setContentView(binding.getRoot());
if (getSupportActionBar() != null) { if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
} }
new ViewModelProvider(EditProfileActivity.this).get(AccountsVM.class).getConnectedAccount(BaseMainActivity.currentInstance, BaseMainActivity.currentToken) new ViewModelProvider(EditProfileActivity.this).get(AccountsVM.class).getConnectedAccount(BaseMainActivity.currentInstance, BaseMainActivity.currentToken)
@ -88,7 +85,7 @@ public class EditProfileActivity extends BaseActivity {
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
private void initializeView() { private void initializeView() {
//Hydrate values //Hydrate values
MastodonHelper.loadProfileMediaMastodon(binding.bannerPp, currentAccount.mastodon_account, MastodonHelper.MediaAccountType.HEADER); MastodonHelper.loadProfileMediaMastodon(EditProfileActivity.this, binding.bannerPp, currentAccount.mastodon_account, MastodonHelper.MediaAccountType.HEADER);
MastodonHelper.loadPPMastodon(binding.accountPp, currentAccount.mastodon_account); MastodonHelper.loadPPMastodon(binding.accountPp, currentAccount.mastodon_account);
binding.displayName.setText(currentAccount.mastodon_account.display_name); binding.displayName.setText(currentAccount.mastodon_account.display_name);
binding.acct.setText(String.format(Locale.getDefault(), "%s@%s", currentAccount.mastodon_account.acct, BaseMainActivity.currentInstance)); binding.acct.setText(String.format(Locale.getDefault(), "%s@%s", currentAccount.mastodon_account.acct, BaseMainActivity.currentInstance));
@ -139,7 +136,7 @@ public class EditProfileActivity extends BaseActivity {
value = Html.fromHtml(field.value).toString(); value = Html.fromHtml(field.value).toString();
fieldItemBinding.value.setText(value); fieldItemBinding.value.setText(value);
fieldItemBinding.remove.setOnClickListener(v -> { fieldItemBinding.remove.setOnClickListener(v -> {
AlertDialog.Builder deleteConfirm = new AlertDialog.Builder(EditProfileActivity.this, Helper.dialogStyle()); AlertDialog.Builder deleteConfirm = new MaterialAlertDialogBuilder(EditProfileActivity.this);
deleteConfirm.setTitle(getString(R.string.delete_field)); deleteConfirm.setTitle(getString(R.string.delete_field));
deleteConfirm.setMessage(getString(R.string.delete_field_confirm)); deleteConfirm.setMessage(getString(R.string.delete_field_confirm));
deleteConfirm.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss()); deleteConfirm.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
@ -161,7 +158,7 @@ public class EditProfileActivity extends BaseActivity {
binding.addField.setOnClickListener(view -> { binding.addField.setOnClickListener(view -> {
AccountFieldItemBinding fieldItemBinding = AccountFieldItemBinding.inflate(getLayoutInflater()); AccountFieldItemBinding fieldItemBinding = AccountFieldItemBinding.inflate(getLayoutInflater());
fieldItemBinding.remove.setOnClickListener(v -> { fieldItemBinding.remove.setOnClickListener(v -> {
AlertDialog.Builder deleteConfirm = new AlertDialog.Builder(EditProfileActivity.this, Helper.dialogStyle()); AlertDialog.Builder deleteConfirm = new MaterialAlertDialogBuilder(EditProfileActivity.this);
deleteConfirm.setTitle(getString(R.string.delete_field)); deleteConfirm.setTitle(getString(R.string.delete_field));
deleteConfirm.setMessage(getString(R.string.delete_field_confirm)); deleteConfirm.setMessage(getString(R.string.delete_field_confirm));
deleteConfirm.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss()); deleteConfirm.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
@ -181,6 +178,8 @@ public class EditProfileActivity extends BaseActivity {
binding.addField.setVisibility(View.GONE); binding.addField.setVisibility(View.GONE);
} }
}); });
//Actions with the activity //Actions with the activity
accountsVM = new ViewModelProvider(EditProfileActivity.this).get(AccountsVM.class); accountsVM = new ViewModelProvider(EditProfileActivity.this).get(AccountsVM.class);
binding.headerSelect.setOnClickListener(view -> startActivityForResult(prepareIntent(), EditProfileActivity.PICK_MEDIA_HEADER)); binding.headerSelect.setOnClickListener(view -> startActivityForResult(prepareIntent(), EditProfileActivity.PICK_MEDIA_HEADER));
@ -209,7 +208,7 @@ public class EditProfileActivity extends BaseActivity {
Helper.recreateMainActivity(EditProfileActivity.this); Helper.recreateMainActivity(EditProfileActivity.this);
new Thread(() -> { new Thread(() -> {
try { try {
new app.fedilab.android.client.entities.app.Account(EditProfileActivity.this).insertOrUpdate(currentAccount); new app.fedilab.android.mastodon.client.entities.app.Account(EditProfileActivity.this).insertOrUpdate(currentAccount);
} catch (DBException e) { } catch (DBException e) {
e.printStackTrace(); e.printStackTrace();
} }
@ -236,7 +235,7 @@ public class EditProfileActivity extends BaseActivity {
currentAccount.mastodon_account = account; currentAccount.mastodon_account = account;
new Thread(() -> { new Thread(() -> {
try { try {
new app.fedilab.android.client.entities.app.Account(EditProfileActivity.this).insertOrUpdate(currentAccount); new app.fedilab.android.mastodon.client.entities.app.Account(EditProfileActivity.this).insertOrUpdate(currentAccount);
} catch (DBException e) { } catch (DBException e) {
e.printStackTrace(); e.printStackTrace();
} }
@ -330,7 +329,7 @@ public class EditProfileActivity extends BaseActivity {
currentAccount.mastodon_account = account; currentAccount.mastodon_account = account;
new Thread(() -> { new Thread(() -> {
try { try {
new app.fedilab.android.client.entities.app.Account(EditProfileActivity.this).insertOrUpdate(currentAccount); new app.fedilab.android.mastodon.client.entities.app.Account(EditProfileActivity.this).insertOrUpdate(currentAccount);
sendBroadCast(account); sendBroadCast(account);
} catch (DBException e) { } catch (DBException e) {
e.printStackTrace(); e.printStackTrace();

View file

@ -1,4 +1,4 @@
package app.fedilab.android.activities; package app.fedilab.android.mastodon.activities;
/* Copyright 2022 Thomas Schneider /* Copyright 2022 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -15,7 +15,6 @@ package app.fedilab.android.activities;
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import android.content.Context; import android.content.Context;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle; import android.os.Bundle;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.MenuItem; import android.view.MenuItem;
@ -28,27 +27,28 @@ import android.widget.Button;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import androidx.lifecycle.ViewModelStoreOwner; import androidx.lifecycle.ViewModelStoreOwner;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import app.fedilab.android.BaseMainActivity; import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.client.entities.api.Filter; import app.fedilab.android.activities.MainActivity;
import app.fedilab.android.databinding.ActivityFiltersBinding; import app.fedilab.android.databinding.ActivityFiltersBinding;
import app.fedilab.android.databinding.PopupAddFilterBinding; import app.fedilab.android.databinding.PopupAddFilterBinding;
import app.fedilab.android.helper.Helper; import app.fedilab.android.mastodon.client.entities.api.Filter;
import app.fedilab.android.helper.ThemeHelper; import app.fedilab.android.mastodon.ui.drawer.FilterAdapter;
import app.fedilab.android.ui.drawer.FilterAdapter; import app.fedilab.android.mastodon.ui.drawer.KeywordAdapter;
import app.fedilab.android.viewmodel.mastodon.AccountsVM; import app.fedilab.android.mastodon.viewmodel.mastodon.FiltersVM;
public class FilterActivity extends BaseActivity implements FilterAdapter.Delete { public class FilterActivity extends BaseBarActivity implements FilterAdapter.Delete {
private ActivityFiltersBinding binding; private ActivityFiltersBinding binding;
private List<Filter> filterList; private List<Filter> filterList;
@ -62,14 +62,15 @@ public class FilterActivity extends BaseActivity implements FilterAdapter.Delete
* @param listener - {@link FilterAdapter.FilterAction} * @param listener - {@link FilterAdapter.FilterAction}
*/ */
public static void addEditFilter(Context context, Filter filter, FilterAdapter.FilterAction listener) { public static void addEditFilter(Context context, Filter filter, FilterAdapter.FilterAction listener) {
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(context, Helper.dialogStyle()); AlertDialog.Builder dialogBuilder = new MaterialAlertDialogBuilder(context);
PopupAddFilterBinding popupAddFilterBinding = PopupAddFilterBinding.inflate(LayoutInflater.from(context)); PopupAddFilterBinding popupAddFilterBinding = PopupAddFilterBinding.inflate(LayoutInflater.from(context));
AccountsVM accountsVM = new ViewModelProvider((ViewModelStoreOwner) context).get(AccountsVM.class); FiltersVM filtersVM = new ViewModelProvider((ViewModelStoreOwner) context).get(FiltersVM.class);
dialogBuilder.setView(popupAddFilterBinding.getRoot()); dialogBuilder.setView(popupAddFilterBinding.getRoot());
ArrayAdapter<CharSequence> adapterResize = ArrayAdapter.createFromResource(Objects.requireNonNull(context), ArrayAdapter<CharSequence> adapterResize = ArrayAdapter.createFromResource(Objects.requireNonNull(context),
R.array.filter_expire, android.R.layout.simple_spinner_dropdown_item); R.array.filter_expire, android.R.layout.simple_spinner_dropdown_item);
popupAddFilterBinding.filterExpire.setAdapter(adapterResize); popupAddFilterBinding.filterExpire.setAdapter(adapterResize);
final int[] expire = {-1}; final int[] expire = {-1};
Filter.FilterParams filterParams = new Filter.FilterParams();
popupAddFilterBinding.filterExpire.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { popupAddFilterBinding.filterExpire.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override @Override
public void onItemSelected(AdapterView<?> parent1, View view, int position1, long id) { public void onItemSelected(AdapterView<?> parent1, View view, int position1, long id) {
@ -103,9 +104,28 @@ public class FilterActivity extends BaseActivity implements FilterAdapter.Delete
} }
}); });
if (filter != null) { if (filter != null) {
popupAddFilterBinding.addPhrase.setText(filter.phrase);
if (filter.context != null) filterParams.filter_action = filter.filter_action;
filterParams.title = filter.title;
// filterParams.expires_in = filter.expires_at;
filterParams.context = filter.context;
filterParams.id = filter.id;
if (filter.keywords != null && filter.keywords.size() > 0) {
filterParams.keywords = new ArrayList<>();
for (Filter.KeywordsAttributes keywordsAttributes : filter.keywords) {
Filter.KeywordsParams keywordsParams = new Filter.KeywordsParams();
keywordsParams._destroy = null;
keywordsParams.id = keywordsAttributes.id;
keywordsParams.keyword = keywordsAttributes.keyword;
keywordsParams.whole_word = keywordsAttributes.whole_word;
filterParams.keywords.add(keywordsParams);
}
}
popupAddFilterBinding.addTitle.setText(filter.title);
if (filter.context != null) {
for (String val : filter.context) { for (String val : filter.context) {
switch (val) { switch (val) {
case "home": case "home":
@ -120,48 +140,91 @@ public class FilterActivity extends BaseActivity implements FilterAdapter.Delete
case "thread": case "thread":
popupAddFilterBinding.contextConversation.setChecked(true); popupAddFilterBinding.contextConversation.setChecked(true);
break; break;
case "account":
popupAddFilterBinding.contextProfiles.setChecked(true);
break;
} }
} }
popupAddFilterBinding.contextWholeWord.setChecked(filter.whole_word); }
popupAddFilterBinding.contextDrop.setChecked(filter.irreversible); if (filter.filter_action.equalsIgnoreCase("warn")) {
popupAddFilterBinding.actionHide.setChecked(true);
popupAddFilterBinding.actionRemove.setChecked(false);
} else {
popupAddFilterBinding.actionHide.setChecked(false);
popupAddFilterBinding.actionRemove.setChecked(true);
}
}
if (filterParams.keywords == null) {
filterParams.keywords = new ArrayList<>();
} }
KeywordAdapter keywordAdapter = new KeywordAdapter(filterParams.keywords);
popupAddFilterBinding.lvKeywords.setAdapter(keywordAdapter);
popupAddFilterBinding.lvKeywords.setNestedScrollingEnabled(false);
popupAddFilterBinding.lvKeywords.setLayoutManager(new LinearLayoutManager(context));
popupAddFilterBinding.addKeyword.setOnClickListener(v -> {
Filter.KeywordsParams keywordsParams = new Filter.KeywordsParams();
keywordsParams.whole_word = true;
filterParams.keywords.add(keywordsParams);
keywordAdapter.notifyItemInserted(filterParams.keywords.size() - 1);
});
popupAddFilterBinding.actionRemove.setOnClickListener(v -> {
popupAddFilterBinding.actionHide.setChecked(false);
popupAddFilterBinding.actionRemove.setChecked(true);
});
popupAddFilterBinding.actionHide.setOnClickListener(v -> {
popupAddFilterBinding.actionRemove.setChecked(false);
popupAddFilterBinding.actionHide.setChecked(true);
});
AlertDialog alertDialog = dialogBuilder.setPositiveButton(R.string.validate, null) AlertDialog alertDialog = dialogBuilder.setPositiveButton(R.string.validate, null)
.setNegativeButton(R.string.cancel, null).create(); .setNegativeButton(R.string.cancel, null).create();
alertDialog.setOnShowListener(dialogInterface -> { alertDialog.setOnShowListener(dialogInterface -> {
Button button = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE); Button button = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
button.setOnClickListener(view -> { button.setOnClickListener(view -> {
if (popupAddFilterBinding.addPhrase.getText() == null || popupAddFilterBinding.addPhrase.getText().toString().trim().length() == 0) {
popupAddFilterBinding.addPhrase.setError(context.getString(R.string.cannot_be_empty)); boolean canBeSent = true;
return; for (int i = 0; i < filterParams.keywords.size(); i++) {
if (filterParams.keywords.get(i).keyword == null || (filterParams.keywords.get(i).keyword.trim().isEmpty() && filterParams.keywords.get(i)._destroy != null && !filterParams.keywords.get(i)._destroy)) {
canBeSent = false;
} }
if (!popupAddFilterBinding.contextConversation.isChecked() && !popupAddFilterBinding.contextHome.isChecked() && !popupAddFilterBinding.contextPublic.isChecked() && !popupAddFilterBinding.contextNotification.isChecked()) { }
if (popupAddFilterBinding.addTitle.getText().toString().trim().isEmpty()) {
popupAddFilterBinding.addTitle.setError(context.getString(R.string.cannot_be_empty));
canBeSent = false;
}
if (!popupAddFilterBinding.contextConversation.isChecked() && !popupAddFilterBinding.contextHome.isChecked() && !popupAddFilterBinding.contextPublic.isChecked() && !popupAddFilterBinding.contextNotification.isChecked() && !popupAddFilterBinding.contextProfiles.isChecked()) {
popupAddFilterBinding.contextDescription.setError(context.getString(R.string.cannot_be_empty)); popupAddFilterBinding.contextDescription.setError(context.getString(R.string.cannot_be_empty));
return; canBeSent = false;
} }
if (popupAddFilterBinding.addPhrase.getText() != null && popupAddFilterBinding.addPhrase.getText().toString().trim().length() > 0) { if (canBeSent) {
Filter filterSent = new Filter(); filterParams.context = new ArrayList<>();
ArrayList<String> contextFilter = new ArrayList<>();
if (popupAddFilterBinding.contextHome.isChecked()) if (popupAddFilterBinding.contextHome.isChecked())
contextFilter.add("home"); filterParams.context.add("home");
if (popupAddFilterBinding.contextPublic.isChecked()) if (popupAddFilterBinding.contextPublic.isChecked())
contextFilter.add("public"); filterParams.context.add("public");
if (popupAddFilterBinding.contextNotification.isChecked()) if (popupAddFilterBinding.contextNotification.isChecked())
contextFilter.add("notifications"); filterParams.context.add("notifications");
if (popupAddFilterBinding.contextConversation.isChecked()) if (popupAddFilterBinding.contextConversation.isChecked())
contextFilter.add("thread"); filterParams.context.add("thread");
filterSent.context = contextFilter; if (popupAddFilterBinding.contextProfiles.isChecked())
filterSent.expires_at_sent = expire[0]; filterParams.context.add("account");
filterSent.phrase = popupAddFilterBinding.addPhrase.getText().toString(); if (expire[0] != -1) {
filterSent.whole_word = popupAddFilterBinding.contextWholeWord.isChecked(); filterParams.expires_in = (long) expire[0];
filterSent.irreversible = popupAddFilterBinding.contextDrop.isChecked(); } else {
if (filter != null) { filterParams.expires_in = null;
accountsVM.editFilter(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, filter.id, filterSent.phrase, filterSent.context, filterSent.irreversible, filterSent.whole_word, filterSent.expires_at_sent) }
filterParams.title = popupAddFilterBinding.addTitle.getText().toString().trim();
filterParams.filter_action = popupAddFilterBinding.actionRemove.isChecked() ? "hide" : "warn";
if (filterParams.id != null) {
filtersVM.editFilter(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, filterParams)
.observe((LifecycleOwner) context, listener::callback); .observe((LifecycleOwner) context, listener::callback);
} else { } else {
accountsVM.addFilter(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, filterSent.phrase, filterSent.context, filterSent.irreversible, filterSent.whole_word, filterSent.expires_at_sent) filtersVM.addFilter(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, filterParams)
.observe((LifecycleOwner) context, listener::callback); .observe((LifecycleOwner) context, listener::callback);
} }
alertDialog.dismiss(); alertDialog.dismiss();
@ -173,8 +236,8 @@ public class FilterActivity extends BaseActivity implements FilterAdapter.Delete
alertDialog.setTitle(context.getString(R.string.action_update_filter)); alertDialog.setTitle(context.getString(R.string.action_update_filter));
alertDialog.setOnDismissListener(dialogInterface -> { alertDialog.setOnDismissListener(dialogInterface -> {
//Hide keyboard //Hide keyboard
InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); InputMethodManager imm = (InputMethodManager) context.getSystemService(INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(popupAddFilterBinding.addPhrase.getWindowToken(), 0); imm.hideSoftInputFromWindow(popupAddFilterBinding.addTitle.getWindowToken(), 0);
}); });
if (alertDialog.getWindow() != null) { if (alertDialog.getWindow() != null) {
alertDialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE); alertDialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
@ -186,17 +249,16 @@ public class FilterActivity extends BaseActivity implements FilterAdapter.Delete
@Override @Override
protected void onCreate(@Nullable Bundle savedInstanceState) { protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
ThemeHelper.applyThemeBar(this);
binding = ActivityFiltersBinding.inflate(getLayoutInflater()); binding = ActivityFiltersBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); setContentView(binding.getRoot());
filterList = new ArrayList<>(); filterList = new ArrayList<>();
if (getSupportActionBar() != null) { if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
} }
AccountsVM accountsVM = new ViewModelProvider(FilterActivity.this).get(AccountsVM.class); FiltersVM filtersVM = new ViewModelProvider(FilterActivity.this).get(FiltersVM.class);
accountsVM.getFilters(BaseMainActivity.currentInstance, BaseMainActivity.currentToken) filtersVM.getFilters(BaseMainActivity.currentInstance, BaseMainActivity.currentToken)
.observe(FilterActivity.this, filters -> { .observe(FilterActivity.this, filters -> {
BaseMainActivity.mainFilters = filters; BaseMainActivity.mainFilters = filters;
if (filters != null && filters.size() > 0) { if (filters != null && filters.size() > 0) {
@ -213,7 +275,11 @@ public class FilterActivity extends BaseActivity implements FilterAdapter.Delete
binding.addFilter.setOnClickListener(v -> addEditFilter(FilterActivity.this, null, filter -> { binding.addFilter.setOnClickListener(v -> addEditFilter(FilterActivity.this, null, filter -> {
if (filter != null) { if (filter != null) {
if (MainActivity.mainFilters == null) {
MainActivity.mainFilters = new ArrayList<>();
}
filterList.add(0, filter); filterList.add(0, filter);
MainActivity.mainFilters.add(filter);
if (filterAdapter != null) { if (filterAdapter != null) {
filterAdapter.notifyItemInserted(0); filterAdapter.notifyItemInserted(0);
} else { } else {

View file

@ -1,4 +1,4 @@
package app.fedilab.android.activities; package app.fedilab.android.mastodon.activities;
/* Copyright 2022 Thomas Schneider /* Copyright 2022 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -15,13 +15,11 @@ package app.fedilab.android.activities;
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle; import android.os.Bundle;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
@ -31,13 +29,12 @@ import java.util.List;
import app.fedilab.android.BaseMainActivity; import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.client.entities.api.Account;
import app.fedilab.android.client.entities.api.Accounts;
import app.fedilab.android.databinding.ActivityStatusInfoBinding; import app.fedilab.android.databinding.ActivityStatusInfoBinding;
import app.fedilab.android.helper.MastodonHelper; import app.fedilab.android.mastodon.client.entities.api.Account;
import app.fedilab.android.helper.ThemeHelper; import app.fedilab.android.mastodon.client.entities.api.Accounts;
import app.fedilab.android.ui.drawer.AccountFollowRequestAdapter; import app.fedilab.android.mastodon.helper.MastodonHelper;
import app.fedilab.android.viewmodel.mastodon.AccountsVM; import app.fedilab.android.mastodon.ui.drawer.AccountFollowRequestAdapter;
import app.fedilab.android.mastodon.viewmodel.mastodon.AccountsVM;
public class FollowRequestActivity extends BaseActivity { public class FollowRequestActivity extends BaseActivity {
@ -51,14 +48,13 @@ public class FollowRequestActivity extends BaseActivity {
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
ThemeHelper.applyTheme(this);
binding = ActivityStatusInfoBinding.inflate(getLayoutInflater()); binding = ActivityStatusInfoBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); setContentView(binding.getRoot());
setSupportActionBar(binding.toolbar); setSupportActionBar(binding.toolbar);
if (getSupportActionBar() != null) { if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true); getSupportActionBar().setDisplayShowHomeEnabled(true);
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
} }
accountList = new ArrayList<>(); accountList = new ArrayList<>();
flagLoading = false; flagLoading = false;

View file

@ -0,0 +1,208 @@
package app.fedilab.android.mastodon.activities;
/* Copyright 2022 Thomas Schneider
*
* This file is a part of Fedilab
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */
import android.os.Bundle;
import android.text.InputFilter;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import java.util.ArrayList;
import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R;
import app.fedilab.android.databinding.ActivityFollowedTagsBinding;
import app.fedilab.android.databinding.PopupAddFollowedTagtBinding;
import app.fedilab.android.mastodon.client.entities.api.Tag;
import app.fedilab.android.mastodon.client.entities.app.Timeline;
import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.mastodon.helper.ThemeHelper;
import app.fedilab.android.mastodon.ui.drawer.FollowedTagAdapter;
import app.fedilab.android.mastodon.ui.fragment.timeline.FragmentMastodonTimeline;
import app.fedilab.android.mastodon.viewmodel.mastodon.TagVM;
import es.dmoral.toasty.Toasty;
public class FollowedTagActivity extends BaseBarActivity implements FollowedTagAdapter.ActionOnTag {
private ActivityFollowedTagsBinding binding;
private boolean canGoBack;
private TagVM tagVM;
private Tag tag;
private ArrayList<Tag> tagList;
private FollowedTagAdapter followedTagAdapter;
private FragmentMastodonTimeline fragmentMastodonTimeline;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityFollowedTagsBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
canGoBack = false;
if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true);
}
tagVM = new ViewModelProvider(FollowedTagActivity.this).get(TagVM.class);
tagVM.followedTags(BaseMainActivity.currentInstance, BaseMainActivity.currentToken)
.observe(FollowedTagActivity.this, tags -> {
if (tags != null && tags.tags != null && tags.tags.size() > 0) {
tagList = new ArrayList<>(tags.tags);
followedTagAdapter = new FollowedTagAdapter(tagList);
followedTagAdapter.actionOnTag = this;
binding.notContent.setVisibility(View.GONE);
binding.recyclerView.setAdapter(followedTagAdapter);
binding.recyclerView.setLayoutManager(new LinearLayoutManager(FollowedTagActivity.this));
} else {
binding.notContent.setVisibility(View.VISIBLE);
}
});
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
onBackPressed();
return true;
} else if (item.getItemId() == R.id.action_unfollow && tag != null) {
AlertDialog.Builder alt_bld = new MaterialAlertDialogBuilder(FollowedTagActivity.this);
alt_bld.setTitle(R.string.action_unfollow_tag);
alt_bld.setMessage(R.string.action_unfollow_tag_confirm);
alt_bld.setPositiveButton(R.string.unfollow, (dialog, id) -> {
tagVM.unfollow(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, tag.name);
int position = 0;
for (Tag tagTmp : tagList) {
if (tagTmp.name.equalsIgnoreCase(tag.name)) {
break;
}
position++;
}
tagList.remove(position);
followedTagAdapter.notifyItemRemoved(position);
ThemeHelper.slideViewsToRight(binding.fragmentContainer, binding.recyclerView, () -> {
canGoBack = false;
if (fragmentMastodonTimeline != null) {
fragmentMastodonTimeline.onDestroyView();
}
invalidateOptionsMenu();
setTitle(R.string.followed_tags);
});
if (tagList.size() == 0) {
binding.notContent.setVisibility(View.VISIBLE);
} else {
binding.notContent.setVisibility(View.GONE);
}
});
alt_bld.setNegativeButton(R.string.cancel, (dialog, id) -> dialog.dismiss());
AlertDialog alert = alt_bld.create();
alert.show();
} else if (item.getItemId() == R.id.action_follow_tag) {
AlertDialog.Builder dialogBuilder = new MaterialAlertDialogBuilder(FollowedTagActivity.this);
PopupAddFollowedTagtBinding popupAddFollowedTagtBinding = PopupAddFollowedTagtBinding.inflate(getLayoutInflater());
dialogBuilder.setView(popupAddFollowedTagtBinding.getRoot());
popupAddFollowedTagtBinding.addTag.setFilters(new InputFilter[]{new InputFilter.LengthFilter(255)});
dialogBuilder.setPositiveButton(R.string.validate, (dialog, id) -> {
if (popupAddFollowedTagtBinding.addTag.getText() != null && popupAddFollowedTagtBinding.addTag.getText().toString().trim().length() > 0) {
tagVM.follow(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, popupAddFollowedTagtBinding.addTag.getText().toString().trim())
.observe(FollowedTagActivity.this, newTag -> {
if (tagList == null) {
tagList = new ArrayList<>();
}
if (followedTagAdapter == null) {
followedTagAdapter = new FollowedTagAdapter(tagList);
followedTagAdapter.actionOnTag = this;
binding.notContent.setVisibility(View.GONE);
binding.recyclerView.setAdapter(followedTagAdapter);
binding.recyclerView.setLayoutManager(new LinearLayoutManager(FollowedTagActivity.this));
}
if (newTag != null) {
tagList.add(0, newTag);
followedTagAdapter.notifyItemInserted(0);
} else {
Toasty.error(FollowedTagActivity.this, getString(R.string.toast_feature_not_supported), Toasty.LENGTH_LONG).show();
}
});
dialog.dismiss();
} else {
popupAddFollowedTagtBinding.addTag.setError(getString(R.string.not_valid_tag_name));
}
});
dialogBuilder.setNegativeButton(R.string.cancel, (dialog, id) -> dialog.dismiss());
dialogBuilder.create().show();
}
return super.onOptionsItemSelected(item);
}
@Override
public boolean onCreateOptionsMenu(@NonNull Menu menu) {
if (!canGoBack) {
getMenuInflater().inflate(R.menu.menu_main_followed_tag, menu);
} else {
getMenuInflater().inflate(R.menu.menu_followed_tag, menu);
}
return true;
}
@Override
public void onBackPressed() {
if (canGoBack) {
canGoBack = false;
ThemeHelper.slideViewsToRight(binding.fragmentContainer, binding.recyclerView, () -> {
if (fragmentMastodonTimeline != null) {
fragmentMastodonTimeline.onDestroyView();
}
});
setTitle(R.string.followed_tags);
invalidateOptionsMenu();
} else {
super.onBackPressed();
}
}
@Override
public void click(Tag tag) {
this.tag = tag;
canGoBack = true;
ThemeHelper.slideViewsToLeft(binding.recyclerView, binding.fragmentContainer, () -> {
fragmentMastodonTimeline = new FragmentMastodonTimeline();
Bundle bundle = new Bundle();
bundle.putSerializable(Helper.ARG_SEARCH_KEYWORD, tag.name);
bundle.putSerializable(Helper.ARG_TIMELINE_TYPE, Timeline.TimeLineEnum.TAG);
setTitle(tag.name);
fragmentMastodonTimeline.setArguments(bundle);
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction =
fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.fragment_container, fragmentMastodonTimeline);
fragmentTransaction.commit();
invalidateOptionsMenu();
});
}
}

View file

@ -0,0 +1,391 @@
package app.fedilab.android.mastodon.activities;
/* Copyright 2022 Thomas Schneider
*
* This file is a part of Fedilab
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */
import static app.fedilab.android.BaseMainActivity.currentAccount;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.Menu;
import android.view.MenuItem;
import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog;
import androidx.lifecycle.ViewModelProvider;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import java.util.ArrayList;
import java.util.List;
import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R;
import app.fedilab.android.activities.MainActivity;
import app.fedilab.android.databinding.ActivityHashtagBinding;
import app.fedilab.android.mastodon.client.entities.api.Filter;
import app.fedilab.android.mastodon.client.entities.api.Status;
import app.fedilab.android.mastodon.client.entities.app.Pinned;
import app.fedilab.android.mastodon.client.entities.app.PinnedTimeline;
import app.fedilab.android.mastodon.client.entities.app.StatusDraft;
import app.fedilab.android.mastodon.client.entities.app.TagTimeline;
import app.fedilab.android.mastodon.client.entities.app.Timeline;
import app.fedilab.android.mastodon.exception.DBException;
import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.mastodon.ui.fragment.timeline.FragmentMastodonTimeline;
import app.fedilab.android.mastodon.viewmodel.mastodon.FiltersVM;
import app.fedilab.android.mastodon.viewmodel.mastodon.ReorderVM;
import app.fedilab.android.mastodon.viewmodel.mastodon.TagVM;
import es.dmoral.toasty.Toasty;
public class HashTagActivity extends BaseActivity {
public static int position;
private String tag;
private String stripTag;
private Boolean pinnedTag;
private Boolean followedTag;
private Boolean mutedTag;
private TagVM tagVM;
private Filter fedilabFilter;
private Filter.KeywordsAttributes keyword;
private PinnedTimeline pinnedTimeline;
private Pinned pinned;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityHashtagBinding binding = ActivityHashtagBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
Bundle b = getIntent().getExtras();
if (b != null) {
tag = b.getString(Helper.ARG_SEARCH_KEYWORD, null);
}
if (tag == null)
finish();
pinnedTag = null;
followedTag = null;
mutedTag = null;
stripTag = tag.replaceAll("#", "");
setSupportActionBar(binding.toolbar);
ActionBar actionBar = getSupportActionBar();
//Remove title
if (actionBar != null) {
actionBar.setDisplayShowTitleEnabled(false);
}
binding.title.setText(tag);
if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true);
}
tagVM = new ViewModelProvider(HashTagActivity.this).get(TagVM.class);
tagVM.getTag(MainActivity.currentInstance, MainActivity.currentToken, stripTag).observe(this, returnedTag -> {
if (returnedTag != null) {
followedTag = returnedTag.following;
invalidateOptionsMenu();
}
});
ReorderVM reorderVM = new ViewModelProvider(HashTagActivity.this).get(ReorderVM.class);
reorderVM.getAllPinned().observe(HashTagActivity.this, pinned -> {
if (pinned == null) {
pinned = new Pinned();
pinned.pinnedTimelines = new ArrayList<>();
}
this.pinned = pinned;
pinnedTag = false;
if (pinned.pinnedTimelines != null) {
for (PinnedTimeline pinnedTimeline : pinned.pinnedTimelines) {
if (pinnedTimeline.tagTimeline != null) {
if (pinnedTimeline.tagTimeline.name.equalsIgnoreCase(stripTag)) {
this.pinnedTimeline = pinnedTimeline;
pinnedTag = true;
break;
}
}
}
invalidateOptionsMenu();
}
});
if (MainActivity.filterFetched && MainActivity.mainFilters != null) {
mutedTag = false;
for (Filter filter : MainActivity.mainFilters) {
if (filter.title.equalsIgnoreCase(Helper.FEDILAB_MUTED_HASHTAGS)) {
fedilabFilter = filter;
String fetch = tag.startsWith("#") ? tag : "#" + tag;
for (Filter.KeywordsAttributes keywordsAttributes : filter.keywords) {
if (fetch.equalsIgnoreCase(keywordsAttributes.keyword)) {
mutedTag = true;
keyword = keywordsAttributes;
invalidateOptionsMenu();
break;
}
}
}
}
invalidateOptionsMenu();
}
Bundle bundle = new Bundle();
bundle.putSerializable(Helper.ARG_TIMELINE_TYPE, Timeline.TimeLineEnum.TAG);
bundle.putString(Helper.ARG_SEARCH_KEYWORD, tag);
Helper.addFragment(getSupportFragmentManager(), R.id.nav_host_fragment_tags, new FragmentMastodonTimeline(), bundle, null, null);
binding.compose.setOnClickListener(v -> {
Intent intentToot = new Intent(HashTagActivity.this, ComposeActivity.class);
StatusDraft statusDraft = new StatusDraft();
Status status = new Status();
status.text = "#" + stripTag;
List<Status> statuses = new ArrayList<>();
statuses.add(status);
statusDraft.statusDraftList = statuses;
Bundle _b = new Bundle();
_b.putSerializable(Helper.ARG_STATUS_DRAFT, statusDraft);
intentToot.putExtras(_b);
startActivity(intentToot);
});
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
finish();
return true;
} else if (item.getItemId() == R.id.action_add_timeline) {
if (pinnedTag) {
AlertDialog.Builder unpinConfirm = new MaterialAlertDialogBuilder(HashTagActivity.this);
unpinConfirm.setMessage(getString(R.string.unpin_timeline_description));
unpinConfirm.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
unpinConfirm.setPositiveButton(R.string.yes, (dialog, which) -> {
if (pinned == null || pinned.pinnedTimelines == null) {
return;
}
pinned.pinnedTimelines.remove(pinnedTimeline);
try {
new Pinned(HashTagActivity.this).updatePinned(pinned);
} catch (DBException e) {
e.printStackTrace();
}
pinnedTag = false;
invalidateOptionsMenu();
Bundle b = new Bundle();
b.putBoolean(Helper.RECEIVE_REDRAW_TOPBAR, true);
Intent intentBD = new Intent(Helper.BROADCAST_DATA);
intentBD.putExtras(b);
LocalBroadcastManager.getInstance(HashTagActivity.this).sendBroadcast(intentBD);
dialog.dismiss();
});
unpinConfirm.show();
} else {
new Thread(() -> {
try {
Pinned pinned = new Pinned(HashTagActivity.this).getPinned(currentAccount);
boolean canBeAdded = true;
boolean update = true;
if (pinned == null) {
pinned = new Pinned();
pinned.pinnedTimelines = new ArrayList<>();
update = false;
} else {
for (PinnedTimeline pinnedTimeline : pinned.pinnedTimelines) {
if (pinnedTimeline.type == Timeline.TimeLineEnum.TAG) {
if (pinnedTimeline.tagTimeline.name.compareTo(stripTag.trim()) == 0) {
canBeAdded = false;
}
}
}
}
if (!canBeAdded) {
Handler mainHandler = new Handler(Looper.getMainLooper());
Runnable myRunnable = () -> Toasty.warning(HashTagActivity.this, getString(R.string.tags_already_stored), Toasty.LENGTH_SHORT).show();
mainHandler.post(myRunnable);
return;
}
pinnedTimeline = new PinnedTimeline();
pinnedTimeline.type = Timeline.TimeLineEnum.TAG;
pinnedTimeline.position = pinned.pinnedTimelines.size();
pinnedTimeline.displayed = true;
TagTimeline tagTimeline = new TagTimeline();
tagTimeline.name = stripTag.trim();
tagTimeline.isNSFW = false;
tagTimeline.isART = false;
tagTimeline.any = new ArrayList<>();
tagTimeline.any.add(stripTag.trim());
pinnedTimeline.tagTimeline = tagTimeline;
pinned.pinnedTimelines.add(pinnedTimeline);
if (pinned.instance == null || pinned.user_id == null) {
pinned.instance = MainActivity.currentInstance;
pinned.user_id = MainActivity.currentUserID;
}
if (update) {
new Pinned(HashTagActivity.this).updatePinned(pinned);
} else {
new Pinned(HashTagActivity.this).insertPinned(pinned);
}
Bundle b = new Bundle();
b.putBoolean(Helper.RECEIVE_REDRAW_TOPBAR, true);
Intent intentBD = new Intent(Helper.BROADCAST_DATA);
intentBD.putExtras(b);
LocalBroadcastManager.getInstance(HashTagActivity.this).sendBroadcast(intentBD);
pinnedTag = true;
invalidateOptionsMenu();
} catch (DBException e) {
e.printStackTrace();
}
}).start();
}
} else if (item.getItemId() == R.id.action_follow_tag) {
if (!followedTag) {
tagVM.follow(MainActivity.currentInstance, MainActivity.currentToken, stripTag).observe(this, returnedTag -> {
if (returnedTag != null) {
followedTag = returnedTag.following;
invalidateOptionsMenu();
}
});
} else {
tagVM.unfollow(MainActivity.currentInstance, MainActivity.currentToken, stripTag).observe(this, returnedTag -> {
if (returnedTag != null) {
followedTag = returnedTag.following;
invalidateOptionsMenu();
}
});
}
} else if (item.getItemId() == R.id.action_mute) {
if (!mutedTag) {
if (MainActivity.mainFilters == null || fedilabFilter == null) {
MainActivity.mainFilters = new ArrayList<>();
Filter.FilterParams filterParams = new Filter.FilterParams();
filterParams.title = Helper.FEDILAB_MUTED_HASHTAGS;
filterParams.filter_action = "hide";
filterParams.context = new ArrayList<>();
filterParams.context.add("home");
filterParams.context.add("public");
filterParams.context.add("thread");
filterParams.context.add("account");
FiltersVM filtersVM = new ViewModelProvider(HashTagActivity.this).get(FiltersVM.class);
filtersVM.addFilter(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, filterParams)
.observe(HashTagActivity.this, filter -> {
if (filter != null) {
MainActivity.mainFilters.add(filter);
mutedTag = false;
fedilabFilter = filter;
muteTags();
invalidateOptionsMenu();
}
});
} else {
muteTags();
}
} else {
unmuteTags();
}
}
return super.onOptionsItemSelected(item);
}
private void unmuteTags() {
String search = tag.startsWith("#") ? tag : "#" + tag;
for (Filter.KeywordsAttributes keywordsAttributes : fedilabFilter.keywords) {
if (search.equalsIgnoreCase(keywordsAttributes.keyword)) {
keyword = keywordsAttributes;
break;
}
}
if (keyword != null && keyword.id != null) {
FiltersVM filtersVM = new ViewModelProvider(HashTagActivity.this).get(FiltersVM.class);
filtersVM.removeKeyword(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, keyword.id);
fedilabFilter.keywords.remove(keyword);
mutedTag = false;
invalidateOptionsMenu();
}
}
private void muteTags() {
Filter.FilterParams filterParams = new Filter.FilterParams();
filterParams.id = fedilabFilter.id;
filterParams.keywords = new ArrayList<>();
Filter.KeywordsParams keywordsParams = new Filter.KeywordsParams();
keywordsParams.whole_word = true;
keywordsParams.keyword = tag.startsWith("#") ? tag : "#" + tag;
filterParams.keywords.add(keywordsParams);
filterParams.context = fedilabFilter.context;
FiltersVM filtersVM = new ViewModelProvider(HashTagActivity.this).get(FiltersVM.class);
filtersVM.editFilter(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, filterParams)
.observe(HashTagActivity.this, filter -> {
fedilabFilter = filter;
mutedTag = true;
invalidateOptionsMenu();
});
}
@Override
public boolean onCreateOptionsMenu(@NonNull Menu menu) {
getMenuInflater().inflate(R.menu.menu_hashtag, menu);
MenuItem pin = menu.findItem(R.id.action_add_timeline);
MenuItem follow = menu.findItem(R.id.action_follow_tag);
MenuItem mute = menu.findItem(R.id.action_mute);
if (pinnedTag != null) {
pin.setVisible(true);
if (pinnedTag) {
pin.setIcon(R.drawable.tag_pin_off);
pin.setTitle(getString(R.string.unpin_tag));
} else {
pin.setTitle(getString(R.string.unpin_tag));
pin.setIcon(R.drawable.tag_pin);
}
} else {
pin.setVisible(false);
}
if (followedTag != null) {
follow.setVisible(true);
if (followedTag) {
follow.setTitle(getString(R.string.unfollow_tag));
follow.setIcon(R.drawable.tag_unfollow);
} else {
follow.setTitle(getString(R.string.follow_tag));
follow.setIcon(R.drawable.tag_follow);
}
} else {
follow.setVisible(false);
}
if (mutedTag != null) {
mute.setVisible(true);
if (mutedTag) {
mute.setTitle(getString(R.string.unmute_tag_action));
mute.setIcon(R.drawable.tag_unmuted);
} else {
mute.setTitle(getString(R.string.mute_tag_action));
mute.setIcon(R.drawable.tag_muted);
}
} else {
mute.setVisible(false);
}
return super.onCreateOptionsMenu(menu);
}
}

View file

@ -0,0 +1,158 @@
package app.fedilab.android.mastodon.activities;
/* Copyright 2022 Thomas Schneider
*
* This file is a part of Fedilab
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */
import android.app.Dialog;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.text.Html;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.style.ForegroundColorSpan;
import android.text.style.UnderlineSpan;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.preference.PreferenceManager;
import com.bumptech.glide.Glide;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R;
import app.fedilab.android.activities.MainActivity;
import app.fedilab.android.databinding.ActivityInstanceBinding;
import app.fedilab.android.mastodon.client.entities.api.Instance;
import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.mastodon.helper.ThemeHelper;
import app.fedilab.android.mastodon.viewmodel.mastodon.InstancesVM;
public class InstanceActivity extends DialogFragment {
ActivityInstanceBinding binding;
private boolean applyMaxChar = false;
@NonNull
@Override
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
binding = ActivityInstanceBinding.inflate(getLayoutInflater());
MaterialAlertDialogBuilder materialAlertDialogBuilder = new MaterialAlertDialogBuilder(requireContext());
materialAlertDialogBuilder.setView(binding.getRoot());
Dialog dialog = materialAlertDialogBuilder.create();
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(requireActivity());
final SpannableString contentAbout = new SpannableString(getString(R.string.action_about_instance));
contentAbout.setSpan(new UnderlineSpan(), 0, contentAbout.length(), 0);
contentAbout.setSpan(new ForegroundColorSpan(ThemeHelper.getAttColor(requireContext(), R.attr.colorPrimary)), 0, contentAbout.length(),
Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
binding.about.setText(contentAbout);
final SpannableString contentPrivacy = new SpannableString(getString(R.string.action_privacy_policy));
contentPrivacy.setSpan(new UnderlineSpan(), 0, contentPrivacy.length(), 0);
contentPrivacy.setSpan(new ForegroundColorSpan(ThemeHelper.getAttColor(requireContext(), R.attr.colorPrimary)), 0, contentPrivacy.length(),
Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
binding.privacy.setText(contentPrivacy);
binding.about.setOnClickListener(v -> Helper.openBrowser(requireActivity(), "https://" + MainActivity.currentInstance + "/about"));
binding.privacy.setOnClickListener(v -> Helper.openBrowser(requireActivity(), "https://" + MainActivity.currentInstance + "/privacy-policy"));
binding.close.setOnClickListener(view -> {
if (applyMaxChar) {
String max_char = binding.maxChar.getText().toString();
SharedPreferences.Editor editor = sharedpreferences.edit();
if (!max_char.isEmpty()) {
try {
editor.putInt(getString(R.string.SET_MAX_INSTANCE_CHAR) + MainActivity.currentInstance, Integer.parseInt(max_char));
editor.apply();
} catch (Exception ignored) {
}
}
}
requireDialog().dismiss();
}
);
InstancesVM instancesVM = new ViewModelProvider(InstanceActivity.this).get(InstancesVM.class);
instancesVM.getInstance(BaseMainActivity.currentInstance).observe(InstanceActivity.this, instanceInfo -> {
binding.loader.setVisibility(View.GONE);
if (instanceInfo == null || instanceInfo.info == null || instanceInfo.info.description == null) {
binding.instanceData.setVisibility(View.GONE);
binding.contact.setVisibility(View.GONE);
int val = sharedpreferences.getInt(getString(R.string.SET_MAX_INSTANCE_CHAR) + MainActivity.currentInstance, -1);
if (val != -1) {
binding.maxChar.setText(String.valueOf(val));
}
applyMaxChar = true;
} else {
Instance instance = instanceInfo.info;
Glide.with(InstanceActivity.this)
.asDrawable()
.placeholder(R.drawable.default_banner)
.load(instance.thumbnail)
.into(binding.backgroundImage);
binding.name.setText(instance.title);
if (instance.description == null || instance.description.trim().length() == 0)
binding.description.setText(getString(R.string.instance_no_description));
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
binding.description.setText(Html.fromHtml(instance.description, Html.FROM_HTML_MODE_LEGACY));
else
binding.description.setText(Html.fromHtml(instance.description));
binding.version.setText(instance.version);
binding.uri.setText(instance.uri);
if (instance.email == null) {
binding.contact.setVisibility(View.GONE);
} else {
binding.contact.setVisibility(View.VISIBLE);
}
binding.contact.setOnClickListener(v -> {
Intent emailIntent = new Intent(Intent.ACTION_SENDTO, Uri.fromParts("mailto", instance.email, null));
emailIntent.putExtra(Intent.EXTRA_SUBJECT, "[Mastodon] - " + instance.uri);
startActivity(Intent.createChooser(emailIntent, getString(R.string.send_email)));
});
binding.instanceData.setVisibility(View.VISIBLE);
}
});
return dialog;
}
@Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
}

View file

@ -0,0 +1,126 @@
package app.fedilab.android.mastodon.activities
import android.app.Dialog
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.text.SpannableString
import android.text.style.UnderlineSpan
import android.view.Window
import androidx.core.view.isVisible
import androidx.fragment.app.DialogFragment
import androidx.lifecycle.ViewModelProvider
import app.fedilab.android.BaseMainActivity
import app.fedilab.android.R
import app.fedilab.android.databinding.ActivityInstanceSocialBinding
import app.fedilab.android.mastodon.client.entities.app.InstanceSocial
import app.fedilab.android.mastodon.helper.Helper
import app.fedilab.android.mastodon.helper.ThemeHelper
import app.fedilab.android.mastodon.viewmodel.mastodon.InstanceSocialVM
import com.bumptech.glide.Glide
import com.google.android.material.dialog.MaterialAlertDialogBuilder
/* Copyright 2022 Thomas Schneider
*
* This file is a part of Fedilab
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */
class InstanceHealthActivity : DialogFragment() {
private var _binding: ActivityInstanceSocialBinding? = null
private val binding: ActivityInstanceSocialBinding get() = _binding!!
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
_binding = ActivityInstanceSocialBinding.inflate(layoutInflater)
binding.close.setOnClickListener { dialog?.dismiss() }
val content = SpannableString(binding.refInstance.text.toString())
content.setSpan(UnderlineSpan(), 0, content.length, 0)
binding.refInstance.text = content
binding.refInstance.setOnClickListener {
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("https://instances.social")))
}
val materialAlertDialogBuilder = MaterialAlertDialogBuilder(requireContext())
materialAlertDialogBuilder.setView(binding.root)
val dialog = materialAlertDialogBuilder.create()
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE)
dialog.setOnShowListener { checkInstance() }
return dialog
}
private fun checkInstance() {
val instanceSocialVM =
ViewModelProvider(this@InstanceHealthActivity)[InstanceSocialVM::class.java]
instanceSocialVM.getInstances(BaseMainActivity.currentInstance.trim { it <= ' ' })
.observe(this@InstanceHealthActivity) { instanceSocialList: InstanceSocial? ->
val instance = instanceSocialList?.instances?.firstOrNull { instance ->
instance.name.equals(
BaseMainActivity.currentInstance.trim { it <= ' ' },
ignoreCase = true
)
}
if (instance != null) {
instance.thumbnail?.takeIf { it != "null" }?.let { thumbnail ->
Glide.with(this@InstanceHealthActivity)
.asBitmap()
.placeholder(R.drawable.default_banner)
.load(thumbnail)
.into(binding.backgroundImage)
}
binding.name.text = instance.name
if (instance.up) {
binding.up.setText(app.fedilab.android.R.string.is_up)
binding.up.setTextColor(
ThemeHelper.getAttColor(
requireContext(),
app.fedilab.android.R.attr.colorPrimary
)
)
} else {
binding.up.setText(app.fedilab.android.R.string.is_down)
binding.up.setTextColor(
ThemeHelper.getAttColor(
requireContext(),
app.fedilab.android.R.attr.colorError
)
)
}
binding.uptime.text = getString(
app.fedilab.android.R.string.instance_health_uptime,
instance.uptime * 100
)
if (instance.checked_at != null)
binding.checkedAt.text =
getString(
app.fedilab.android.R.string.instance_health_checkedat,
Helper.dateToString(instance.checked_at)
)
binding.values.text = getString(
app.fedilab.android.R.string.instance_health_indication,
instance.version,
Helper.withSuffix(instance.active_users.toLong()),
Helper.withSuffix(instance.statuses.toLong())
)
} else {
binding.instanceData.isVisible = false
binding.noInstance.isVisible = true
}
binding.loader.isVisible = false
}
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}

View file

@ -0,0 +1,78 @@
package app.fedilab.android.mastodon.activities;
/* Copyright 2022 Thomas Schneider
*
* This file is a part of Fedilab
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */
import android.app.Dialog;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment;
import androidx.lifecycle.ViewModelProvider;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import app.fedilab.android.R;
import app.fedilab.android.databinding.ActivityInstanceProfileBinding;
import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.mastodon.viewmodel.mastodon.NodeInfoVM;
import es.dmoral.toasty.Toasty;
public class InstanceProfileActivity extends DialogFragment {
private String instance;
private ActivityInstanceProfileBinding binding;
@NonNull
@Override
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
binding = ActivityInstanceProfileBinding.inflate(getLayoutInflater());
MaterialAlertDialogBuilder materialAlertDialogBuilder = new MaterialAlertDialogBuilder(requireContext());
materialAlertDialogBuilder.setView(binding.getRoot());
Dialog dialog = materialAlertDialogBuilder.create();
Bundle b = getArguments();
if (b != null)
instance = b.getString(Helper.ARG_INSTANCE, null);
if (instance == null) {
requireDialog().dismiss();
}
binding.close.setOnClickListener(v -> requireDialog().dismiss());
NodeInfoVM nodeInfoVM = new ViewModelProvider(InstanceProfileActivity.this).get(NodeInfoVM.class);
nodeInfoVM.getNodeInfo(instance).observe(InstanceProfileActivity.this, nodeInfo -> {
if (nodeInfo == null) {
Toasty.error(requireContext(), getString(R.string.toast_error), Toast.LENGTH_LONG).show();
requireDialog().dismiss();
return;
}
binding.name.setText(instance);
binding.userCount.setText(Helper.withSuffix((nodeInfo.usage.users.total)));
binding.statusCount.setText(Helper.withSuffix(((nodeInfo.usage.localPosts))));
String softwareStr = nodeInfo.software.name + " - ";
binding.software.setText(softwareStr);
binding.version.setText(nodeInfo.software.version);
binding.loader.setVisibility(View.GONE);
});
return dialog;
}
}

View file

@ -1,4 +1,4 @@
package app.fedilab.android.activities; package app.fedilab.android.mastodon.activities;
/* Copyright 2022 Thomas Schneider /* Copyright 2022 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -15,10 +15,7 @@ package app.fedilab.android.activities;
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import static app.fedilab.android.helper.PinnedTimelineHelper.sortListPositionAsc;
import android.content.Intent; import android.content.Intent;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle; import android.os.Bundle;
import android.text.Editable; import android.text.Editable;
import android.text.InputFilter; import android.text.InputFilter;
@ -30,7 +27,6 @@ import android.view.View;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction; import androidx.fragment.app.FragmentTransaction;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
@ -38,30 +34,36 @@ import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import app.fedilab.android.BaseMainActivity; import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.client.entities.api.Account; import app.fedilab.android.activities.MainActivity;
import app.fedilab.android.client.entities.api.MastodonList;
import app.fedilab.android.client.entities.app.PinnedTimeline;
import app.fedilab.android.client.entities.app.Timeline;
import app.fedilab.android.databinding.ActivityListBinding; import app.fedilab.android.databinding.ActivityListBinding;
import app.fedilab.android.databinding.PopupAddListBinding; import app.fedilab.android.databinding.PopupAddListBinding;
import app.fedilab.android.databinding.PopupManageAccountsListBinding; import app.fedilab.android.databinding.PopupManageAccountsListBinding;
import app.fedilab.android.helper.Helper; import app.fedilab.android.mastodon.client.entities.api.Account;
import app.fedilab.android.helper.ThemeHelper; import app.fedilab.android.mastodon.client.entities.api.MastodonList;
import app.fedilab.android.ui.drawer.AccountListAdapter; import app.fedilab.android.mastodon.client.entities.app.Pinned;
import app.fedilab.android.ui.drawer.MastodonListAdapter; import app.fedilab.android.mastodon.client.entities.app.PinnedTimeline;
import app.fedilab.android.ui.fragment.timeline.FragmentMastodonTimeline; import app.fedilab.android.mastodon.client.entities.app.Timeline;
import app.fedilab.android.viewmodel.mastodon.AccountsVM; import app.fedilab.android.mastodon.exception.DBException;
import app.fedilab.android.viewmodel.mastodon.ReorderVM; import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.viewmodel.mastodon.TimelinesVM; import app.fedilab.android.mastodon.helper.ThemeHelper;
import app.fedilab.android.mastodon.ui.drawer.AccountListAdapter;
import app.fedilab.android.mastodon.ui.drawer.MastodonListAdapter;
import app.fedilab.android.mastodon.ui.fragment.timeline.FragmentMastodonTimeline;
import app.fedilab.android.mastodon.viewmodel.mastodon.AccountsVM;
import app.fedilab.android.mastodon.viewmodel.mastodon.ReorderVM;
import app.fedilab.android.mastodon.viewmodel.mastodon.TimelinesVM;
import es.dmoral.toasty.Toasty; import es.dmoral.toasty.Toasty;
public class MastodonListActivity extends BaseActivity implements MastodonListAdapter.ActionOnList { public class MastodonListActivity extends BaseBarActivity implements MastodonListAdapter.ActionOnList {
AccountListAdapter accountsInListAdapter; AccountListAdapter accountsInListAdapter;
@ -76,20 +78,21 @@ public class MastodonListActivity extends BaseActivity implements MastodonListAd
private boolean flagLoading; private boolean flagLoading;
private String max_id; private String max_id;
private FragmentMastodonTimeline fragmentMastodonTimeline; private FragmentMastodonTimeline fragmentMastodonTimeline;
private boolean orderASC;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
ThemeHelper.applyThemeBar(this);
binding = ActivityListBinding.inflate(getLayoutInflater()); binding = ActivityListBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); setContentView(binding.getRoot());
canGoBack = false; canGoBack = false;
if (getSupportActionBar() != null) { if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true); getSupportActionBar().setDisplayShowHomeEnabled(true);
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
} }
flagLoading = false; flagLoading = false;
orderASC = true;
max_id = null; max_id = null;
accountsVM = new ViewModelProvider(MastodonListActivity.this).get(AccountsVM.class); accountsVM = new ViewModelProvider(MastodonListActivity.this).get(AccountsVM.class);
timelinesVM = new ViewModelProvider(MastodonListActivity.this).get(TimelinesVM.class); timelinesVM = new ViewModelProvider(MastodonListActivity.this).get(TimelinesVM.class);
@ -112,7 +115,7 @@ public class MastodonListActivity extends BaseActivity implements MastodonListAd
} }
} }
} }
sortListPositionAsc(mastodonListList); sortAsc(mastodonListList);
mastodonListAdapter = new MastodonListAdapter(mastodonListList); mastodonListAdapter = new MastodonListAdapter(mastodonListList);
mastodonListAdapter.actionOnList = this; mastodonListAdapter.actionOnList = this;
binding.notContent.setVisibility(View.GONE); binding.notContent.setVisibility(View.GONE);
@ -125,13 +128,40 @@ public class MastodonListActivity extends BaseActivity implements MastodonListAd
}); });
} }
private void sortAsc(List<MastodonList> mastodonLists) {
Collections.sort(mastodonLists, (obj1, obj2) -> obj1.title.compareToIgnoreCase(obj2.title));
orderASC = true;
invalidateOptionsMenu();
}
private void sortDesc(List<MastodonList> mastodonLists) {
Collections.sort(mastodonLists, (obj1, obj2) -> obj2.title.compareToIgnoreCase(obj1.title));
orderASC = false;
invalidateOptionsMenu();
}
@Override @Override
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) { if (item.getItemId() == android.R.id.home) {
onBackPressed(); onBackPressed();
return true; return true;
} else if (item.getItemId() == R.id.action_user_mute_home) {
AlertDialog.Builder dialogBuilder = new MaterialAlertDialogBuilder(MastodonListActivity.this);
dialogBuilder.setTitle(R.string.put_all_accounts_in_home_muted);
dialogBuilder.setPositiveButton(R.string.mute_them_all, (dialog, id) -> {
timelinesVM.getAccountsInList(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, mastodonList.id, null, null, 0)
.observe(MastodonListActivity.this, accounts -> {
if (accounts != null && accounts.size() > 0) {
accountsVM.muteAccountsHome(MainActivity.currentAccount, accounts);
}
});
dialog.dismiss();
});
dialogBuilder.setNegativeButton(R.string.cancel, (dialog, id) -> dialog.dismiss());
dialogBuilder.show();
} else if (item.getItemId() == R.id.action_manage_users) { } else if (item.getItemId() == R.id.action_manage_users) {
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(MastodonListActivity.this, Helper.dialogStyle()); AlertDialog.Builder dialogBuilder = new MaterialAlertDialogBuilder(MastodonListActivity.this);
PopupManageAccountsListBinding popupManageAccountsListBinding = PopupManageAccountsListBinding.inflate(getLayoutInflater()); PopupManageAccountsListBinding popupManageAccountsListBinding = PopupManageAccountsListBinding.inflate(getLayoutInflater());
dialogBuilder.setView(popupManageAccountsListBinding.getRoot()); dialogBuilder.setView(popupManageAccountsListBinding.getRoot());
popupManageAccountsListBinding.loader.setVisibility(View.VISIBLE); popupManageAccountsListBinding.loader.setVisibility(View.VISIBLE);
@ -228,7 +258,7 @@ public class MastodonListActivity extends BaseActivity implements MastodonListAd
dialogBuilder.setPositiveButton(R.string.close, (dialog, id) -> dialog.dismiss()); dialogBuilder.setPositiveButton(R.string.close, (dialog, id) -> dialog.dismiss());
dialogBuilder.create().show(); dialogBuilder.create().show();
} else if (item.getItemId() == R.id.action_delete && mastodonList != null) { } else if (item.getItemId() == R.id.action_delete && mastodonList != null) {
AlertDialog.Builder alt_bld = new AlertDialog.Builder(MastodonListActivity.this, Helper.dialogStyle()); AlertDialog.Builder alt_bld = new MaterialAlertDialogBuilder(MastodonListActivity.this);
alt_bld.setTitle(R.string.action_lists_delete); alt_bld.setTitle(R.string.action_lists_delete);
alt_bld.setMessage(R.string.action_lists_confirm_delete); alt_bld.setMessage(R.string.action_lists_confirm_delete);
alt_bld.setPositiveButton(R.string.delete, (dialog, id) -> { alt_bld.setPositiveButton(R.string.delete, (dialog, id) -> {
@ -266,7 +296,7 @@ public class MastodonListActivity extends BaseActivity implements MastodonListAd
AlertDialog alert = alt_bld.create(); AlertDialog alert = alt_bld.create();
alert.show(); alert.show();
} else if (item.getItemId() == R.id.action_add_list) { } else if (item.getItemId() == R.id.action_add_list) {
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(MastodonListActivity.this, Helper.dialogStyle()); AlertDialog.Builder dialogBuilder = new MaterialAlertDialogBuilder(MastodonListActivity.this);
PopupAddListBinding popupAddListBinding = PopupAddListBinding.inflate(getLayoutInflater()); PopupAddListBinding popupAddListBinding = PopupAddListBinding.inflate(getLayoutInflater());
dialogBuilder.setView(popupAddListBinding.getRoot()); dialogBuilder.setView(popupAddListBinding.getRoot());
popupAddListBinding.addList.setFilters(new InputFilter[]{new InputFilter.LengthFilter(255)}); popupAddListBinding.addList.setFilters(new InputFilter[]{new InputFilter.LengthFilter(255)});
@ -277,8 +307,15 @@ public class MastodonListActivity extends BaseActivity implements MastodonListAd
if (mastodonListList == null) { if (mastodonListList == null) {
mastodonListList = new ArrayList<>(); mastodonListList = new ArrayList<>();
} }
if (newMastodonList != null && mastodonListAdapter != null) { if (newMastodonList != null) {
mastodonListList.add(0, newMastodonList); mastodonListList.add(0, newMastodonList);
if (mastodonListAdapter == null) {
mastodonListAdapter = new MastodonListAdapter(mastodonListList);
mastodonListAdapter.actionOnList = MastodonListActivity.this;
binding.notContent.setVisibility(View.GONE);
binding.recyclerView.setAdapter(mastodonListAdapter);
binding.recyclerView.setLayoutManager(new LinearLayoutManager(MastodonListActivity.this));
}
mastodonListAdapter.notifyItemInserted(0); mastodonListAdapter.notifyItemInserted(0);
} else { } else {
Toasty.error(MastodonListActivity.this, getString(R.string.toast_error), Toasty.LENGTH_LONG).show(); Toasty.error(MastodonListActivity.this, getString(R.string.toast_error), Toasty.LENGTH_LONG).show();
@ -298,6 +335,83 @@ public class MastodonListActivity extends BaseActivity implements MastodonListAd
}); });
dialogBuilder.setNegativeButton(R.string.cancel, (dialog, id) -> dialog.dismiss()); dialogBuilder.setNegativeButton(R.string.cancel, (dialog, id) -> dialog.dismiss());
dialogBuilder.create().show(); dialogBuilder.create().show();
} else if (item.getItemId() == R.id.action_edit) {
AlertDialog.Builder dialogBuilder = new MaterialAlertDialogBuilder(MastodonListActivity.this);
PopupAddListBinding popupAddListBinding = PopupAddListBinding.inflate(getLayoutInflater());
dialogBuilder.setView(popupAddListBinding.getRoot());
popupAddListBinding.addList.setFilters(new InputFilter[]{new InputFilter.LengthFilter(255)});
popupAddListBinding.addList.setText(mastodonList.title);
popupAddListBinding.addList.setSelection(popupAddListBinding.addList.getText().length());
dialogBuilder.setPositiveButton(R.string.validate, (dialog, id) -> {
if (popupAddListBinding.addList.getText() != null && popupAddListBinding.addList.getText().toString().trim().length() > 0) {
timelinesVM.updateList(
BaseMainActivity.currentInstance, BaseMainActivity.currentToken, mastodonList.id,
popupAddListBinding.addList.getText().toString().trim(), null)
.observe(MastodonListActivity.this, newMastodonList -> {
if (mastodonListList != null && newMastodonList != null) {
int position = 0;
for (MastodonList mastodonList : mastodonListList) {
if (newMastodonList.id.equalsIgnoreCase(mastodonList.id)) {
ReorderVM reorderVM = new ViewModelProvider(MastodonListActivity.this).get(ReorderVM.class);
int finalPosition = position;
reorderVM.getAllPinned().observe(MastodonListActivity.this, pinned -> {
if (pinned != null) {
if (pinned.pinnedTimelines != null) {
for (PinnedTimeline pinnedTimeline : pinned.pinnedTimelines) {
if (pinnedTimeline.mastodonList != null) {
if (pinnedTimeline.mastodonList.id.equalsIgnoreCase(newMastodonList.id)) {
if (!newMastodonList.title.equalsIgnoreCase(pinnedTimeline.mastodonList.title)) {
pinnedTimeline.mastodonList.title = newMastodonList.title;
setTitle(newMastodonList.title);
mastodonListList.get(finalPosition).title = newMastodonList.title;
mastodonListAdapter.notifyItemChanged(finalPosition);
new Thread(() -> {
try {
new Pinned(MastodonListActivity.this).updatePinned(pinned);
Bundle b = new Bundle();
b.putBoolean(Helper.RECEIVE_REDRAW_TOPBAR, true);
Intent intentBD = new Intent(Helper.BROADCAST_DATA);
intentBD.putExtras(b);
LocalBroadcastManager.getInstance(MastodonListActivity.this).sendBroadcast(intentBD);
} catch (DBException e) {
e.printStackTrace();
}
}).start();
}
break;
}
}
}
}
}
});
}
position++;
}
} else {
Toasty.error(MastodonListActivity.this, getString(R.string.toast_error), Toasty.LENGTH_LONG).show();
}
});
dialog.dismiss();
} else {
popupAddListBinding.addList.setError(getString(R.string.not_valid_list_name));
}
});
dialogBuilder.setNegativeButton(R.string.cancel, (dialog, id) -> dialog.dismiss());
dialogBuilder.create().show();
} else if (item.getItemId() == R.id.action_order) {
if (mastodonListList != null && mastodonListList.size() > 0 && mastodonListAdapter != null) {
if (orderASC) {
sortDesc(mastodonListList);
} else {
sortAsc(mastodonListList);
}
invalidateOptionsMenu();
mastodonListAdapter.notifyItemRangeChanged(0, mastodonListList.size());
}
} }
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }
@ -327,6 +441,14 @@ public class MastodonListActivity extends BaseActivity implements MastodonListAd
public boolean onCreateOptionsMenu(@NonNull Menu menu) { public boolean onCreateOptionsMenu(@NonNull Menu menu) {
if (!canGoBack) { if (!canGoBack) {
getMenuInflater().inflate(R.menu.menu_main_list, menu); getMenuInflater().inflate(R.menu.menu_main_list, menu);
MenuItem order = menu.findItem(R.id.action_order);
if (order != null) {
if (orderASC) {
order.setIcon(R.drawable.ic_baseline_filter_asc_24);
} else {
order.setIcon(R.drawable.ic_baseline_filter_desc_24);
}
}
} else { } else {
getMenuInflater().inflate(R.menu.menu_list, menu); getMenuInflater().inflate(R.menu.menu_list, menu);
} }

View file

@ -1,4 +1,4 @@
package app.fedilab.android.activities; package app.fedilab.android.mastodon.activities;
/* Copyright 2022 Thomas Schneider /* Copyright 2022 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -21,16 +21,11 @@ import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.graphics.Point;
import android.graphics.drawable.ColorDrawable;
import android.net.Uri; import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.text.Html;
import android.view.Display;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.MotionEvent; import android.view.MotionEvent;
@ -45,32 +40,26 @@ import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentStatePagerAdapter; import androidx.fragment.app.FragmentStatePagerAdapter;
import androidx.preference.PreferenceManager;
import androidx.viewpager.widget.ViewPager; import androidx.viewpager.widget.ViewPager;
import com.github.stom79.mytransl.MyTransL;
import com.github.stom79.mytransl.client.HttpsConnectionException;
import com.github.stom79.mytransl.client.Results;
import com.github.stom79.mytransl.translate.Params;
import com.github.stom79.mytransl.translate.Translate;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.ArrayList; import java.util.ArrayList;
import app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.client.entities.api.Attachment;
import app.fedilab.android.client.entities.api.Status;
import app.fedilab.android.databinding.ActivityMediaPagerBinding; import app.fedilab.android.databinding.ActivityMediaPagerBinding;
import app.fedilab.android.helper.Helper; import app.fedilab.android.mastodon.client.entities.api.Attachment;
import app.fedilab.android.helper.MediaHelper; import app.fedilab.android.mastodon.client.entities.api.Status;
import app.fedilab.android.helper.ThemeHelper; import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.interfaces.OnDownloadInterface; import app.fedilab.android.mastodon.helper.MediaHelper;
import app.fedilab.android.ui.fragment.media.FragmentMedia; import app.fedilab.android.mastodon.helper.TranslateHelper;
import app.fedilab.android.mastodon.interfaces.OnDownloadInterface;
import app.fedilab.android.mastodon.ui.fragment.media.FragmentMedia;
import app.fedilab.android.mastodon.ui.fragment.media.FragmentMediaProfile;
import es.dmoral.toasty.Toasty; import es.dmoral.toasty.Toasty;
public class MediaActivity extends BaseActivity implements OnDownloadInterface { public class MediaActivity extends BaseTransparentActivity implements OnDownloadInterface {
int flags; int flags;
private ArrayList<Attachment> attachments; private ArrayList<Attachment> attachments;
@ -82,7 +71,7 @@ public class MediaActivity extends BaseActivity implements OnDownloadInterface {
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
long id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1); long id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
if (downloadID == id) { if (downloadID == id) {
DownloadManager manager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE); DownloadManager manager = (DownloadManager) context.getSystemService(DOWNLOAD_SERVICE);
assert manager != null; assert manager != null;
Uri uri = manager.getUriForDownloadedFile(downloadID); Uri uri = manager.getUriForDownloadedFile(downloadID);
Intent shareIntent = new Intent(Intent.ACTION_SEND); Intent shareIntent = new Intent(Intent.ACTION_SEND);
@ -104,17 +93,15 @@ public class MediaActivity extends BaseActivity implements OnDownloadInterface {
}; };
private boolean fullscreen; private boolean fullscreen;
private Handler handler; private Handler handler;
private int minTouch, maxTouch;
private float startX;
private float startY;
private ActivityMediaPagerBinding binding; private ActivityMediaPagerBinding binding;
private FragmentMedia mCurrentFragment; private FragmentMedia mCurrentFragment;
private Status status; private Status status;
private boolean mediaFromProfile;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
getWindow().requestFeature(Window.FEATURE_ACTION_BAR_OVERLAY); getWindow().requestFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
ThemeHelper.applyThemeBar(this);
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
ActivityCompat.postponeEnterTransition(MediaActivity.this); ActivityCompat.postponeEnterTransition(MediaActivity.this);
binding = ActivityMediaPagerBinding.inflate(getLayoutInflater()); binding = ActivityMediaPagerBinding.inflate(getLayoutInflater());
@ -127,12 +114,16 @@ public class MediaActivity extends BaseActivity implements OnDownloadInterface {
if (b != null) { if (b != null) {
mediaPosition = b.getInt(Helper.ARG_MEDIA_POSITION, 1); mediaPosition = b.getInt(Helper.ARG_MEDIA_POSITION, 1);
attachments = (ArrayList<Attachment>) b.getSerializable(Helper.ARG_MEDIA_ARRAY); attachments = (ArrayList<Attachment>) b.getSerializable(Helper.ARG_MEDIA_ARRAY);
mediaFromProfile = b.getBoolean(Helper.ARG_MEDIA_ARRAY_PROFILE, false);
status = (Status) b.getSerializable(Helper.ARG_STATUS); status = (Status) b.getSerializable(Helper.ARG_STATUS);
} }
if (mediaFromProfile && FragmentMediaProfile.mediaAttachmentProfile != null) {
attachments = new ArrayList<>();
attachments.addAll(FragmentMediaProfile.mediaAttachmentProfile);
}
if (getSupportActionBar() != null) { if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true); getSupportActionBar().setDisplayShowHomeEnabled(true);
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
} }
if (attachments == null || attachments.size() == 0) if (attachments == null || attachments.size() == 0)
@ -144,14 +135,13 @@ public class MediaActivity extends BaseActivity implements OnDownloadInterface {
binding.mediaViewpager.setAdapter(mPagerAdapter); binding.mediaViewpager.setAdapter(mPagerAdapter);
binding.mediaViewpager.setSaveEnabled(false); binding.mediaViewpager.setSaveEnabled(false);
binding.mediaViewpager.setCurrentItem(mediaPosition - 1); binding.mediaViewpager.setCurrentItem(mediaPosition - 1);
binding.haulerView.setOnDragDismissedListener(dragDirection -> ActivityCompat.finishAfterTransition(MediaActivity.this));
registerReceiver(onDownloadComplete, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)); registerReceiver(onDownloadComplete, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
String description = attachments.get(mediaPosition - 1).description; String description = attachments.get(mediaPosition - 1).description;
handler = new Handler(); handler = new Handler();
if (status != null) { if (attachments.get(mediaPosition - 1).status != null) {
binding.originalMessage.setOnClickListener(v -> { binding.originalMessage.setOnClickListener(v -> {
Intent intentContext = new Intent(MediaActivity.this, ContextActivity.class); Intent intentContext = new Intent(MediaActivity.this, ContextActivity.class);
intentContext.putExtra(Helper.ARG_STATUS, status); intentContext.putExtra(Helper.ARG_STATUS, attachments.get(mediaPosition - 1).status);
intentContext.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intentContext.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intentContext); startActivity(intentContext);
}); });
@ -160,41 +150,15 @@ public class MediaActivity extends BaseActivity implements OnDownloadInterface {
binding.mediaDescription.setText(description); binding.mediaDescription.setText(description);
binding.translate.setOnClickListener(v -> { binding.translate.setOnClickListener(v -> {
String descriptionToTranslate = attachments.get(mediaPosition - 1).description; String descriptionToTranslate = attachments.get(mediaPosition - 1).description;
MyTransL.translatorEngine et = MyTransL.translatorEngine.LIBRETRANSLATE; TranslateHelper.translate(MediaActivity.this, descriptionToTranslate, translated -> {
final MyTransL myTransL = MyTransL.getInstance(et); if (translated != null) {
myTransL.setObfuscation(true); attachments.get(mediaPosition - 1).translation = translated;
Params params = new Params(); binding.mediaDescriptionTranslated.setText(translated);
params.setSplit_sentences(false);
params.setFormat(Params.fType.TEXT);
params.setSource_lang("auto");
myTransL.setLibretranslateDomain("translate.fedilab.app");
String statusToTranslate;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
statusToTranslate = Html.fromHtml(descriptionToTranslate, Html.FROM_HTML_MODE_LEGACY).toString();
else
statusToTranslate = Html.fromHtml(descriptionToTranslate).toString();
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(MediaActivity.this);
String translate = sharedpreferences.getString(getString(R.string.SET_LIVE_TRANSLATE), MyTransL.getLocale());
if (translate != null && translate.equalsIgnoreCase("default")) {
translate = MyTransL.getLocale();
}
myTransL.translate(statusToTranslate, translate, params, new Results() {
@Override
public void onSuccess(Translate translate) {
if (translate.getTranslatedContent() != null) {
attachments.get(mediaPosition - 1).translation = translate.getTranslatedContent();
binding.mediaDescriptionTranslated.setText(translate.getTranslatedContent());
binding.mediaDescriptionTranslated.setVisibility(View.VISIBLE); binding.mediaDescriptionTranslated.setVisibility(View.VISIBLE);
binding.mediaDescription.setVisibility(View.GONE); binding.mediaDescription.setVisibility(View.GONE);
} else { } else {
Toasty.error(MediaActivity.this, getString(R.string.toast_error_translate), Toast.LENGTH_LONG).show(); Toasty.error(MediaActivity.this, getString(R.string.toast_error_translate), Toast.LENGTH_LONG).show();
} }
}
@Override
public void onFail(HttpsConnectionException httpsConnectionException) {
}
}); });
}); });
if (attachments.get(mediaPosition - 1).translation != null) { if (attachments.get(mediaPosition - 1).translation != null) {
@ -215,6 +179,7 @@ public class MediaActivity extends BaseActivity implements OnDownloadInterface {
} }
public void onPageSelected(int position) { public void onPageSelected(int position) {
mediaPosition = position;
String description = attachments.get(position).description; String description = attachments.get(position).description;
if (handler != null) { if (handler != null) {
handler.removeCallbacksAndMessages(null); handler.removeCallbacksAndMessages(null);
@ -225,41 +190,15 @@ public class MediaActivity extends BaseActivity implements OnDownloadInterface {
} }
binding.translate.setOnClickListener(v -> { binding.translate.setOnClickListener(v -> {
String descriptionToTranslate = attachments.get(position).description; String descriptionToTranslate = attachments.get(position).description;
MyTransL.translatorEngine et = MyTransL.translatorEngine.LIBRETRANSLATE; TranslateHelper.translate(MediaActivity.this, descriptionToTranslate, translated -> {
final MyTransL myTransL = MyTransL.getInstance(et); if (translated != null) {
myTransL.setObfuscation(true); attachments.get(position).translation = translated;
Params params = new Params(); binding.mediaDescriptionTranslated.setText(translated);
params.setSplit_sentences(false);
params.setFormat(Params.fType.TEXT);
params.setSource_lang("auto");
myTransL.setLibretranslateDomain("translate.fedilab.app");
String statusToTranslate;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
statusToTranslate = Html.fromHtml(descriptionToTranslate, Html.FROM_HTML_MODE_LEGACY).toString();
else
statusToTranslate = Html.fromHtml(descriptionToTranslate).toString();
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(MediaActivity.this);
String translate = sharedpreferences.getString(getString(R.string.SET_LIVE_TRANSLATE), MyTransL.getLocale());
if (translate != null && translate.equalsIgnoreCase("default")) {
translate = MyTransL.getLocale();
}
myTransL.translate(statusToTranslate, translate, params, new Results() {
@Override
public void onSuccess(Translate translate) {
if (translate.getTranslatedContent() != null) {
attachments.get(position).translation = translate.getTranslatedContent();
binding.mediaDescriptionTranslated.setText(translate.getTranslatedContent());
binding.mediaDescriptionTranslated.setVisibility(View.VISIBLE); binding.mediaDescriptionTranslated.setVisibility(View.VISIBLE);
binding.mediaDescription.setVisibility(View.GONE); binding.mediaDescription.setVisibility(View.GONE);
} else { } else {
Toasty.error(MediaActivity.this, getString(R.string.toast_error_translate), Toast.LENGTH_LONG).show(); Toasty.error(MediaActivity.this, getString(R.string.toast_error_translate), Toast.LENGTH_LONG).show();
} }
}
@Override
public void onFail(HttpsConnectionException httpsConnectionException) {
}
}); });
}); });
if (!fullscreen) { if (!fullscreen) {
@ -277,18 +216,23 @@ public class MediaActivity extends BaseActivity implements OnDownloadInterface {
} }
} }
}); });
setFullscreen(true); setFullscreen(true);
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
int screenHeight = size.y;
minTouch = (int) (screenHeight * 0.1);
maxTouch = (int) (screenHeight * 0.9);
} }
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
try {
return super.dispatchTouchEvent(event);
} catch (IllegalArgumentException ex) {
ex.printStackTrace();
}
return false;
}
public void toogleFullScreen() {
fullscreen = !fullscreen;
setFullscreen(fullscreen);
}
@Override @Override
public boolean onCreateOptionsMenu(@NonNull Menu menu) { public boolean onCreateOptionsMenu(@NonNull Menu menu) {
@ -306,6 +250,7 @@ public class MediaActivity extends BaseActivity implements OnDownloadInterface {
int position = binding.mediaViewpager.getCurrentItem(); int position = binding.mediaViewpager.getCurrentItem();
Attachment attachment = attachments.get(position); Attachment attachment = attachments.get(position);
if (Build.VERSION.SDK_INT >= 23) { if (Build.VERSION.SDK_INT >= 23) {
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) {
if (ContextCompat.checkSelfPermission(MediaActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(MediaActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { if (ContextCompat.checkSelfPermission(MediaActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(MediaActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MediaActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, Helper.EXTERNAL_STORAGE_REQUEST_CODE_MEDIA_SAVE); ActivityCompat.requestPermissions(MediaActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, Helper.EXTERNAL_STORAGE_REQUEST_CODE_MEDIA_SAVE);
} else { } else {
@ -316,6 +261,14 @@ public class MediaActivity extends BaseActivity implements OnDownloadInterface {
downloadID = -1; downloadID = -1;
} }
} }
} else {
if (attachment.type.compareTo("image") == 0) {
MediaHelper.manageMove(MediaActivity.this, attachment.url, false);
} else {
MediaHelper.manageDownloadsNoPopup(MediaActivity.this, attachment.url);
downloadID = -1;
}
}
} else { } else {
if (attachment.type.compareToIgnoreCase("image") == 0) { if (attachment.type.compareToIgnoreCase("image") == 0) {
MediaHelper.manageMove(MediaActivity.this, attachment.url, false); MediaHelper.manageMove(MediaActivity.this, attachment.url, false);
@ -369,25 +322,9 @@ public class MediaActivity extends BaseActivity implements OnDownloadInterface {
} }
} }
@Override private void toggleScreenContain(boolean fullscreen) {
public boolean dispatchTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
startX = event.getX();
startY = event.getY();
break;
case MotionEvent.ACTION_UP:
float endX = event.getX();
float endY = event.getY();
if (endY > minTouch && endY < maxTouch && isAClick(startX, endX, startY, endY)) {
setFullscreen(!fullscreen);
if (!fullscreen) { if (!fullscreen) {
String description = attachments.get(binding.mediaViewpager.getCurrentItem()).description; String description = attachments.get(binding.mediaViewpager.getCurrentItem()).description;
if (handler != null) {
handler.removeCallbacksAndMessages(null);
}
handler = new Handler();
if (description != null && description.trim().length() > 0 && description.trim().compareTo("null") != 0) { if (description != null && description.trim().length() > 0 && description.trim().compareTo("null") != 0) {
binding.mediaDescription.setText(description); binding.mediaDescription.setText(description);
if (attachments.get(binding.mediaViewpager.getCurrentItem()).translation != null) { if (attachments.get(binding.mediaViewpager.getCurrentItem()).translation != null) {
@ -400,7 +337,11 @@ public class MediaActivity extends BaseActivity implements OnDownloadInterface {
} }
} else { } else {
binding.translate.setVisibility(View.GONE); binding.translate.setVisibility(View.GONE);
if (status != null) {
binding.originalMessage.setVisibility(View.VISIBLE);
} else {
binding.originalMessage.setVisibility(View.INVISIBLE); binding.originalMessage.setVisibility(View.INVISIBLE);
}
binding.mediaDescriptionTranslated.setVisibility(View.GONE); binding.mediaDescriptionTranslated.setVisibility(View.GONE);
binding.mediaDescription.setVisibility(View.GONE); binding.mediaDescription.setVisibility(View.GONE);
} }
@ -411,24 +352,7 @@ public class MediaActivity extends BaseActivity implements OnDownloadInterface {
binding.mediaDescription.setVisibility(View.GONE); binding.mediaDescription.setVisibility(View.GONE);
} }
} }
break;
}
try {
return super.dispatchTouchEvent(event);
} catch (IllegalArgumentException ex) {
ex.printStackTrace();
}
return false;
}
private boolean isAClick(float startX, float endX, float startY, float endY) {
float differenceX = Math.abs(startX - endX);
float differenceY = Math.abs(startY - endY);
int CLICK_ACTION_THRESHOLD = 200;
return !(differenceX > CLICK_ACTION_THRESHOLD/* =5 */ || differenceY > CLICK_ACTION_THRESHOLD);
}
@Override @Override
public void onDestroy() { public void onDestroy() {
@ -451,6 +375,15 @@ public class MediaActivity extends BaseActivity implements OnDownloadInterface {
super.onPostResume(); super.onPostResume();
} }
// Shows the system bars by removing all the flags
// except for the ones that make the content appear under the system bars.
private void showSystemUI() {
View decorView = getWindow().getDecorView();
decorView.setSystemUiVisibility(flags |
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
}
public boolean getFullScreen() { public boolean getFullScreen() {
return this.fullscreen; return this.fullscreen;
@ -462,15 +395,16 @@ public class MediaActivity extends BaseActivity implements OnDownloadInterface {
showSystemUI(); showSystemUI();
binding.mediaDescription.setVisibility(View.VISIBLE); binding.mediaDescription.setVisibility(View.VISIBLE);
binding.translate.setVisibility(View.VISIBLE); binding.translate.setVisibility(View.VISIBLE);
if (status != null) { if (mediaFromProfile) {
binding.originalMessage.setVisibility(View.VISIBLE); binding.originalMessage.setVisibility(View.VISIBLE);
} }
} else { } else {
hideSystemUI();
binding.mediaDescription.setVisibility(View.GONE); binding.mediaDescription.setVisibility(View.GONE);
binding.translate.setVisibility(View.GONE); binding.translate.setVisibility(View.GONE);
binding.originalMessage.setVisibility(View.INVISIBLE); binding.originalMessage.setVisibility(View.INVISIBLE);
hideSystemUI();
} }
toggleScreenContain(fullscreen);
} }
private void hideSystemUI() { private void hideSystemUI() {
@ -490,16 +424,6 @@ public class MediaActivity extends BaseActivity implements OnDownloadInterface {
| View.SYSTEM_UI_FLAG_FULLSCREEN); | View.SYSTEM_UI_FLAG_FULLSCREEN);
} }
// Shows the system bars by removing all the flags
// except for the ones that make the content appear under the system bars.
private void showSystemUI() {
View decorView = getWindow().getDecorView();
decorView.setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
}
public FragmentMedia getCurrentFragment() { public FragmentMedia getCurrentFragment() {
return mCurrentFragment; return mCurrentFragment;
} }

View file

@ -1,4 +1,4 @@
package app.fedilab.android.activities; package app.fedilab.android.mastodon.activities;
/* Copyright 2022 Thomas Schneider /* Copyright 2022 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -16,7 +16,6 @@ package app.fedilab.android.activities;
import android.content.Intent; import android.content.Intent;
import android.graphics.drawable.ColorDrawable;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.text.method.LinkMovementMethod; import android.text.method.LinkMovementMethod;
@ -25,7 +24,6 @@ import android.view.View;
import android.widget.TextView; import android.widget.TextView;
import androidx.core.app.ActivityOptionsCompat; import androidx.core.app.ActivityOptionsCompat;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import java.util.ArrayList; import java.util.ArrayList;
@ -33,29 +31,27 @@ import java.util.List;
import app.fedilab.android.BaseMainActivity; import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.client.entities.api.Account;
import app.fedilab.android.client.entities.api.Status;
import app.fedilab.android.databinding.ActivityPartnershipBinding; import app.fedilab.android.databinding.ActivityPartnershipBinding;
import app.fedilab.android.helper.CrossActionHelper; import app.fedilab.android.mastodon.client.entities.api.Account;
import app.fedilab.android.helper.Helper; import app.fedilab.android.mastodon.client.entities.api.Status;
import app.fedilab.android.helper.MastodonHelper; import app.fedilab.android.mastodon.helper.CrossActionHelper;
import app.fedilab.android.helper.ThemeHelper; import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.viewmodel.mastodon.AccountsVM; import app.fedilab.android.mastodon.helper.MastodonHelper;
import app.fedilab.android.mastodon.viewmodel.mastodon.AccountsVM;
public class PartnerShipActivity extends BaseActivity { public class PartnerShipActivity extends BaseBarActivity {
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
ThemeHelper.applyThemeBar(this);
ActivityPartnershipBinding binding = ActivityPartnershipBinding.inflate(getLayoutInflater()); ActivityPartnershipBinding binding = ActivityPartnershipBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); setContentView(binding.getRoot());
if (getSupportActionBar() != null) { if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
} }
TextView about_partnership = findViewById(R.id.about_partnership); TextView about_partnership = findViewById(R.id.about_partnership);
@ -65,9 +61,8 @@ public class PartnerShipActivity extends BaseActivity {
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://masto.host")); Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://masto.host"));
startActivity(browserIntent); startActivity(browserIntent);
}); });
binding.accountFollow.setBackgroundTintList(ThemeHelper.getButtonActionColorStateList(PartnerShipActivity.this));
setTitle(R.string.action_partnership); setTitle(R.string.action_partnership);
binding.accountFollow.setImageResource(R.drawable.ic_baseline_person_add_24); binding.accountFollow.setIconResource(R.drawable.ic_baseline_person_add_24);
CrossActionHelper.fetchRemoteAccount(PartnerShipActivity.this, "@mastohost@mastodon.social", new CrossActionHelper.Callback() { CrossActionHelper.fetchRemoteAccount(PartnerShipActivity.this, "@mastohost@mastodon.social", new CrossActionHelper.Callback() {
@Override @Override
public void federatedStatus(Status status) { public void federatedStatus(Status status) {
@ -98,7 +93,7 @@ public class PartnerShipActivity extends BaseActivity {
if (relationShips != null && relationShips.size() > 0) { if (relationShips != null && relationShips.size() > 0) {
if (!relationShips.get(0).following) { if (!relationShips.get(0).following) {
binding.accountFollow.setVisibility(View.VISIBLE); binding.accountFollow.setVisibility(View.VISIBLE);
binding.accountFollow.setOnClickListener(v -> accountsVM.follow(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id, true, false) binding.accountFollow.setOnClickListener(v -> accountsVM.follow(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id, true, false, null)
.observe(PartnerShipActivity.this, relationShip -> binding.accountFollow.setVisibility(View.GONE))); .observe(PartnerShipActivity.this, relationShip -> binding.accountFollow.setVisibility(View.GONE)));
} }
} }

View file

@ -1,4 +1,4 @@
package app.fedilab.android.activities; package app.fedilab.android.mastodon.activities;
/* Copyright 2022 Thomas Schneider /* Copyright 2022 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -25,7 +25,7 @@ import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.res.ColorStateList; import android.content.res.ColorStateList;
import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Animatable;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.os.Bundle; import android.os.Bundle;
import android.text.SpannableString; import android.text.SpannableString;
@ -34,6 +34,7 @@ import android.text.method.LinkMovementMethod;
import android.text.style.ForegroundColorSpan; import android.text.style.ForegroundColorSpan;
import android.text.style.UnderlineSpan; import android.text.style.UnderlineSpan;
import android.util.TypedValue; import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
@ -48,6 +49,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.core.app.ActivityCompat;
import androidx.core.app.ActivityOptionsCompat; import androidx.core.app.ActivityOptionsCompat;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
@ -59,6 +61,7 @@ import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide; import com.bumptech.glide.Glide;
import com.bumptech.glide.request.target.CustomTarget; import com.bumptech.glide.request.target.CustomTarget;
import com.bumptech.glide.request.transition.Transition; import com.bumptech.glide.request.transition.Transition;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.tabs.TabLayout; import com.google.android.material.tabs.TabLayout;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
@ -73,31 +76,35 @@ import java.util.concurrent.TimeUnit;
import app.fedilab.android.BaseMainActivity; import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.client.entities.api.Account; import app.fedilab.android.activities.MainActivity;
import app.fedilab.android.client.entities.api.Attachment;
import app.fedilab.android.client.entities.api.Field;
import app.fedilab.android.client.entities.api.IdentityProof;
import app.fedilab.android.client.entities.api.MastodonList;
import app.fedilab.android.client.entities.api.RelationShip;
import app.fedilab.android.client.entities.app.Pinned;
import app.fedilab.android.client.entities.app.PinnedTimeline;
import app.fedilab.android.client.entities.app.RemoteInstance;
import app.fedilab.android.client.entities.app.Timeline;
import app.fedilab.android.client.entities.app.WellKnownNodeinfo;
import app.fedilab.android.databinding.ActivityProfileBinding; import app.fedilab.android.databinding.ActivityProfileBinding;
import app.fedilab.android.exception.DBException; import app.fedilab.android.databinding.NotificationsRelatedAccountsBinding;
import app.fedilab.android.helper.CrossActionHelper; import app.fedilab.android.mastodon.client.entities.api.Account;
import app.fedilab.android.helper.Helper; import app.fedilab.android.mastodon.client.entities.api.Attachment;
import app.fedilab.android.helper.MastodonHelper; import app.fedilab.android.mastodon.client.entities.api.FamiliarFollowers;
import app.fedilab.android.helper.SpannableHelper; import app.fedilab.android.mastodon.client.entities.api.Field;
import app.fedilab.android.helper.ThemeHelper; import app.fedilab.android.mastodon.client.entities.api.IdentityProof;
import app.fedilab.android.ui.drawer.FieldAdapter; import app.fedilab.android.mastodon.client.entities.api.MastodonList;
import app.fedilab.android.ui.drawer.IdentityProofsAdapter; import app.fedilab.android.mastodon.client.entities.api.RelationShip;
import app.fedilab.android.ui.pageadapter.FedilabProfileTLPageAdapter; import app.fedilab.android.mastodon.client.entities.app.Languages;
import app.fedilab.android.viewmodel.mastodon.AccountsVM; import app.fedilab.android.mastodon.client.entities.app.Pinned;
import app.fedilab.android.viewmodel.mastodon.NodeInfoVM; import app.fedilab.android.mastodon.client.entities.app.PinnedTimeline;
import app.fedilab.android.viewmodel.mastodon.ReorderVM; import app.fedilab.android.mastodon.client.entities.app.RemoteInstance;
import app.fedilab.android.viewmodel.mastodon.TimelinesVM; import app.fedilab.android.mastodon.client.entities.app.Timeline;
import app.fedilab.android.mastodon.client.entities.app.WellKnownNodeinfo;
import app.fedilab.android.mastodon.exception.DBException;
import app.fedilab.android.mastodon.helper.CrossActionHelper;
import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.mastodon.helper.MastodonHelper;
import app.fedilab.android.mastodon.helper.SpannableHelper;
import app.fedilab.android.mastodon.helper.ThemeHelper;
import app.fedilab.android.mastodon.ui.drawer.FieldAdapter;
import app.fedilab.android.mastodon.ui.drawer.IdentityProofsAdapter;
import app.fedilab.android.mastodon.ui.pageadapter.FedilabProfileTLPageAdapter;
import app.fedilab.android.mastodon.viewmodel.mastodon.AccountsVM;
import app.fedilab.android.mastodon.viewmodel.mastodon.NodeInfoVM;
import app.fedilab.android.mastodon.viewmodel.mastodon.ReorderVM;
import app.fedilab.android.mastodon.viewmodel.mastodon.TimelinesVM;
import es.dmoral.toasty.Toasty; import es.dmoral.toasty.Toasty;
@ -105,6 +112,7 @@ public class ProfileActivity extends BaseActivity {
private RelationShip relationship; private RelationShip relationship;
private FamiliarFollowers familiarFollowers;
private Account account; private Account account;
private ScheduledExecutorService scheduledExecutorService; private ScheduledExecutorService scheduledExecutorService;
private action doAction; private action doAction;
@ -115,7 +123,7 @@ public class ProfileActivity extends BaseActivity {
private String account_id; private String account_id;
private String mention_str; private String mention_str;
private WellKnownNodeinfo.NodeInfo nodeInfo; private WellKnownNodeinfo.NodeInfo nodeInfo;
private boolean checkRemotely;
private final BroadcastReceiver broadcast_data = new BroadcastReceiver() { private final BroadcastReceiver broadcast_data = new BroadcastReceiver() {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
@ -130,38 +138,43 @@ public class ProfileActivity extends BaseActivity {
} }
} }
}; };
private boolean homeMuted;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
ThemeHelper.applyTheme(this);
binding = ActivityProfileBinding.inflate(getLayoutInflater()); binding = ActivityProfileBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); setContentView(binding.getRoot());
setSupportActionBar(binding.toolbar); setSupportActionBar(binding.toolbar);
ActionBar actionBar = getSupportActionBar(); ActionBar actionBar = getSupportActionBar();
Bundle b = getIntent().getExtras(); Bundle b = getIntent().getExtras();
binding.accountFollow.setEnabled(false); binding.accountFollow.setEnabled(false);
checkRemotely = false;
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(this);
homeMuted = false;
if (b != null) { if (b != null) {
account = (Account) b.getSerializable(Helper.ARG_ACCOUNT); account = (Account) b.getSerializable(Helper.ARG_ACCOUNT);
account_id = b.getString(Helper.ARG_USER_ID, null); account_id = b.getString(Helper.ARG_USER_ID, null);
mention_str = b.getString(Helper.ARG_MENTION, null); mention_str = b.getString(Helper.ARG_MENTION, null);
checkRemotely = b.getBoolean(Helper.ARG_CHECK_REMOTELY, false);
} }
postponeEnterTransition(); if (!checkRemotely) {
checkRemotely = sharedpreferences.getBoolean(getString(R.string.SET_PROFILE_REMOTELY), false);
}
ActivityCompat.postponeEnterTransition(ProfileActivity.this);
//Remove title //Remove title
if (actionBar != null) { if (actionBar != null) {
actionBar.setDisplayShowTitleEnabled(false); actionBar.setDisplayShowTitleEnabled(false);
actionBar.setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
} }
if (getSupportActionBar() != null) { if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true); getSupportActionBar().setDisplayShowHomeEnabled(true);
} }
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(this);
float scale = sharedpreferences.getFloat(getString(R.string.SET_FONT_SCALE), 1.1f); float scale = sharedpreferences.getFloat(getString(R.string.SET_FONT_SCALE), 1.1f);
binding.title.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18 * 1.1f / scale); binding.title.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18 * 1.1f / scale);
binding.toolbar.setPopupTheme(Helper.popupStyle());
accountsVM = new ViewModelProvider(ProfileActivity.this).get(AccountsVM.class); accountsVM = new ViewModelProvider(ProfileActivity.this).get(AccountsVM.class);
if (account != null) { if (account != null) {
initializeView(account); initializeView(account);
@ -176,21 +189,47 @@ public class ProfileActivity extends BaseActivity {
account = accounts.get(0); account = accounts.get(0);
initializeView(account); initializeView(account);
} else { } else {
Toasty.error(ProfileActivity.this, getString(R.string.toast_error_loading_account), Toast.LENGTH_LONG).show(); Toasty.error(ProfileActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show();
finish(); finish();
} }
}); });
} else { } else {
Toasty.error(ProfileActivity.this, getString(R.string.toast_error_loading_account), Toast.LENGTH_LONG).show(); Toasty.error(ProfileActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show();
finish(); finish();
} }
//Check if account is homeMuted
accountsVM.isMuted(currentAccount, account).observe(this, result -> homeMuted = result != null && result);
LocalBroadcastManager.getInstance(ProfileActivity.this).registerReceiver(broadcast_data, new IntentFilter(Helper.BROADCAST_DATA)); LocalBroadcastManager.getInstance(ProfileActivity.this).registerReceiver(broadcast_data, new IntentFilter(Helper.BROADCAST_DATA));
} }
private void updateViewWithNewData(Account account) {
if (account != null) {
if (account.role != null && account.role.highlighted) {
binding.accountRole.setText(account.role.name);
binding.accountRole.setVisibility(View.VISIBLE);
}
if (binding.accountTabLayout.getTabCount() > 2) {
TabLayout.Tab statusTab = binding.accountTabLayout.getTabAt(0);
TabLayout.Tab followingTab = binding.accountTabLayout.getTabAt(1);
TabLayout.Tab followerTab = binding.accountTabLayout.getTabAt(2);
if (statusTab != null) {
statusTab.setText(getString(R.string.status_cnt, Helper.withSuffix(account.statuses_count)));
}
if (followingTab != null) {
followingTab.setText(getString(R.string.following_cnt, Helper.withSuffix(account.following_count)));
}
if (followerTab != null) {
followerTab.setText(getString(R.string.followers_cnt, Helper.withSuffix(account.followers_count)));
}
}
}
}
private void initializeView(Account account) { private void initializeView(Account account) {
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(ProfileActivity.this); SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(ProfileActivity.this);
if (account == null) { if (account == null) {
Toasty.error(ProfileActivity.this, getString(R.string.toast_error_loading_account), Toast.LENGTH_LONG).show(); Toasty.error(ProfileActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show();
finish(); finish();
return; return;
} }
@ -223,6 +262,13 @@ public class ProfileActivity extends BaseActivity {
updateAccount(); updateAccount();
} }
}); });
accountsVM.getFamiliarFollowers(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, accountListToCheck).observe(ProfileActivity.this, familiarFollowersList -> {
if (familiarFollowersList != null && familiarFollowersList.size() > 0) {
this.familiarFollowers = familiarFollowersList.get(0);
updateAccount();
}
});
//Retrieve identity proofs //Retrieve identity proofs
accountsVM.getIdentityProofs(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id).observe(ProfileActivity.this, identityProofs -> { accountsVM.getIdentityProofs(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id).observe(ProfileActivity.this, identityProofs -> {
this.identityProofList = identityProofs; this.identityProofList = identityProofs;
@ -239,7 +285,7 @@ public class ProfileActivity extends BaseActivity {
binding.accountTabLayout.clearOnTabSelectedListeners(); binding.accountTabLayout.clearOnTabSelectedListeners();
binding.accountTabLayout.removeAllTabs(); binding.accountTabLayout.removeAllTabs();
//Tablayout for timelines/following/followers //Tablayout for timelines/following/followers
FedilabProfileTLPageAdapter fedilabProfileTLPageAdapter = new FedilabProfileTLPageAdapter(getSupportFragmentManager(), account); FedilabProfileTLPageAdapter fedilabProfileTLPageAdapter = new FedilabProfileTLPageAdapter(getSupportFragmentManager(), account, checkRemotely);
binding.accountTabLayout.addTab(binding.accountTabLayout.newTab().setText(getString(R.string.status_cnt, Helper.withSuffix(account.statuses_count)))); binding.accountTabLayout.addTab(binding.accountTabLayout.newTab().setText(getString(R.string.status_cnt, Helper.withSuffix(account.statuses_count))));
binding.accountTabLayout.addTab(binding.accountTabLayout.newTab().setText(getString(R.string.following_cnt, Helper.withSuffix(account.following_count)))); binding.accountTabLayout.addTab(binding.accountTabLayout.newTab().setText(getString(R.string.following_cnt, Helper.withSuffix(account.following_count))));
binding.accountTabLayout.addTab(binding.accountTabLayout.newTab().setText(getString(R.string.followers_cnt, Helper.withSuffix(account.followers_count)))); binding.accountTabLayout.addTab(binding.accountTabLayout.newTab().setText(getString(R.string.followers_cnt, Helper.withSuffix(account.followers_count))));
@ -260,10 +306,9 @@ public class ProfileActivity extends BaseActivity {
public void onTabReselected(TabLayout.Tab tab) { public void onTabReselected(TabLayout.Tab tab) {
} }
}); });
binding.accountTabLayout.setTabTextColors(ThemeHelper.getAttColor(ProfileActivity.this, R.attr.mTextColor), ContextCompat.getColor(ProfileActivity.this, R.color.cyanea_accent_dark_reference));
binding.accountTabLayout.setTabIconTint(ThemeHelper.getColorStateList(ProfileActivity.this));
boolean disableGif = sharedpreferences.getBoolean(getString(R.string.SET_DISABLE_GIF), false); boolean disableGif = sharedpreferences.getBoolean(getString(R.string.SET_DISABLE_GIF), false);
String targetedUrl = disableGif ? account.avatar_static : account.avatar; String targetedUrl = disableGif ? account.avatar_static : account.avatar;
// MastodonHelper.loadPPMastodon(binding.accountPp, account);
Glide.with(ProfileActivity.this) Glide.with(ProfileActivity.this)
.asDrawable() .asDrawable()
.dontTransform() .dontTransform()
@ -272,23 +317,32 @@ public class ProfileActivity extends BaseActivity {
@Override @Override
public void onResourceReady(@NonNull final Drawable resource, Transition<? super Drawable> transition) { public void onResourceReady(@NonNull final Drawable resource, Transition<? super Drawable> transition) {
binding.profilePicture.setImageDrawable(resource); binding.profilePicture.setImageDrawable(resource);
startPostponedEnterTransition(); binding.accountPp.setImageDrawable(resource);
if (resource instanceof Animatable) {
binding.profilePicture.animate();
binding.accountPp.animate();
((Animatable) resource).start();
}
ActivityCompat.startPostponedEnterTransition(ProfileActivity.this);
} }
@Override @Override
public void onLoadFailed(@Nullable Drawable errorDrawable) { public void onLoadFailed(@Nullable Drawable errorDrawable) {
binding.profilePicture.setImageResource(R.drawable.ic_person); binding.profilePicture.setImageResource(R.drawable.ic_person);
startPostponedEnterTransition(); binding.accountPp.setImageResource(R.drawable.ic_person);
ActivityCompat.startPostponedEnterTransition(ProfileActivity.this);
} }
@Override @Override
public void onLoadCleared(@Nullable Drawable placeholder) { public void onLoadCleared(@Nullable Drawable placeholder) {
binding.profilePicture.setImageResource(R.drawable.ic_person);
binding.accountPp.setImageResource(R.drawable.ic_person);
ActivityCompat.startPostponedEnterTransition(ProfileActivity.this);
} }
} }
); );
//Load header //Load header
MastodonHelper.loadProfileMediaMastodon(binding.bannerPp, account, MastodonHelper.MediaAccountType.HEADER); MastodonHelper.loadProfileMediaMastodon(ProfileActivity.this, binding.bannerPp, account, MastodonHelper.MediaAccountType.HEADER);
//Redraws icon for locked accounts //Redraws icon for locked accounts
final float scale = getResources().getDisplayMetrics().density; final float scale = getResources().getDisplayMetrics().density;
if (account.locked) { if (account.locked) {
@ -311,7 +365,7 @@ public class ProfileActivity extends BaseActivity {
final SpannableString content = new SpannableString(getString(R.string.disclaimer_full)); final SpannableString content = new SpannableString(getString(R.string.disclaimer_full));
content.setSpan(new UnderlineSpan(), 0, content.length(), 0); content.setSpan(new UnderlineSpan(), 0, content.length(), 0);
content.setSpan(new ForegroundColorSpan(ContextCompat.getColor(ProfileActivity.this, R.color.cyanea_accent_reference)), 0, content.length(), content.setSpan(new ForegroundColorSpan(ThemeHelper.getAttColor(this, R.attr.colorPrimary)), 0, content.length(),
Spanned.SPAN_INCLUSIVE_EXCLUSIVE); Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
binding.warningMessage.setText(content); binding.warningMessage.setText(content);
binding.warningMessage.setOnClickListener(view -> { binding.warningMessage.setOnClickListener(view -> {
@ -339,15 +393,28 @@ public class ProfileActivity extends BaseActivity {
binding.accountMoved.setMovementMethod(LinkMovementMethod.getInstance()); binding.accountMoved.setMovementMethod(LinkMovementMethod.getInstance());
} }
if (account.acct != null && account.acct.contains("@")) if (account.acct != null && account.acct.contains("@"))
binding.warningMessage.setVisibility(View.VISIBLE); binding.warningContainer.setVisibility(View.VISIBLE);
else else
binding.warningMessage.setVisibility(View.GONE); binding.warningContainer.setVisibility(View.GONE);
if (checkRemotely) {
binding.openRemoteProfile.setVisibility(View.GONE);
}
binding.openRemoteProfile.setOnClickListener(v -> {
Intent intent = new Intent(ProfileActivity.this, ProfileActivity.class);
Bundle b = new Bundle();
b.putSerializable(Helper.ARG_ACCOUNT, account);
b.putSerializable(Helper.ARG_CHECK_REMOTELY, true);
intent.putExtras(b);
ActivityOptionsCompat options = ActivityOptionsCompat
.makeSceneTransitionAnimation(ProfileActivity.this, binding.profilePicture, getString(R.string.activity_porfile_pp));
startActivity(intent, options.toBundle());
finish();
});
//Fields for profile //Fields for profile
List<Field> fields = account.fields; List<Field> fields = account.fields;
if (fields != null && fields.size() > 0) { if (fields != null && fields.size() > 0) {
FieldAdapter fieldAdapter = new FieldAdapter(fields); FieldAdapter fieldAdapter = new FieldAdapter(fields, account);
binding.fieldsContainer.setAdapter(fieldAdapter); binding.fieldsContainer.setAdapter(fieldAdapter);
binding.fieldsContainer.setLayoutManager(new LinearLayoutManager(ProfileActivity.this)); binding.fieldsContainer.setLayoutManager(new LinearLayoutManager(ProfileActivity.this));
} }
@ -375,7 +442,7 @@ public class ProfileActivity extends BaseActivity {
TextView.BufferType.SPANNABLE); TextView.BufferType.SPANNABLE);
binding.accountNote.setMovementMethod(LinkMovementMethod.getInstance()); binding.accountNote.setMovementMethod(LinkMovementMethod.getInstance());
MastodonHelper.loadPPMastodon(binding.accountPp, account);
binding.accountPp.setOnClickListener(v -> { binding.accountPp.setOnClickListener(v -> {
Intent intent = new Intent(ProfileActivity.this, MediaActivity.class); Intent intent = new Intent(ProfileActivity.this, MediaActivity.class);
Bundle b = new Bundle(); Bundle b = new Bundle();
@ -402,7 +469,7 @@ public class ProfileActivity extends BaseActivity {
Toasty.info(ProfileActivity.this, getString(R.string.nothing_to_do), Toast.LENGTH_LONG).show(); Toasty.info(ProfileActivity.this, getString(R.string.nothing_to_do), Toast.LENGTH_LONG).show();
} else if (doAction == action.FOLLOW) { } else if (doAction == action.FOLLOW) {
binding.accountFollow.setEnabled(false); binding.accountFollow.setEnabled(false);
accountsVM.follow(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id, true, false) accountsVM.follow(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id, true, false, null)
.observe(ProfileActivity.this, relationShip -> { .observe(ProfileActivity.this, relationShip -> {
this.relationship = relationShip; this.relationship = relationShip;
updateAccount(); updateAccount();
@ -410,7 +477,7 @@ public class ProfileActivity extends BaseActivity {
} else if (doAction == action.UNFOLLOW) { } else if (doAction == action.UNFOLLOW) {
boolean confirm_unfollow = sharedpreferences.getBoolean(getString(R.string.SET_UNFOLLOW_VALIDATION), true); boolean confirm_unfollow = sharedpreferences.getBoolean(getString(R.string.SET_UNFOLLOW_VALIDATION), true);
if (confirm_unfollow) { if (confirm_unfollow) {
AlertDialog.Builder unfollowConfirm = new AlertDialog.Builder(ProfileActivity.this, Helper.dialogStyle()); AlertDialog.Builder unfollowConfirm = new MaterialAlertDialogBuilder(ProfileActivity.this);
unfollowConfirm.setTitle(getString(R.string.unfollow_confirm)); unfollowConfirm.setTitle(getString(R.string.unfollow_confirm));
unfollowConfirm.setMessage(account.acct); unfollowConfirm.setMessage(account.acct);
unfollowConfirm.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss()); unfollowConfirm.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
@ -466,16 +533,19 @@ public class ProfileActivity extends BaseActivity {
binding.instanceInfo.setVisibility(View.VISIBLE); binding.instanceInfo.setVisibility(View.VISIBLE);
binding.instanceInfo.setOnClickListener(v -> { binding.instanceInfo.setOnClickListener(v -> {
Intent intent = new Intent(ProfileActivity.this, InstanceProfileActivity.class); InstanceProfileActivity instanceProfileActivity = new InstanceProfileActivity();
Bundle b = new Bundle(); Bundle b = new Bundle();
b.putString(Helper.ARG_INSTANCE, finalAccountInstance); b.putString(Helper.ARG_INSTANCE, finalAccountInstance);
intent.putExtras(b); instanceProfileActivity.setArguments(b);
startActivity(intent); instanceProfileActivity.show(getSupportFragmentManager(), null);
}); });
} }
}); });
if (accountInstance != null && !accountInstance.equalsIgnoreCase(MainActivity.currentInstance)) {
accountsVM.lookUpAccount(accountInstance, account.username).observe(ProfileActivity.this, this::updateViewWithNewData);
} else if (accountInstance != null && accountInstance.equalsIgnoreCase(MainActivity.currentInstance)) {
updateViewWithNewData(account);
}
} }
@ -486,12 +556,7 @@ public class ProfileActivity extends BaseActivity {
if (currentAccount == null || account == null) { if (currentAccount == null || account == null) {
return; return;
} }
//The value for account is from same server so id can be used
if (account.id.equals(currentAccount.user_id)) {
binding.accountFollow.setVisibility(View.GONE);
binding.headerEditProfile.setVisibility(View.VISIBLE);
binding.headerEditProfile.bringToFront();
}
//Manage indentity proofs if not yet displayed //Manage indentity proofs if not yet displayed
if (identityProofList != null && identityProofList.size() > 0) { if (identityProofList != null && identityProofList.size() > 0) {
@ -500,7 +565,7 @@ public class ProfileActivity extends BaseActivity {
//Recyclerview for identity proof has not been inflated yet //Recyclerview for identity proof has not been inflated yet
if (identityProofsRecycler == null) { if (identityProofsRecycler == null) {
identity_proofs_indicator.setOnClickListener(v -> { identity_proofs_indicator.setOnClickListener(v -> {
AlertDialog.Builder builder = new AlertDialog.Builder(ProfileActivity.this, Helper.dialogStyle()); AlertDialog.Builder builder = new MaterialAlertDialogBuilder(ProfileActivity.this);
identityProofsRecycler = new RecyclerView(ProfileActivity.this); identityProofsRecycler = new RecyclerView(ProfileActivity.this);
LinearLayoutManager mLayoutManager = new LinearLayoutManager(ProfileActivity.this); LinearLayoutManager mLayoutManager = new LinearLayoutManager(ProfileActivity.this);
identityProofsRecycler.setLayoutManager(mLayoutManager); identityProofsRecycler.setLayoutManager(mLayoutManager);
@ -514,13 +579,33 @@ public class ProfileActivity extends BaseActivity {
}); });
} }
} }
binding.accountFollow.setBackgroundTintList(ThemeHelper.getButtonActionColorStateList(ProfileActivity.this));
binding.headerEditProfile.setBackgroundTintList(ThemeHelper.getButtonActionColorStateList(ProfileActivity.this)); if (familiarFollowers != null && familiarFollowers.accounts != null && familiarFollowers.accounts.size() > 0) {
binding.relatedAccounts.removeAllViews();
for (Account account : familiarFollowers.accounts) {
NotificationsRelatedAccountsBinding notificationsRelatedAccountsBinding = NotificationsRelatedAccountsBinding.inflate(LayoutInflater.from(ProfileActivity.this));
MastodonHelper.loadProfileMediaMastodonRound(ProfileActivity.this, notificationsRelatedAccountsBinding.profilePicture, account);
notificationsRelatedAccountsBinding.acc.setText(account.username);
notificationsRelatedAccountsBinding.relatedAccountContainer.setOnClickListener(v -> {
Intent intent = new Intent(ProfileActivity.this, ProfileActivity.class);
Bundle b = new Bundle();
b.putSerializable(Helper.ARG_ACCOUNT, account);
intent.putExtras(b);
ActivityOptionsCompat options = ActivityOptionsCompat
.makeSceneTransitionAnimation(ProfileActivity.this, notificationsRelatedAccountsBinding.profilePicture, getString(R.string.activity_porfile_pp));
// start the new activity
startActivity(intent, options.toBundle());
});
binding.relatedAccounts.addView(notificationsRelatedAccountsBinding.getRoot());
}
binding.familiarFollowers.setVisibility(View.VISIBLE);
}
binding.accountFollow.setEnabled(true); binding.accountFollow.setEnabled(true);
//Visibility depending of the relationship //Visibility depending of the relationship
if (relationship != null) { if (relationship != null) {
if (relationship.blocked_by) { if (relationship.blocked_by) {
binding.accountFollow.setImageResource(R.drawable.ic_baseline_person_add_24); binding.accountFollow.setIconResource(R.drawable.ic_baseline_person_add_24);
binding.accountFollow.setVisibility(View.VISIBLE); binding.accountFollow.setVisibility(View.VISIBLE);
binding.accountFollow.setEnabled(false); binding.accountFollow.setEnabled(false);
binding.accountFollow.setContentDescription(getString(R.string.action_disabled)); binding.accountFollow.setContentDescription(getString(R.string.action_disabled));
@ -528,7 +613,7 @@ public class ProfileActivity extends BaseActivity {
if (relationship.requested) { if (relationship.requested) {
binding.accountFollowRequest.setVisibility(View.VISIBLE); binding.accountFollowRequest.setVisibility(View.VISIBLE);
binding.accountFollow.setImageResource(R.drawable.ic_baseline_hourglass_full_24); binding.accountFollow.setIconResource(R.drawable.ic_baseline_hourglass_full_24);
binding.accountFollow.setVisibility(View.VISIBLE); binding.accountFollow.setVisibility(View.VISIBLE);
binding.accountFollow.setContentDescription(getString(R.string.follow_request)); binding.accountFollow.setContentDescription(getString(R.string.follow_request));
doAction = action.UNFOLLOW; doAction = action.UNFOLLOW;
@ -539,37 +624,44 @@ public class ProfileActivity extends BaseActivity {
binding.accountFollowedBy.setVisibility(View.GONE); binding.accountFollowedBy.setVisibility(View.GONE);
} }
if (relationship.following) { if (relationship.following) {
binding.accountFollow.setImageResource(R.drawable.ic_baseline_person_remove_24); binding.accountFollow.setIconResource(R.drawable.ic_baseline_person_remove_24);
binding.accountFollow.setBackgroundTintList(ColorStateList.valueOf(ContextCompat.getColor(ProfileActivity.this, R.color.red_1))); binding.accountFollow.setBackgroundTintList(ColorStateList.valueOf(ThemeHelper.getAttColor(this, R.attr.colorError)));
doAction = action.UNFOLLOW; doAction = action.UNFOLLOW;
binding.accountFollow.setContentDescription(getString(R.string.action_unfollow)); binding.accountFollow.setContentDescription(getString(R.string.action_unfollow));
binding.accountFollow.setVisibility(View.VISIBLE); binding.accountFollow.setVisibility(View.VISIBLE);
} else if (relationship.blocking) { } else if (relationship.blocking) {
binding.accountFollow.setBackgroundTintList(ColorStateList.valueOf(ContextCompat.getColor(ProfileActivity.this, R.color.red_1))); binding.accountFollow.setBackgroundTintList(ColorStateList.valueOf(ThemeHelper.getAttColor(this, R.attr.colorError)));
binding.accountFollow.setImageResource(R.drawable.ic_baseline_lock_open_24); binding.accountFollow.setIconResource(R.drawable.ic_baseline_lock_open_24);
doAction = action.UNBLOCK; doAction = action.UNBLOCK;
binding.accountFollow.setVisibility(View.VISIBLE); binding.accountFollow.setVisibility(View.VISIBLE);
binding.accountFollow.setContentDescription(getString(R.string.action_unblock)); binding.accountFollow.setContentDescription(getString(R.string.action_unblock));
} else { } else {
binding.accountFollow.setImageResource(R.drawable.ic_baseline_person_add_24); binding.accountFollow.setIconResource(R.drawable.ic_baseline_person_add_24);
doAction = action.FOLLOW; doAction = action.FOLLOW;
binding.accountFollow.setVisibility(View.VISIBLE); binding.accountFollow.setVisibility(View.VISIBLE);
binding.accountFollow.setContentDescription(getString(R.string.action_follow)); binding.accountFollow.setContentDescription(getString(R.string.action_follow));
} }
//The value for account is from same server so id can be used
if (account.id.equals(currentAccount.user_id)) {
binding.accountFollow.setVisibility(View.GONE);
binding.headerEditProfile.setVisibility(View.VISIBLE);
binding.headerEditProfile.bringToFront();
}
if (!relationship.following) { if (!relationship.following) {
binding.accountNotification.setVisibility(View.GONE); binding.accountNotification.setVisibility(View.GONE);
} else { } else {
binding.accountNotification.setVisibility(View.VISIBLE); binding.accountNotification.setVisibility(View.VISIBLE);
} }
if (relationship.notifying) { if (relationship.notifying) {
binding.accountNotification.setImageResource(R.drawable.ic_baseline_notifications_active_24); binding.accountNotification.setIconResource(R.drawable.ic_baseline_notifications_active_24);
} else { } else {
binding.accountNotification.setImageResource(R.drawable.ic_baseline_notifications_off_24); binding.accountNotification.setIconResource(R.drawable.ic_baseline_notifications_off_24);
} }
binding.accountNotification.setOnClickListener(v -> { binding.accountNotification.setOnClickListener(v -> {
if (relationship != null && relationship.following) { if (relationship != null && relationship.following) {
relationship.notifying = !relationship.notifying; relationship.notifying = !relationship.notifying;
accountsVM.follow(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id, relationship.showing_reblogs, relationship.notifying) accountsVM.follow(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id, relationship.showing_reblogs, relationship.notifying, relationship.languages)
.observe(ProfileActivity.this, relationShip -> { .observe(ProfileActivity.this, relationShip -> {
this.relationship = relationShip; this.relationship = relationShip;
updateAccount(); updateAccount();
@ -583,7 +675,7 @@ public class ProfileActivity extends BaseActivity {
binding.personalNote.setText(relationship.note); binding.personalNote.setText(relationship.note);
} }
binding.personalNote.setOnClickListener(view -> { binding.personalNote.setOnClickListener(view -> {
AlertDialog.Builder builderInner = new AlertDialog.Builder(ProfileActivity.this, Helper.dialogStyle()); AlertDialog.Builder builderInner = new MaterialAlertDialogBuilder(ProfileActivity.this);
builderInner.setTitle(R.string.note_for_account); builderInner.setTitle(R.string.note_for_account);
EditText input = new EditText(ProfileActivity.this); EditText input = new EditText(ProfileActivity.this);
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
@ -626,6 +718,7 @@ public class ProfileActivity extends BaseActivity {
menu.findItem(R.id.action_block).setVisible(false); menu.findItem(R.id.action_block).setVisible(false);
menu.findItem(R.id.action_report).setVisible(false); menu.findItem(R.id.action_report).setVisible(false);
menu.findItem(R.id.action_mute).setVisible(false); menu.findItem(R.id.action_mute).setVisible(false);
menu.findItem(R.id.action_timed_mute).setVisible(false);
menu.findItem(R.id.action_mention).setVisible(false); menu.findItem(R.id.action_mention).setVisible(false);
menu.findItem(R.id.action_follow_instance).setVisible(false); menu.findItem(R.id.action_follow_instance).setVisible(false);
menu.findItem(R.id.action_block_instance).setVisible(false); menu.findItem(R.id.action_block_instance).setVisible(false);
@ -633,9 +726,13 @@ public class ProfileActivity extends BaseActivity {
menu.findItem(R.id.action_endorse).setVisible(false); menu.findItem(R.id.action_endorse).setVisible(false);
menu.findItem(R.id.action_direct_message).setVisible(false); menu.findItem(R.id.action_direct_message).setVisible(false);
menu.findItem(R.id.action_add_to_list).setVisible(false); menu.findItem(R.id.action_add_to_list).setVisible(false);
menu.findItem(R.id.action_mute_home).setVisible(false);
menu.findItem(R.id.action_subscribed_language).setVisible(false);
} else { } else {
menu.findItem(R.id.action_block).setVisible(true); menu.findItem(R.id.action_block).setVisible(true);
menu.findItem(R.id.action_mute).setVisible(true); menu.findItem(R.id.action_mute).setVisible(true);
menu.findItem(R.id.action_mute_home).setVisible(true);
menu.findItem(R.id.action_timed_mute).setVisible(true);
menu.findItem(R.id.action_mention).setVisible(true); menu.findItem(R.id.action_mention).setVisible(true);
} }
//Update menu title depending of relationship //Update menu title depending of relationship
@ -643,6 +740,8 @@ public class ProfileActivity extends BaseActivity {
if (!relationship.following) { if (!relationship.following) {
menu.findItem(R.id.action_hide_boost).setVisible(false); menu.findItem(R.id.action_hide_boost).setVisible(false);
menu.findItem(R.id.action_endorse).setVisible(false); menu.findItem(R.id.action_endorse).setVisible(false);
menu.findItem(R.id.action_mute_home).setVisible(false);
menu.findItem(R.id.action_subscribed_language).setVisible(false);
} }
if (relationship.blocking) { if (relationship.blocking) {
menu.findItem(R.id.action_block).setTitle(R.string.action_unblock); menu.findItem(R.id.action_block).setTitle(R.string.action_unblock);
@ -660,6 +759,11 @@ public class ProfileActivity extends BaseActivity {
} else { } else {
menu.findItem(R.id.action_hide_boost).setTitle(getString(R.string.show_boost, account.username)); menu.findItem(R.id.action_hide_boost).setTitle(getString(R.string.show_boost, account.username));
} }
if (homeMuted) {
menu.findItem(R.id.action_mute_home).setTitle(getString(R.string.unmute_home));
} else {
menu.findItem(R.id.action_mute_home).setTitle(getString(R.string.mute_home));
}
} }
} }
return true; return true;
@ -673,7 +777,6 @@ public class ProfileActivity extends BaseActivity {
splitAcct = account.acct.split("@"); splitAcct = account.acct.split("@");
} }
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(ProfileActivity.this); SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(ProfileActivity.this);
AlertDialog.Builder builderInner = null;
final boolean isOwner = account != null && account.id != null && BaseMainActivity.currentUserID != null && account.id.compareToIgnoreCase(BaseMainActivity.currentUserID) == 0; final boolean isOwner = account != null && account.id != null && BaseMainActivity.currentUserID != null && account.id.compareToIgnoreCase(BaseMainActivity.currentUserID) == 0;
final String[] stringArrayConf; final String[] stringArrayConf;
if (isOwner) { if (isOwner) {
@ -733,6 +836,10 @@ public class ProfileActivity extends BaseActivity {
pinnedTimeline.type = Timeline.TimeLineEnum.REMOTE; pinnedTimeline.type = Timeline.TimeLineEnum.REMOTE;
pinnedTimeline.position = pinned.pinnedTimelines.size(); pinnedTimeline.position = pinned.pinnedTimelines.size();
pinned.pinnedTimelines.add(pinnedTimeline); pinned.pinnedTimelines.add(pinnedTimeline);
if (pinned.instance == null || pinned.user_id == null) {
pinned.instance = MainActivity.currentInstance;
pinned.user_id = MainActivity.currentUserID;
}
Pinned finalPinned = pinned; Pinned finalPinned = pinned;
boolean finalPresent = present; boolean finalPresent = present;
new Thread(() -> { new Thread(() -> {
@ -757,7 +864,7 @@ public class ProfileActivity extends BaseActivity {
}); });
return true; return true;
} else if (itemId == R.id.action_filter) { } else if (itemId == R.id.action_filter) {
AlertDialog.Builder filterTagDialog = new AlertDialog.Builder(ProfileActivity.this, Helper.dialogStyle()); AlertDialog.Builder filterTagDialog = new MaterialAlertDialogBuilder(ProfileActivity.this);
Set<String> featuredTagsSet = sharedpreferences.getStringSet(getString(R.string.SET_FEATURED_TAGS), null); Set<String> featuredTagsSet = sharedpreferences.getStringSet(getString(R.string.SET_FEATURED_TAGS), null);
List<String> tags = new ArrayList<>(); List<String> tags = new ArrayList<>();
if (featuredTagsSet != null) { if (featuredTagsSet != null) {
@ -798,13 +905,70 @@ public class ProfileActivity extends BaseActivity {
.observe(ProfileActivity.this, relationShip -> this.relationship = relationShip); .observe(ProfileActivity.this, relationShip -> this.relationship = relationShip);
} }
return true; return true;
} else if (itemId == R.id.action_subscribed_language) {
if (relationship != null) {
List<String> subscribedLanguages = relationship.languages;
Set<String> storedLanguages = sharedpreferences.getStringSet(getString(R.string.SET_SELECTED_LANGUAGE), null);
List<Languages.Language> languages = Languages.get(ProfileActivity.this);
if (languages == null) {
return true;
}
String[] codesArr;
String[] languagesArr;
boolean[] presentArr;
if (storedLanguages != null && storedLanguages.size() > 0) {
int i = 0;
codesArr = new String[storedLanguages.size()];
languagesArr = new String[storedLanguages.size()];
presentArr = new boolean[storedLanguages.size()];
for (String code : storedLanguages) {
for (Languages.Language language : languages) {
if (language.code.equalsIgnoreCase(code)) {
languagesArr[i] = language.language;
}
}
codesArr[i] = code;
presentArr[i] = subscribedLanguages != null && subscribedLanguages.contains(code);
i++;
}
} else {
codesArr = new String[languages.size()];
presentArr = new boolean[languages.size()];
languagesArr = new String[languages.size()];
int i = 0;
for (Languages.Language language : languages) {
codesArr[i] = language.code;
languagesArr[i] = language.language;
if (subscribedLanguages != null && subscribedLanguages.contains(language.code)) {
presentArr[i] = true;
}
i++;
}
}
AlertDialog.Builder builder = new MaterialAlertDialogBuilder(ProfileActivity.this);
builder.setTitle(getString(R.string.filter_languages));
builder.setMultiChoiceItems(languagesArr, presentArr, (dialog, which, isChecked) -> {
List<String> languagesFilter = new ArrayList<>();
for (int i = 0; i < codesArr.length; i++) {
if (presentArr[i]) {
languagesFilter.add(codesArr[i]);
}
}
accountsVM.follow(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id, relationship.showing_reblogs, relationship.notifying, languagesFilter)
.observe(ProfileActivity.this, relationShip -> this.relationship = relationShip);
});
builder.setNegativeButton(R.string.close, (dialog, which) -> dialog.dismiss());
builder.create().show();
}
return true;
} else if (itemId == R.id.action_hide_boost) { } else if (itemId == R.id.action_hide_boost) {
if (relationship != null) if (relationship != null)
if (relationship.showing_reblogs) { if (relationship.showing_reblogs) {
accountsVM.follow(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id, false, relationship.notifying) accountsVM.follow(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id, false, relationship.notifying, relationship.languages)
.observe(ProfileActivity.this, relationShip -> this.relationship = relationShip); .observe(ProfileActivity.this, relationShip -> this.relationship = relationShip);
} else { } else {
accountsVM.follow(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id, true, relationship.notifying) accountsVM.follow(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id, true, relationship.notifying, relationship.languages)
.observe(ProfileActivity.this, relationShip -> this.relationship = relationShip); .observe(ProfileActivity.this, relationShip -> this.relationship = relationShip);
} }
return true; return true;
@ -826,7 +990,7 @@ public class ProfileActivity extends BaseActivity {
} }
accountsVM.getListContainingAccount(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id) accountsVM.getListContainingAccount(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id)
.observe(ProfileActivity.this, mastodonListUserIs -> { .observe(ProfileActivity.this, mastodonListUserIs -> {
AlertDialog.Builder builderSingle = new AlertDialog.Builder(ProfileActivity.this, Helper.dialogStyle()); AlertDialog.Builder builderSingle = new MaterialAlertDialogBuilder(ProfileActivity.this);
builderSingle.setTitle(getString(R.string.action_lists_add_to)); builderSingle.setTitle(getString(R.string.action_lists_add_to));
builderSingle.setPositiveButton(R.string.close, (dialog, which) -> dialog.dismiss()); builderSingle.setPositiveButton(R.string.close, (dialog, which) -> dialog.dismiss());
String[] listsId = new String[mastodonLists.size()]; String[] listsId = new String[mastodonLists.size()];
@ -848,20 +1012,32 @@ public class ProfileActivity extends BaseActivity {
i++; i++;
} }
builderSingle.setMultiChoiceItems(listsArray, presentArray, (dialog, which, isChecked) -> { builderSingle.setMultiChoiceItems(listsArray, presentArray, (dialog, which, isChecked) -> {
if (!relationship.following) { if (relationship == null || !relationship.following) {
accountsVM.follow(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id, true, false) accountsVM.follow(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id, true, false, null)
.observe(ProfileActivity.this, newRelationShip -> { .observe(ProfileActivity.this, newRelationShip -> {
if (newRelationShip != null) {
relationship = newRelationShip; relationship = newRelationShip;
updateAccount(); updateAccount();
if (isChecked) { if (isChecked) {
timelinesVM.addAccountsList(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, listsId[which], userIds); timelinesVM.addAccountsList(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, listsId[which], userIds).observe(ProfileActivity.this, success -> {
if (success == null || !success) {
Toasty.error(ProfileActivity.this, getString(R.string.toast_error_add_to_list), Toast.LENGTH_LONG).show();
}
});
} else { } else {
timelinesVM.deleteAccountsList(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, listsId[which], userIds); timelinesVM.deleteAccountsList(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, listsId[which], userIds);
} }
} else {
Toasty.error(ProfileActivity.this, getString(R.string.toast_error_add_to_list), Toast.LENGTH_LONG).show();
}
}); });
} else { } else {
if (isChecked) { if (isChecked) {
timelinesVM.addAccountsList(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, listsId[which], userIds); timelinesVM.addAccountsList(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, listsId[which], userIds).observe(ProfileActivity.this, success -> {
if (success == null || !success) {
Toasty.error(ProfileActivity.this, getString(R.string.toast_error_add_to_list), Toast.LENGTH_LONG).show();
}
});
} else { } else {
timelinesVM.deleteAccountsList(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, listsId[which], userIds); timelinesVM.deleteAccountsList(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, listsId[which], userIds);
} }
@ -890,23 +1066,71 @@ public class ProfileActivity extends BaseActivity {
startActivity(intent); startActivity(intent);
return true; return true;
} else if (itemId == R.id.action_mute) { } else if (itemId == R.id.action_mute) {
AlertDialog.Builder builderInner;
if (relationship != null) { if (relationship != null) {
String target;
if (item.getItemId() == R.id.action_block_instance) {
target = account.acct.split("@")[1];
} else {
target = account.id;
}
if (relationship.muting) { if (relationship.muting) {
builderInner = new AlertDialog.Builder(ProfileActivity.this, Helper.dialogStyle()); accountsVM.unmute(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, target)
builderInner.setTitle(stringArrayConf[4]); .observe(ProfileActivity.this, relationShip -> {
doActionAccount = action.UNMUTE; this.relationship = relationShip;
} else { updateAccount();
builderInner = new AlertDialog.Builder(ProfileActivity.this, Helper.dialogStyle()); });
return true;
}
builderInner = new MaterialAlertDialogBuilder(ProfileActivity.this);
builderInner.setTitle(stringArrayConf[0]); builderInner.setTitle(stringArrayConf[0]);
doActionAccount = action.MUTE;
}
} else {
doActionAccount = action.NOTHING;
}
builderInner.setNeutralButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
builderInner.setNegativeButton(R.string.keep_notifications, (dialog, which) -> accountsVM.mute(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, target, false, 0)
.observe(ProfileActivity.this, relationShip -> {
this.relationship = relationShip;
updateAccount();
}));
builderInner.setPositiveButton(R.string.action_mute, (dialog, which) -> {
accountsVM.mute(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, target, true, 0)
.observe(ProfileActivity.this, relationShip -> {
this.relationship = relationShip;
updateAccount();
});
dialog.dismiss();
});
builderInner.show();
}
} else if (itemId == R.id.action_mute_home) {
AlertDialog.Builder builderInner = new MaterialAlertDialogBuilder(ProfileActivity.this);
builderInner.setMessage(account.acct);
builderInner.setNeutralButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
if (homeMuted) {
builderInner.setTitle(R.string.unmute_home);
builderInner.setPositiveButton(R.string.action_unmute, (dialog, which) -> accountsVM.unmuteHome(currentAccount, account)
.observe(ProfileActivity.this, account -> {
homeMuted = false;
invalidateOptionsMenu();
Toasty.info(ProfileActivity.this, getString(R.string.toast_unmute), Toasty.LENGTH_LONG).show();
}));
} else {
builderInner.setTitle(R.string.mute_home);
builderInner.setPositiveButton(R.string.action_mute, (dialog, which) -> accountsVM.muteHome(currentAccount, account)
.observe(ProfileActivity.this, account -> {
homeMuted = true;
invalidateOptionsMenu();
Toasty.info(ProfileActivity.this, getString(R.string.toast_mute), Toasty.LENGTH_LONG).show();
}));
}
builderInner.show();
} else if (itemId == R.id.action_timed_mute) {
MastodonHelper.scheduleBoost(ProfileActivity.this, MastodonHelper.ScheduleType.TIMED_MUTED, null, account, rs -> {
this.relationship = rs;
updateAccount();
});
return true;
} else if (itemId == R.id.action_report) { } else if (itemId == R.id.action_report) {
builderInner = new AlertDialog.Builder(ProfileActivity.this, Helper.dialogStyle()); AlertDialog.Builder builderInner = new MaterialAlertDialogBuilder(ProfileActivity.this);
builderInner.setTitle(R.string.report_account); builderInner.setTitle(R.string.report_account);
//Text for report //Text for report
EditText input = new EditText(ProfileActivity.this); EditText input = new EditText(ProfileActivity.this);
@ -926,7 +1150,7 @@ public class ProfileActivity extends BaseActivity {
builderInner.show(); builderInner.show();
return true; return true;
} else if (itemId == R.id.action_block) { } else if (itemId == R.id.action_block) {
builderInner = new AlertDialog.Builder(ProfileActivity.this, Helper.dialogStyle()); AlertDialog.Builder builderInner = new MaterialAlertDialogBuilder(ProfileActivity.this);
if (relationship != null) { if (relationship != null) {
if (relationship.blocking) { if (relationship.blocking) {
builderInner.setTitle(stringArrayConf[5]); builderInner.setTitle(stringArrayConf[5]);
@ -938,15 +1162,6 @@ public class ProfileActivity extends BaseActivity {
} else { } else {
doActionAccount = action.NOTHING; doActionAccount = action.NOTHING;
} }
} else if (itemId == R.id.action_block_instance) {
builderInner = new AlertDialog.Builder(ProfileActivity.this, Helper.dialogStyle());
doActionAccount = action.BLOCK_DOMAIN;
String domain = account.acct.split("@")[1];
builderInner.setMessage(getString(R.string.block_domain_confirm_message, domain));
} else {
return true;
}
if (doAction != action.NOTHING && builderInner != null) {
builderInner.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss()); builderInner.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
builderInner.setPositiveButton(R.string.yes, (dialog, which) -> { builderInner.setPositiveButton(R.string.yes, (dialog, which) -> {
String target; String target;
@ -956,20 +1171,6 @@ public class ProfileActivity extends BaseActivity {
target = account.id; target = account.id;
} }
switch (doActionAccount) { switch (doActionAccount) {
case MUTE:
accountsVM.mute(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, target, true, 0)
.observe(ProfileActivity.this, relationShip -> {
this.relationship = relationShip;
updateAccount();
});
break;
case UNMUTE:
accountsVM.unmute(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, target)
.observe(ProfileActivity.this, relationShip -> {
this.relationship = relationShip;
updateAccount();
});
break;
case BLOCK: case BLOCK:
accountsVM.block(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, target) accountsVM.block(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, target)
.observe(ProfileActivity.this, relationShip -> { .observe(ProfileActivity.this, relationShip -> {
@ -984,13 +1185,28 @@ public class ProfileActivity extends BaseActivity {
updateAccount(); updateAccount();
}); });
break; break;
case BLOCK_DOMAIN:
accountsVM.addDomainBlocks(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, target);
break;
} }
dialog.dismiss(); dialog.dismiss();
}); });
builderInner.show(); builderInner.show();
} else if (itemId == R.id.action_block_instance) {
AlertDialog.Builder builderInner = new MaterialAlertDialogBuilder(ProfileActivity.this);
String domain = account.acct.split("@")[1];
builderInner.setMessage(getString(R.string.block_domain_confirm_message, domain));
builderInner.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
builderInner.setPositiveButton(R.string.yes, (dialog, which) -> {
String target;
if (item.getItemId() == R.id.action_block_instance) {
target = account.acct.split("@")[1];
} else {
target = account.id;
}
accountsVM.addDomainBlocks(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, target);
dialog.dismiss();
});
builderInner.show();
} else {
return true;
} }
return true; return true;
} }

View file

@ -1,4 +1,4 @@
package app.fedilab.android.activities; package app.fedilab.android.mastodon.activities;
/* Copyright 2022 Thomas Schneider /* Copyright 2022 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -15,39 +15,44 @@ package app.fedilab.android.activities;
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import android.app.Dialog;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.os.Bundle; import android.os.Bundle;
import android.view.MenuItem; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.AdapterView; import android.view.Window;
import android.widget.ArrayAdapter;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.databinding.ActivityProxyBinding; import app.fedilab.android.databinding.ActivityProxyBinding;
public class ProxyActivity extends BaseActivity { public class ProxyActivity extends DialogFragment {
private ActivityProxyBinding binding; private ActivityProxyBinding binding;
private int position; private int position;
@NonNull
@Override @Override
protected void onCreate(Bundle savedInstanceState) { public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(ProxyActivity.this);
binding = ActivityProxyBinding.inflate(getLayoutInflater()); binding = ActivityProxyBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
getWindow().setLayout(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); MaterialAlertDialogBuilder materialAlertDialogBuilder = new MaterialAlertDialogBuilder(requireContext());
if (getSupportActionBar() != null) materialAlertDialogBuilder.setView(binding.getRoot());
getSupportActionBar().hide();
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(requireActivity());
//Enable proxy //Enable proxy
boolean enable_proxy = sharedpreferences.getBoolean(getString(R.string.SET_PROXY_ENABLED), false); boolean enable_proxy = sharedpreferences.getBoolean(getString(R.string.SET_PROXY_ENABLED), false);
binding.enableProxy.setChecked(enable_proxy); binding.enableProxy.setChecked(enable_proxy);
position = 0; position = sharedpreferences.getInt(getString(R.string.SET_PROXY_TYPE), 0);
String hostVal = sharedpreferences.getString(getString(R.string.SET_PROXY_HOST), "127.0.0.1"); String hostVal = sharedpreferences.getString(getString(R.string.SET_PROXY_HOST), "127.0.0.1");
int portVal = sharedpreferences.getInt(getString(R.string.SET_PROXY_PORT), 8118); int portVal = sharedpreferences.getInt(getString(R.string.SET_PROXY_PORT), 8118);
final String login = sharedpreferences.getString(getString(R.string.SET_PROXY_LOGIN), null); final String login = sharedpreferences.getString(getString(R.string.SET_PROXY_LOGIN), null);
@ -62,22 +67,17 @@ public class ProxyActivity extends BaseActivity {
if (pwd != null && binding.proxyPassword.length() > 0) { if (pwd != null && binding.proxyPassword.length() > 0) {
binding.proxyPassword.setText(pwd); binding.proxyPassword.setText(pwd);
} }
ArrayAdapter<CharSequence> adapterTrans = ArrayAdapter.createFromResource(ProxyActivity.this, if (position == 1) binding.protocol.check(R.id.protocol_socks);
R.array.proxy_type_choice, android.R.layout.simple_spinner_dropdown_item); binding.protocol.addOnButtonCheckedListener((group, checkedId, isChecked) -> {
binding.type.setAdapter(adapterTrans); if (isChecked) {
binding.type.setSelection(sharedpreferences.getInt(getString(R.string.SET_PROXY_TYPE), 0), false); if (checkedId == R.id.protocol_http)
binding.type.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { position = 0;
@Override else
public void onItemSelected(AdapterView<?> parent, View view, int p, long id) { position = 1;
position = p;
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
} }
}); });
binding.setProxySave.setOnClickListener(view -> {
materialAlertDialogBuilder.setPositiveButton(R.string.save, (dialog1, which) -> {
String hostVal1 = binding.host.getText().toString().trim(); String hostVal1 = binding.host.getText().toString().trim();
String portVal1 = binding.port.getText().toString().trim(); String portVal1 = binding.port.getText().toString().trim();
String proxy_loginVal = binding.proxyLogin.getText().toString().trim(); String proxy_loginVal = binding.proxyLogin.getText().toString().trim();
@ -91,18 +91,24 @@ public class ProxyActivity extends BaseActivity {
editor.putString(getString(R.string.SET_PROXY_LOGIN), proxy_loginVal); editor.putString(getString(R.string.SET_PROXY_LOGIN), proxy_loginVal);
editor.putString(getString(R.string.SET_PROXY_PASSWORD), proxy_passwordVal); editor.putString(getString(R.string.SET_PROXY_PASSWORD), proxy_passwordVal);
editor.apply(); editor.apply();
finish();
}); });
materialAlertDialogBuilder.setNeutralButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
Dialog dialog = materialAlertDialogBuilder.create();
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
return dialog;
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return super.onCreateView(inflater, container, savedInstanceState);
} }
@Override @Override
public boolean onOptionsItemSelected(MenuItem item) { public void onDestroyView() {
if (item.getItemId() == android.R.id.home) { super.onDestroyView();
finish(); binding = null;
return true;
} }
return super.onOptionsItemSelected(item);
}
} }

View file

@ -1,4 +1,4 @@
package app.fedilab.android.activities; package app.fedilab.android.mastodon.activities;
/* Copyright 2022 Thomas Schneider /* Copyright 2022 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -15,14 +15,9 @@ package app.fedilab.android.activities;
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import static app.fedilab.android.helper.PinnedTimelineHelper.sortMenuItem;
import static app.fedilab.android.helper.PinnedTimelineHelper.sortPositionAsc;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler;
import android.text.Editable; import android.text.Editable;
import android.text.TextWatcher; import android.text.TextWatcher;
import android.view.Menu; import android.view.Menu;
@ -35,7 +30,6 @@ import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import androidx.localbroadcastmanager.content.LocalBroadcastManager; import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
@ -43,31 +37,31 @@ import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.snackbar.Snackbar; import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.client.entities.app.BottomMenu; import app.fedilab.android.activities.MainActivity;
import app.fedilab.android.client.entities.app.InstanceSocial;
import app.fedilab.android.client.entities.app.Pinned;
import app.fedilab.android.client.entities.app.PinnedTimeline;
import app.fedilab.android.client.entities.app.RemoteInstance;
import app.fedilab.android.client.entities.app.Timeline;
import app.fedilab.android.databinding.ActivityReorderTabsBinding; import app.fedilab.android.databinding.ActivityReorderTabsBinding;
import app.fedilab.android.databinding.PopupSearchInstanceBinding; import app.fedilab.android.databinding.PopupSearchInstanceBinding;
import app.fedilab.android.exception.DBException; import app.fedilab.android.mastodon.client.entities.app.BottomMenu;
import app.fedilab.android.helper.Helper; import app.fedilab.android.mastodon.client.entities.app.InstanceSocial;
import app.fedilab.android.helper.ThemeHelper; import app.fedilab.android.mastodon.client.entities.app.Pinned;
import app.fedilab.android.helper.itemtouchhelper.OnStartDragListener; import app.fedilab.android.mastodon.client.entities.app.PinnedTimeline;
import app.fedilab.android.helper.itemtouchhelper.OnUndoListener; import app.fedilab.android.mastodon.client.entities.app.RemoteInstance;
import app.fedilab.android.helper.itemtouchhelper.SimpleItemTouchHelperCallback; import app.fedilab.android.mastodon.client.entities.app.Timeline;
import app.fedilab.android.ui.drawer.ReorderBottomMenuAdapter; import app.fedilab.android.mastodon.exception.DBException;
import app.fedilab.android.ui.drawer.ReorderTabAdapter; import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.viewmodel.mastodon.InstanceSocialVM; import app.fedilab.android.mastodon.helper.PinnedTimelineHelper;
import app.fedilab.android.viewmodel.mastodon.ReorderVM; import app.fedilab.android.mastodon.helper.itemtouchhelper.OnStartDragListener;
import app.fedilab.android.mastodon.helper.itemtouchhelper.SimpleItemTouchHelperCallback;
import app.fedilab.android.mastodon.ui.drawer.ReorderBottomMenuAdapter;
import app.fedilab.android.mastodon.ui.drawer.ReorderTabAdapter;
import app.fedilab.android.mastodon.viewmodel.mastodon.InstanceSocialVM;
import app.fedilab.android.mastodon.viewmodel.mastodon.ReorderVM;
import es.dmoral.toasty.Toasty; import es.dmoral.toasty.Toasty;
import okhttp3.Call; import okhttp3.Call;
import okhttp3.Callback; import okhttp3.Callback;
@ -78,7 +72,7 @@ import okhttp3.RequestBody;
import okhttp3.Response; import okhttp3.Response;
public class ReorderTimelinesActivity extends BaseActivity implements OnStartDragListener, OnUndoListener { public class ReorderTimelinesActivity extends BaseBarActivity implements OnStartDragListener {
private ItemTouchHelper touchHelper; private ItemTouchHelper touchHelper;
@ -104,13 +98,12 @@ public class ReorderTimelinesActivity extends BaseActivity implements OnStartDra
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
ThemeHelper.applyThemeBar(this);
binding = ActivityReorderTabsBinding.inflate(getLayoutInflater()); binding = ActivityReorderTabsBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); setContentView(binding.getRoot());
searchInstanceRunning = false; searchInstanceRunning = false;
if (getSupportActionBar() != null) { if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
} }
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(ReorderTimelinesActivity.this); SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(ReorderTimelinesActivity.this);
@ -131,8 +124,8 @@ public class ReorderTimelinesActivity extends BaseActivity implements OnStartDra
this.pinned.pinnedTimelines = new ArrayList<>(); this.pinned.pinnedTimelines = new ArrayList<>();
update = false; update = false;
} }
sortPositionAsc(this.pinned.pinnedTimelines); PinnedTimelineHelper.sortPositionAsc(this.pinned.pinnedTimelines);
reorderTabAdapter = new ReorderTabAdapter(this.pinned, ReorderTimelinesActivity.this, ReorderTimelinesActivity.this); reorderTabAdapter = new ReorderTabAdapter(this.pinned, ReorderTimelinesActivity.this);
ItemTouchHelper.Callback callback = ItemTouchHelper.Callback callback =
new SimpleItemTouchHelperCallback(reorderTabAdapter); new SimpleItemTouchHelperCallback(reorderTabAdapter);
touchHelper = new ItemTouchHelper(callback); touchHelper = new ItemTouchHelper(callback);
@ -147,7 +140,7 @@ public class ReorderTimelinesActivity extends BaseActivity implements OnStartDra
this.bottomMenu = new BottomMenu(getApplicationContext()).defaultBottomMenu(); this.bottomMenu = new BottomMenu(getApplicationContext()).defaultBottomMenu();
this.bottomMenu.bottom_menu = new ArrayList<>(); this.bottomMenu.bottom_menu = new ArrayList<>();
} }
sortMenuItem(this.bottomMenu.bottom_menu); PinnedTimelineHelper.sortMenuItem(this.bottomMenu.bottom_menu);
reorderBottomMenuAdapter = new ReorderBottomMenuAdapter(this.bottomMenu, ReorderTimelinesActivity.this); reorderBottomMenuAdapter = new ReorderBottomMenuAdapter(this.bottomMenu, ReorderTimelinesActivity.this);
ItemTouchHelper.Callback callback = ItemTouchHelper.Callback callback =
new SimpleItemTouchHelperCallback(reorderBottomMenuAdapter); new SimpleItemTouchHelperCallback(reorderBottomMenuAdapter);
@ -178,7 +171,7 @@ public class ReorderTimelinesActivity extends BaseActivity implements OnStartDra
} }
private void addInstance() { private void addInstance() {
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(ReorderTimelinesActivity.this, Helper.dialogStyle()); AlertDialog.Builder dialogBuilder = new MaterialAlertDialogBuilder(ReorderTimelinesActivity.this);
PopupSearchInstanceBinding popupSearchInstanceBinding = PopupSearchInstanceBinding.inflate(getLayoutInflater()); PopupSearchInstanceBinding popupSearchInstanceBinding = PopupSearchInstanceBinding.inflate(getLayoutInflater());
dialogBuilder.setView(popupSearchInstanceBinding.getRoot()); dialogBuilder.setView(popupSearchInstanceBinding.getRoot());
TextWatcher textWatcher = autoComplete(popupSearchInstanceBinding); TextWatcher textWatcher = autoComplete(popupSearchInstanceBinding);
@ -269,7 +262,10 @@ public class ReorderTimelinesActivity extends BaseActivity implements OnStartDra
pinnedTimeline.type = Timeline.TimeLineEnum.REMOTE; pinnedTimeline.type = Timeline.TimeLineEnum.REMOTE;
pinnedTimeline.position = pinned.pinnedTimelines.size(); pinnedTimeline.position = pinned.pinnedTimelines.size();
pinned.pinnedTimelines.add(pinnedTimeline); pinned.pinnedTimelines.add(pinnedTimeline);
if (pinned.user_id == null || pinned.instance == null) {
pinned.user_id = MainActivity.currentUserID;
pinned.instance = MainActivity.currentInstance;
}
if (update) { if (update) {
try { try {
new Pinned(ReorderTimelinesActivity.this).updatePinned(pinned); new Pinned(ReorderTimelinesActivity.this).updatePinned(pinned);
@ -393,51 +389,6 @@ public class ReorderTimelinesActivity extends BaseActivity implements OnStartDra
} }
@Override
public void onUndo(PinnedTimeline pinnedTimeline, int position) {
String text = "";
switch (pinnedTimeline.type) {
case TAG:
text = getString(R.string.reorder_tag_removed);
break;
case REMOTE:
text = getString(R.string.reorder_instance_removed);
break;
case LIST:
text = getString(R.string.reorder_list_deleted);
break;
}
Runnable runnable = () -> {
//change position of pinned that are after the removed item
for (int i = pinnedTimeline.position + 1; i < pinned.pinnedTimelines.size(); i++) {
pinned.pinnedTimelines.get(i).position -= 1;
}
pinned.pinnedTimelines.remove(pinnedTimeline);
reorderTabAdapter.notifyItemRemoved(position);
try {
new Pinned(ReorderTimelinesActivity.this).updatePinned(pinned);
changes = true;
} catch (DBException e) {
e.printStackTrace();
}
};
Handler handler = new Handler();
handler.postDelayed(runnable, 4000);
Snackbar.make(binding.getRoot(), text, 4000)
.setAction(getString(R.string.undo), view -> {
pinned.pinnedTimelines.add(position, pinnedTimeline);
reorderTabAdapter.notifyItemInserted(position);
handler.removeCallbacks(runnable);
})
.setTextColor(ThemeHelper.getAttColor(ReorderTimelinesActivity.this, R.attr.mTextColor))
.setActionTextColor(ContextCompat.getColor(ReorderTimelinesActivity.this, R.color.cyanea_accent_reference))
.setBackgroundTint(ContextCompat.getColor(ReorderTimelinesActivity.this, R.color.cyanea_primary_dark_reference))
.show();
}
@Override @Override
public void onStop() { public void onStop() {
super.onStop(); super.onStop();

View file

@ -1,4 +1,4 @@
package app.fedilab.android.activities; package app.fedilab.android.mastodon.activities;
/* Copyright 2022 Thomas Schneider /* Copyright 2022 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -14,7 +14,6 @@ package app.fedilab.android.activities;
* You should have received a copy of the GNU General Public License along with Fedilab; if not, * You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle; import android.os.Bundle;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
@ -22,7 +21,6 @@ import android.widget.RadioButton;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.widget.LinearLayoutCompat; import androidx.appcompat.widget.LinearLayoutCompat;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction; import androidx.fragment.app.FragmentTransaction;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
@ -33,20 +31,19 @@ import java.util.List;
import app.fedilab.android.BaseMainActivity; import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.client.entities.api.Account;
import app.fedilab.android.client.entities.api.RelationShip;
import app.fedilab.android.client.entities.api.Status;
import app.fedilab.android.client.entities.app.Timeline;
import app.fedilab.android.databinding.ActivityReportBinding; import app.fedilab.android.databinding.ActivityReportBinding;
import app.fedilab.android.helper.Helper; import app.fedilab.android.mastodon.client.entities.api.Account;
import app.fedilab.android.helper.ThemeHelper; import app.fedilab.android.mastodon.client.entities.api.RelationShip;
import app.fedilab.android.ui.drawer.RulesAdapter; import app.fedilab.android.mastodon.client.entities.api.Status;
import app.fedilab.android.ui.fragment.timeline.FragmentMastodonTimeline; import app.fedilab.android.mastodon.client.entities.app.Timeline;
import app.fedilab.android.viewmodel.mastodon.AccountsVM; import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.mastodon.ui.drawer.RulesAdapter;
import app.fedilab.android.mastodon.ui.fragment.timeline.FragmentMastodonTimeline;
import app.fedilab.android.mastodon.viewmodel.mastodon.AccountsVM;
import es.dmoral.toasty.Toasty; import es.dmoral.toasty.Toasty;
public class ReportActivity extends BaseActivity { public class ReportActivity extends BaseBarActivity {
private ActivityReportBinding binding; private ActivityReportBinding binding;
private Status status; private Status status;
@ -64,14 +61,13 @@ public class ReportActivity extends BaseActivity {
@Override @Override
protected void onCreate(@Nullable Bundle savedInstanceState) { protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
ThemeHelper.applyThemeBar(this);
binding = ActivityReportBinding.inflate(getLayoutInflater()); binding = ActivityReportBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); setContentView(binding.getRoot());
if (getSupportActionBar() != null) { if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
} }
Bundle b = getIntent().getExtras(); Bundle b = getIntent().getExtras();

View file

@ -1,4 +1,4 @@
package app.fedilab.android.activities; package app.fedilab.android.mastodon.activities;
/* Copyright 2022 Thomas Schneider /* Copyright 2022 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -17,23 +17,20 @@ package app.fedilab.android.activities;
import static app.fedilab.android.BaseMainActivity.currentAccount; import static app.fedilab.android.BaseMainActivity.currentAccount;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle; import android.os.Bundle;
import android.util.TypedValue; import android.util.TypedValue;
import android.view.MenuItem; import android.view.MenuItem;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.ActionBar;
import androidx.core.content.ContextCompat;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import com.google.android.material.tabs.TabLayout; import com.google.android.material.tabs.TabLayout;
import app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.databinding.ActivityScheduledBinding; import app.fedilab.android.databinding.ActivityScheduledBinding;
import app.fedilab.android.helper.MastodonHelper; import app.fedilab.android.mastodon.helper.MastodonHelper;
import app.fedilab.android.helper.ThemeHelper; import app.fedilab.android.mastodon.ui.pageadapter.FedilabScheduledPageAdapter;
import app.fedilab.android.ui.pageadapter.FedilabScheduledPageAdapter;
public class ScheduledActivity extends BaseActivity { public class ScheduledActivity extends BaseActivity {
@ -42,7 +39,7 @@ public class ScheduledActivity extends BaseActivity {
@Override @Override
protected void onCreate(@Nullable Bundle savedInstanceState) { protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
ThemeHelper.applyTheme(this);
binding = ActivityScheduledBinding.inflate(getLayoutInflater()); binding = ActivityScheduledBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); setContentView(binding.getRoot());
setSupportActionBar(binding.toolbar); setSupportActionBar(binding.toolbar);
@ -50,7 +47,6 @@ public class ScheduledActivity extends BaseActivity {
//Remove title //Remove title
if (actionBar != null) { if (actionBar != null) {
actionBar.setDisplayShowTitleEnabled(false); actionBar.setDisplayShowTitleEnabled(false);
actionBar.setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
} }
if (getSupportActionBar() != null) { if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
@ -68,8 +64,7 @@ public class ScheduledActivity extends BaseActivity {
binding.scheduleViewpager.setAdapter(new FedilabScheduledPageAdapter(getSupportFragmentManager())); binding.scheduleViewpager.setAdapter(new FedilabScheduledPageAdapter(getSupportFragmentManager()));
binding.scheduleViewpager.setOffscreenPageLimit(3); binding.scheduleViewpager.setOffscreenPageLimit(3);
binding.scheduleViewpager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(binding.scheduleTablayout)); binding.scheduleViewpager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(binding.scheduleTablayout));
binding.scheduleTablayout.setTabTextColors(ThemeHelper.getAttColor(ScheduledActivity.this, R.attr.mTextColor), ContextCompat.getColor(ScheduledActivity.this, R.color.cyanea_accent_dark_reference));
binding.scheduleTablayout.setTabIconTint(ThemeHelper.getColorStateList(ScheduledActivity.this));
binding.scheduleTablayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() { binding.scheduleTablayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override @Override

View file

@ -1,4 +1,4 @@
package app.fedilab.android.activities; package app.fedilab.android.mastodon.activities;
/* Copyright 2022 Thomas Schneider /* Copyright 2022 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -16,8 +16,9 @@ package app.fedilab.android.activities;
import android.app.SearchManager; import android.app.SearchManager;
import android.content.Context; import android.content.Context;
import android.graphics.drawable.ColorDrawable; import android.database.MatrixCursor;
import android.os.Bundle; import android.os.Bundle;
import android.provider.BaseColumns;
import android.view.Menu; import android.view.Menu;
import android.view.MenuInflater; import android.view.MenuInflater;
import android.view.MenuItem; import android.view.MenuItem;
@ -27,38 +28,54 @@ import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.appcompat.widget.SearchView; import androidx.appcompat.widget.SearchView;
import androidx.core.content.ContextCompat; import androidx.cursoradapter.widget.CursorAdapter;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentStatePagerAdapter; import androidx.fragment.app.FragmentStatePagerAdapter;
import androidx.lifecycle.ViewModelProvider;
import androidx.viewpager.widget.PagerAdapter; import androidx.viewpager.widget.PagerAdapter;
import androidx.viewpager.widget.ViewPager; import androidx.viewpager.widget.ViewPager;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.FutureTarget;
import com.bumptech.glide.request.target.Target;
import com.google.android.material.tabs.TabLayout; import com.google.android.material.tabs.TabLayout;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.util.concurrent.ExecutionException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.databinding.ActivitySearchResultTabsBinding; import app.fedilab.android.databinding.ActivitySearchResultTabsBinding;
import app.fedilab.android.helper.Helper; import app.fedilab.android.mastodon.client.entities.api.Account;
import app.fedilab.android.helper.ThemeHelper; import app.fedilab.android.mastodon.client.entities.api.Tag;
import app.fedilab.android.ui.fragment.timeline.FragmentMastodonAccount; import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.ui.fragment.timeline.FragmentMastodonTag; import app.fedilab.android.mastodon.ui.drawer.AccountsSearchTopBarAdapter;
import app.fedilab.android.ui.fragment.timeline.FragmentMastodonTimeline; import app.fedilab.android.mastodon.ui.drawer.TagSearchTopBarAdapter;
import app.fedilab.android.mastodon.ui.fragment.timeline.FragmentMastodonAccount;
import app.fedilab.android.mastodon.ui.fragment.timeline.FragmentMastodonTag;
import app.fedilab.android.mastodon.ui.fragment.timeline.FragmentMastodonTimeline;
import app.fedilab.android.mastodon.viewmodel.mastodon.AccountsVM;
import app.fedilab.android.mastodon.viewmodel.mastodon.SearchVM;
import es.dmoral.toasty.Toasty; import es.dmoral.toasty.Toasty;
public class SearchResultTabActivity extends BaseActivity { public class SearchResultTabActivity extends BaseBarActivity {
private String search; private String search;
private ActivitySearchResultTabsBinding binding; private ActivitySearchResultTabsBinding binding;
private TabLayout.Tab initial; private TabLayout.Tab initial;
public Boolean tagEmpty, accountEmpty;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
ThemeHelper.applyThemeBar(this);
binding = ActivitySearchResultTabsBinding.inflate(getLayoutInflater()); binding = ActivitySearchResultTabsBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); setContentView(binding.getRoot());
@ -75,7 +92,6 @@ public class SearchResultTabActivity extends BaseActivity {
if (getSupportActionBar() != null) { if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
} }
setTitle(search); setTitle(search);
initial = binding.searchTabLayout.newTab(); initial = binding.searchTabLayout.newTab();
@ -83,8 +99,6 @@ public class SearchResultTabActivity extends BaseActivity {
binding.searchTabLayout.addTab(binding.searchTabLayout.newTab().setText(getString(R.string.accounts))); binding.searchTabLayout.addTab(binding.searchTabLayout.newTab().setText(getString(R.string.accounts)));
binding.searchTabLayout.addTab(binding.searchTabLayout.newTab().setText(getString(R.string.toots))); binding.searchTabLayout.addTab(binding.searchTabLayout.newTab().setText(getString(R.string.toots)));
binding.searchTabLayout.addTab(binding.searchTabLayout.newTab().setText(getString(R.string.action_cache))); binding.searchTabLayout.addTab(binding.searchTabLayout.newTab().setText(getString(R.string.action_cache)));
binding.searchTabLayout.setTabTextColors(ThemeHelper.getAttColor(SearchResultTabActivity.this, R.attr.mTextColor), ContextCompat.getColor(SearchResultTabActivity.this, R.color.cyanea_accent_dark_reference));
binding.searchTabLayout.setTabIconTint(ThemeHelper.getColorStateList(SearchResultTabActivity.this));
binding.searchTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() { binding.searchTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override @Override
public void onTabSelected(TabLayout.Tab tab) { public void onTabSelected(TabLayout.Tab tab) {
@ -122,6 +136,9 @@ public class SearchResultTabActivity extends BaseActivity {
inflater.inflate(R.menu.menu_search, menu); inflater.inflate(R.menu.menu_search, menu);
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE); SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
SearchView searchView = (SearchView) menu.findItem(R.id.action_search).getActionView(); SearchView searchView = (SearchView) menu.findItem(R.id.action_search).getActionView();
if (search != null) {
searchView.setQuery(search, false);
}
searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName())); searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
searchView.setIconifiedByDefault(false); searchView.setIconifiedByDefault(false);
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@ -133,15 +150,85 @@ public class SearchResultTabActivity extends BaseActivity {
search = query.trim(); search = query.trim();
ScreenSlidePagerAdapter mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager()); ScreenSlidePagerAdapter mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager());
binding.searchViewpager.setAdapter(mPagerAdapter); binding.searchViewpager.setAdapter(mPagerAdapter);
searchView.clearFocus();
setTitle(search); setTitle(search);
searchView.setIconified(true); searchView.setIconified(true);
searchView.setQuery(search, false);
searchView.clearFocus();
binding.searchTabLayout.selectTab(initial); binding.searchTabLayout.selectTab(initial);
return false; return false;
} }
@Override @Override
public boolean onQueryTextChange(String newText) { public boolean onQueryTextChange(String newText) {
String pattern = "^(@[\\w_-]+@[a-z0-9.\\-]+|@[\\w_-]+)";
final Pattern mentionPattern = Pattern.compile(pattern);
String patternTag = "^#([\\w-]{2,})$";
final Pattern tagPattern = Pattern.compile(patternTag);
Matcher matcherMention, matcherTag;
matcherMention = mentionPattern.matcher(newText);
matcherTag = tagPattern.matcher(newText);
if (newText.trim().isEmpty()) {
searchView.setSuggestionsAdapter(null);
}
if (matcherMention.matches()) {
String[] from = new String[]{SearchManager.SUGGEST_COLUMN_ICON_1, SearchManager.SUGGEST_COLUMN_TEXT_1};
int[] to = new int[]{R.id.account_pp, R.id.account_un};
String searchGroup = matcherMention.group();
AccountsVM accountsVM = new ViewModelProvider(SearchResultTabActivity.this).get(AccountsVM.class);
MatrixCursor cursor = new MatrixCursor(new String[]{BaseColumns._ID,
SearchManager.SUGGEST_COLUMN_ICON_1,
SearchManager.SUGGEST_COLUMN_TEXT_1});
accountsVM.searchAccounts(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, searchGroup, 5, false, false)
.observe(SearchResultTabActivity.this, accounts -> {
if (accounts == null) {
return;
}
AccountsSearchTopBarAdapter cursorAdapter = new AccountsSearchTopBarAdapter(SearchResultTabActivity.this, accounts, R.layout.drawer_account_search, null, from, to, CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
searchView.setSuggestionsAdapter(cursorAdapter);
new Thread(() -> {
int i = 0;
for (Account account : accounts) {
FutureTarget<File> futureTarget = Glide
.with(SearchResultTabActivity.this.getApplicationContext())
.load(account.avatar_static)
.downloadOnly(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL);
File cacheFile;
try {
cacheFile = futureTarget.get();
cursor.addRow(new String[]{String.valueOf(i), cacheFile.getAbsolutePath(), "@" + account.acct});
i++;
} catch (ExecutionException | InterruptedException e) {
e.printStackTrace();
}
}
runOnUiThread(() -> cursorAdapter.changeCursor(cursor));
}).start();
});
} else if (matcherTag.matches()) {
SearchVM searchVM = new ViewModelProvider(SearchResultTabActivity.this).get(SearchVM.class);
String[] from = new String[]{SearchManager.SUGGEST_COLUMN_TEXT_1};
int[] to = new int[]{R.id.tag_name};
String searchGroup = matcherTag.group();
MatrixCursor cursor = new MatrixCursor(new String[]{BaseColumns._ID,
SearchManager.SUGGEST_COLUMN_TEXT_1});
searchVM.search(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, searchGroup, null,
"hashtags", false, true, false, 0,
null, null, 10).observe(SearchResultTabActivity.this,
results -> {
if (results == null || results.hashtags == null) {
return;
}
TagSearchTopBarAdapter cursorAdapter = new TagSearchTopBarAdapter(SearchResultTabActivity.this, results.hashtags, R.layout.drawer_tag_search, null, from, to, CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
searchView.setSuggestionsAdapter(cursorAdapter);
int i = 0;
for (Tag tag : results.hashtags) {
cursor.addRow(new String[]{String.valueOf(i), "#" + tag.name});
i++;
}
runOnUiThread(() -> cursorAdapter.changeCursor(cursor));
});
}
return false; return false;
} }
}); });
@ -190,6 +277,18 @@ public class SearchResultTabActivity extends BaseActivity {
} }
public void moveToAccount() {
tagEmpty = null;
accountEmpty = null;
binding.searchViewpager.setCurrentItem(1);
}
public void moveToMessage() {
tagEmpty = null;
accountEmpty = null;
binding.searchViewpager.setCurrentItem(2);
}
/** /**
* Pager adapter for the 4 fragments * Pager adapter for the 4 fragments
*/ */

View file

@ -0,0 +1,57 @@
package app.fedilab.android.mastodon.activities;
/* Copyright 2022 Thomas Schneider
*
* This file is a part of Fedilab
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */
import static androidx.navigation.ui.NavigationUI.setupActionBarWithNavController;
import android.os.Bundle;
import android.view.MenuItem;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import androidx.navigation.ui.AppBarConfiguration;
import app.fedilab.android.R;
import app.fedilab.android.databinding.ActivitySettingsBinding;
public class SettingsActivity extends BaseBarActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
app.fedilab.android.databinding.ActivitySettingsBinding binding = ActivitySettingsBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
NavController navController = Navigation.findNavController(this, R.id.fragment_container);
AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder().build();
setupActionBarWithNavController(this, navController, appBarConfiguration);
}
@Override
public boolean onSupportNavigateUp() {
NavController navController = Navigation.findNavController(this, R.id.fragment_container);
return navController.navigateUp() || super.onSupportNavigateUp();
}
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
NavController navController = Navigation.findNavController(this, R.id.fragment_container);
if (item.getItemId() == android.R.id.home && navController.getCurrentDestination() != null && navController.getCurrentDestination().getId() == R.id.FragmentSettingsCategories) {
finish();
}
return super.onOptionsItemSelected(item);
}
}

View file

@ -1,4 +1,4 @@
package app.fedilab.android.activities; package app.fedilab.android.mastodon.activities;
/* Copyright 2022 Thomas Schneider /* Copyright 2022 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -16,38 +16,35 @@ package app.fedilab.android.activities;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle; import android.os.Bundle;
import android.view.MenuItem; import android.view.MenuItem;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.activities.MainActivity;
import app.fedilab.android.databinding.ActivityStatusHistoryBinding; import app.fedilab.android.databinding.ActivityStatusHistoryBinding;
import app.fedilab.android.helper.Helper; import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.helper.ThemeHelper; import app.fedilab.android.mastodon.ui.drawer.StatusHistoryAdapter;
import app.fedilab.android.ui.drawer.StatusHistoryAdapter; import app.fedilab.android.mastodon.viewmodel.mastodon.StatusesVM;
import app.fedilab.android.viewmodel.mastodon.StatusesVM;
import es.dmoral.toasty.Toasty; import es.dmoral.toasty.Toasty;
public class StatusHistoryActivity extends BaseActivity { public class StatusHistoryActivity extends BaseBarActivity {
public static Resources.Theme theme; public static Resources.Theme theme;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
ThemeHelper.applyTheme(this);
ActivityStatusHistoryBinding binding = ActivityStatusHistoryBinding.inflate(getLayoutInflater()); ActivityStatusHistoryBinding binding = ActivityStatusHistoryBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); setContentView(binding.getRoot());
if (getSupportActionBar() != null) { if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
} }
Bundle b = getIntent().getExtras(); Bundle b = getIntent().getExtras();

View file

@ -1,4 +1,4 @@
package app.fedilab.android.activities; package app.fedilab.android.mastodon.activities;
/* Copyright 2021 Thomas Schneider /* Copyright 2021 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -15,13 +15,11 @@ package app.fedilab.android.activities;
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle; import android.os.Bundle;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
@ -31,14 +29,15 @@ import java.util.List;
import app.fedilab.android.BaseMainActivity; import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.client.entities.api.Account;
import app.fedilab.android.client.entities.api.Accounts;
import app.fedilab.android.client.entities.api.Status;
import app.fedilab.android.databinding.ActivityStatusInfoBinding; import app.fedilab.android.databinding.ActivityStatusInfoBinding;
import app.fedilab.android.helper.Helper; import app.fedilab.android.mastodon.client.entities.api.Account;
import app.fedilab.android.helper.ThemeHelper; import app.fedilab.android.mastodon.client.entities.api.Accounts;
import app.fedilab.android.ui.drawer.AccountAdapter; import app.fedilab.android.mastodon.client.entities.api.RelationShip;
import app.fedilab.android.viewmodel.mastodon.StatusesVM; import app.fedilab.android.mastodon.client.entities.api.Status;
import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.mastodon.ui.drawer.AccountAdapter;
import app.fedilab.android.mastodon.viewmodel.mastodon.AccountsVM;
import app.fedilab.android.mastodon.viewmodel.mastodon.StatusesVM;
public class StatusInfoActivity extends BaseActivity { public class StatusInfoActivity extends BaseActivity {
@ -54,14 +53,13 @@ public class StatusInfoActivity extends BaseActivity {
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
ThemeHelper.applyTheme(this);
binding = ActivityStatusInfoBinding.inflate(getLayoutInflater()); binding = ActivityStatusInfoBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); setContentView(binding.getRoot());
setSupportActionBar(binding.toolbar); setSupportActionBar(binding.toolbar);
if (getSupportActionBar() != null) { if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true); getSupportActionBar().setDisplayShowHomeEnabled(true);
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
} }
accountList = new ArrayList<>(); accountList = new ArrayList<>();
Bundle b = getIntent().getExtras(); Bundle b = getIntent().getExtras();
@ -115,6 +113,8 @@ public class StatusInfoActivity extends BaseActivity {
private void manageView(Accounts accounts) { private void manageView(Accounts accounts) {
binding.loadingNextAccounts.setVisibility(View.GONE); binding.loadingNextAccounts.setVisibility(View.GONE);
if (accountList != null && accounts != null && accounts.accounts != null) { if (accountList != null && accounts != null && accounts.accounts != null) {
int position = this.accountList.size();
fetchRelationShip(accounts.accounts, position);
int startId = 0; int startId = 0;
//There are some statuses present in the timeline //There are some statuses present in the timeline
if (accountList.size() > 0) { if (accountList.size() > 0) {
@ -127,6 +127,27 @@ public class StatusInfoActivity extends BaseActivity {
} }
} }
private void fetchRelationShip(List<Account> accounts, int position) {
List<String> ids = new ArrayList<>();
for (Account account : accounts) {
ids.add(account.id);
}
AccountsVM accountsVM = new ViewModelProvider(StatusInfoActivity.this).get(AccountsVM.class);
accountsVM.getRelationships(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, ids)
.observe(StatusInfoActivity.this, relationShips -> {
if (relationShips != null) {
for (RelationShip relationShip : relationShips) {
for (Account account : accounts) {
if (account.id.compareToIgnoreCase(relationShip.id) == 0) {
account.relationShip = relationShip;
}
}
}
accountAdapter.notifyItemRangeChanged(position, accounts.size());
}
});
}
@Override @Override
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) { if (item.getItemId() == android.R.id.home) {

View file

@ -0,0 +1,56 @@
package app.fedilab.android.mastodon.activities;
/* Copyright 2022 Thomas Schneider
*
* This file is a part of Fedilab
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */
import android.os.Bundle;
import android.view.MenuItem;
import org.jetbrains.annotations.NotNull;
import app.fedilab.android.R;
import app.fedilab.android.databinding.ActivitySuggestionsBinding;
import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.mastodon.ui.fragment.timeline.FragmentMastodonSuggestion;
public class SuggestionActivity extends BaseBarActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivitySuggestionsBinding binding = ActivitySuggestionsBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
Helper.addFragment(getSupportFragmentManager(), R.id.nav_host_fragment_suggestions, new FragmentMastodonSuggestion(), null, null, null);
}
@Override
public boolean onOptionsItemSelected(@NotNull MenuItem item) {
if (item.getItemId() == android.R.id.home) {
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
}

View file

@ -1,4 +1,4 @@
package app.fedilab.android.activities; package app.fedilab.android.mastodon.activities;
/* Copyright 2022 Thomas Schneider /* Copyright 2022 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -14,13 +14,11 @@ package app.fedilab.android.activities;
* You should have received a copy of the GNU General Public License along with Fedilab; if not, * You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle; import android.os.Bundle;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.ViewGroup; import android.view.ViewGroup;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentStatePagerAdapter; import androidx.fragment.app.FragmentStatePagerAdapter;
@ -32,15 +30,14 @@ import com.google.android.material.tabs.TabLayout;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.client.entities.app.Timeline;
import app.fedilab.android.databinding.ActivityTrendsBinding; import app.fedilab.android.databinding.ActivityTrendsBinding;
import app.fedilab.android.helper.Helper; import app.fedilab.android.mastodon.client.entities.app.Timeline;
import app.fedilab.android.helper.ThemeHelper; import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.ui.fragment.timeline.FragmentMastodonTag; import app.fedilab.android.mastodon.ui.fragment.timeline.FragmentMastodonTag;
import app.fedilab.android.ui.fragment.timeline.FragmentMastodonTimeline; import app.fedilab.android.mastodon.ui.fragment.timeline.FragmentMastodonTimeline;
public class TrendsActivity extends BaseActivity { public class TrendsActivity extends BaseBarActivity {
private ActivityTrendsBinding binding; private ActivityTrendsBinding binding;
@ -48,20 +45,17 @@ public class TrendsActivity extends BaseActivity {
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
ThemeHelper.applyThemeBar(this);
binding = ActivityTrendsBinding.inflate(getLayoutInflater()); binding = ActivityTrendsBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); setContentView(binding.getRoot());
if (getSupportActionBar() != null) { if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
} }
binding.searchTabLayout.addTab(binding.searchTabLayout.newTab().setText(getString(R.string.tags))); binding.searchTabLayout.addTab(binding.searchTabLayout.newTab().setText(getString(R.string.tags)));
binding.searchTabLayout.addTab(binding.searchTabLayout.newTab().setText(getString(R.string.toots))); binding.searchTabLayout.addTab(binding.searchTabLayout.newTab().setText(getString(R.string.toots)));
binding.searchTabLayout.setTabTextColors(ThemeHelper.getAttColor(TrendsActivity.this, R.attr.mTextColor), ContextCompat.getColor(TrendsActivity.this, R.color.cyanea_accent_dark_reference));
binding.searchTabLayout.setTabIconTint(ThemeHelper.getColorStateList(TrendsActivity.this));
binding.searchTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() { binding.searchTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override @Override
public void onTabSelected(TabLayout.Tab tab) { public void onTabSelected(TabLayout.Tab tab) {

View file

@ -1,4 +1,4 @@
package app.fedilab.android.activities; package app.fedilab.android.mastodon.activities.admin;
/* Copyright 2022 Thomas Schneider /* Copyright 2022 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -19,7 +19,6 @@ import android.content.ClipData;
import android.content.ClipboardManager; import android.content.ClipboardManager;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.os.Bundle; import android.os.Bundle;
import android.text.SpannableString; import android.text.SpannableString;
@ -47,7 +46,6 @@ import com.bumptech.glide.request.transition.Transition;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
@ -55,48 +53,51 @@ import java.util.concurrent.TimeUnit;
import app.fedilab.android.BaseMainActivity; import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.client.entities.api.Account; import app.fedilab.android.activities.MainActivity;
import app.fedilab.android.client.entities.api.AdminAccount;
import app.fedilab.android.client.entities.api.Attachment;
import app.fedilab.android.databinding.ActivityAdminAccountBinding; import app.fedilab.android.databinding.ActivityAdminAccountBinding;
import app.fedilab.android.helper.Helper; import app.fedilab.android.mastodon.activities.BaseActivity;
import app.fedilab.android.helper.MastodonHelper; import app.fedilab.android.mastodon.activities.InstanceProfileActivity;
import app.fedilab.android.helper.SpannableHelper; import app.fedilab.android.mastodon.activities.MediaActivity;
import app.fedilab.android.helper.ThemeHelper; import app.fedilab.android.mastodon.client.entities.api.Attachment;
import app.fedilab.android.viewmodel.mastodon.AdminVM; import app.fedilab.android.mastodon.client.entities.api.admin.AdminAccount;
import app.fedilab.android.viewmodel.mastodon.NodeInfoVM; import app.fedilab.android.mastodon.client.entities.api.admin.AdminIp;
import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.mastodon.helper.MastodonHelper;
import app.fedilab.android.mastodon.helper.SpannableHelper;
import app.fedilab.android.mastodon.helper.ThemeHelper;
import app.fedilab.android.mastodon.viewmodel.mastodon.AdminVM;
import app.fedilab.android.mastodon.viewmodel.mastodon.NodeInfoVM;
import es.dmoral.toasty.Toasty; import es.dmoral.toasty.Toasty;
public class AdminAccountActivity extends BaseActivity { public class AdminAccountActivity extends BaseActivity {
private AdminAccount adminAccount;
private Account account;
private ScheduledExecutorService scheduledExecutorService; private ScheduledExecutorService scheduledExecutorService;
private ActivityAdminAccountBinding binding; private ActivityAdminAccountBinding binding;
private String account_id;
private AdminVM adminVM;
private AdminAccount adminAccount;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
ThemeHelper.applyTheme(this);
binding = ActivityAdminAccountBinding.inflate(getLayoutInflater()); binding = ActivityAdminAccountBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); setContentView(binding.getRoot());
setSupportActionBar(binding.toolbar); setSupportActionBar(binding.toolbar);
ActionBar actionBar = getSupportActionBar(); ActionBar actionBar = getSupportActionBar();
Bundle b = getIntent().getExtras(); Bundle b = getIntent().getExtras();
adminAccount = null;
if (b != null) { if (b != null) {
adminAccount = (AdminAccount) b.getSerializable(Helper.ARG_ACCOUNT); adminAccount = (AdminAccount) b.getSerializable(Helper.ARG_ACCOUNT);
if (adminAccount != null) { account_id = b.getString(Helper.ARG_ACCOUNT_ID, null);
account = adminAccount.account;
}
} }
postponeEnterTransition(); postponeEnterTransition();
//Remove title //Remove title
if (actionBar != null) { if (actionBar != null) {
actionBar.setDisplayShowTitleEnabled(false); actionBar.setDisplayShowTitleEnabled(false);
actionBar.setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
} }
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(this); SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(this);
float scale = sharedpreferences.getFloat(getString(R.string.SET_FONT_SCALE), 1.1f); float scale = sharedpreferences.getFloat(getString(R.string.SET_FONT_SCALE), 1.1f);
@ -105,23 +106,87 @@ public class AdminAccountActivity extends BaseActivity {
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true); getSupportActionBar().setDisplayShowHomeEnabled(true);
} }
binding.toolbar.setPopupTheme(Helper.popupStyle()); adminVM = new ViewModelProvider(AdminAccountActivity.this).get(AdminVM.class);
if (account != null) { if (account_id != null) {
initializeView(account); adminVM.getAccount(MainActivity.currentInstance, MainActivity.currentToken, account_id).observe(this, this::initializeView);
return;
}
if (adminAccount != null && adminAccount.account != null) {
initializeView(adminAccount);
} else { } else {
Toasty.error(AdminAccountActivity.this, getString(R.string.toast_error_loading_account), Toast.LENGTH_LONG).show(); Toasty.error(AdminAccountActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show();
finish(); finish();
} }
binding.disableAction.setOnClickListener(v -> {
if (adminAccount.disabled) {
adminVM.enable(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, adminAccount.id)
.observe(AdminAccountActivity.this, adminAccountResult -> {
adminAccount.disabled = false;
binding.disableAction.setText(R.string.disable);
binding.disabled.setText(R.string.no);
});
} else {
adminVM.performAction(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, adminAccount.id, "disable ", null, null, null, null);
adminAccount.disabled = true;
binding.disableAction.setText(R.string.undisable);
binding.disabled.setText(R.string.yes);
}
});
binding.approveAction.setOnClickListener(v -> {
if (adminAccount.approved) {
adminVM.reject(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, adminAccount.id)
.observe(AdminAccountActivity.this, adminAccountResult -> {
adminAccount = adminAccountResult;
initializeView(adminAccount);
});
} else {
adminVM.approve(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, adminAccount.id);
adminAccount.approved = true;
initializeView(adminAccount);
}
});
binding.silenceAction.setOnClickListener(v -> {
if (adminAccount.silenced) {
adminVM.unsilence(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, adminAccount.id)
.observe(AdminAccountActivity.this, adminAccountResult -> {
adminAccount = adminAccountResult;
initializeView(adminAccount);
});
} else {
adminVM.performAction(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, adminAccount.id, "silence", null, null, null, null);
adminAccount.silenced = true;
initializeView(adminAccount);
}
});
binding.suspendAction.setOnClickListener(v -> {
if (adminAccount.suspended) {
adminVM.unsuspend(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, adminAccount.id)
.observe(AdminAccountActivity.this, adminAccountResult -> {
adminAccount = adminAccountResult;
initializeView(adminAccount);
});
} else {
adminVM.performAction(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, adminAccount.id, "suspend", null, null, null, null);
adminAccount.suspended = true;
initializeView(adminAccount);
}
});
} }
private void initializeView(Account account) { private void initializeView(AdminAccount adminAccount) {
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(AdminAccountActivity.this); SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(AdminAccountActivity.this);
if (account == null) { if (adminAccount == null) {
Toasty.error(AdminAccountActivity.this, getString(R.string.toast_error_loading_account), Toast.LENGTH_LONG).show(); Toasty.error(AdminAccountActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show();
finish(); finish();
return; return;
} }
binding.title.setText(String.format(Locale.getDefault(), "@%s", account.acct)); binding.title.setText(String.format(Locale.getDefault(), "@%s", adminAccount.account.acct));
// MastodonHelper.loadPPMastodon(binding.profilePicture, account); // MastodonHelper.loadPPMastodon(binding.profilePicture, account);
binding.appBar.addOnOffsetChangedListener((appBarLayout, verticalOffset) -> { binding.appBar.addOnOffsetChangedListener((appBarLayout, verticalOffset) -> {
@ -135,14 +200,18 @@ public class AdminAccountActivity extends BaseActivity {
} }
}); });
binding.username.setText(String.format(Locale.getDefault(), "@%s", adminAccount.username)); binding.username.setText(String.format(Locale.getDefault(), "@%s", adminAccount.username));
binding.domain.setText(adminAccount.domain); binding.domain.setText(adminAccount.domain);
binding.email.setText(adminAccount.email); binding.email.setText(adminAccount.email);
StringBuilder lastActive = new StringBuilder(); StringBuilder lastActive = new StringBuilder();
if (adminAccount.ips != null) { if (adminAccount.ips != null) {
for (AdminAccount.IP ip : adminAccount.ips) { int count = 0;
for (AdminIp ip : adminAccount.ips) {
lastActive.append(Helper.shortDateToString(ip.used_at)).append(" - ").append(ip.ip).append("\r\n"); lastActive.append(Helper.shortDateToString(ip.used_at)).append(" - ").append(ip.ip).append("\r\n");
count++;
if (count > 4) {
break;
}
} }
} }
if (lastActive.toString().trim().length() == 0) { if (lastActive.toString().trim().length() == 0) {
@ -162,78 +231,9 @@ public class AdminAccountActivity extends BaseActivity {
binding.silenceAction.setText(adminAccount.silenced ? R.string.unsilence : R.string.silence); binding.silenceAction.setText(adminAccount.silenced ? R.string.unsilence : R.string.silence);
binding.suspendAction.setText(adminAccount.suspended ? R.string.unsuspend : R.string.suspend); binding.suspendAction.setText(adminAccount.suspended ? R.string.unsuspend : R.string.suspend);
AdminVM adminVM = new ViewModelProvider(AdminAccountActivity.this).get(AdminVM.class);
binding.disableAction.setOnClickListener(v -> {
if (adminAccount.disabled) {
adminVM.enable(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id)
.observe(AdminAccountActivity.this, adminAccountResult -> {
adminAccount.disabled = false;
binding.disableAction.setText(R.string.disable);
binding.disabled.setText(R.string.no);
});
} else {
adminVM.performAction(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id, "disable ", null, null, null, null);
adminAccount.disabled = true;
binding.disableAction.setText(R.string.undisable);
binding.disabled.setText(R.string.yes);
}
});
binding.approveAction.setOnClickListener(v -> {
if (adminAccount.approved) {
adminVM.reject(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id)
.observe(AdminAccountActivity.this, adminAccountResult -> {
adminAccount.approved = false;
binding.approveAction.setText(R.string.approve);
binding.approved.setText(R.string.no);
});
} else {
adminVM.approve(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id);
adminAccount.approved = true;
binding.approveAction.setText(R.string.reject);
binding.approved.setText(R.string.yes);
}
});
binding.silenceAction.setOnClickListener(v -> {
if (adminAccount.disabled) {
adminVM.unsilence(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id)
.observe(AdminAccountActivity.this, adminAccountResult -> {
adminAccount.silenced = false;
binding.silenceAction.setText(R.string.silence);
binding.disabled.setText(R.string.no);
});
} else {
adminVM.performAction(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id, "silence", null, null, null, null);
adminAccount.silenced = true;
binding.disableAction.setText(R.string.unsilence);
binding.disabled.setText(R.string.yes);
}
});
binding.suspendAction.setOnClickListener(v -> {
if (adminAccount.disabled) {
adminVM.unsuspend(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id)
.observe(AdminAccountActivity.this, adminAccountResult -> {
adminAccount.suspended = false;
binding.suspendAction.setText(R.string.suspend);
binding.suspended.setText(R.string.no);
});
} else {
adminVM.performAction(BaseMainActivity.currentInstance, BaseMainActivity.currentToken, account.id, "suspend", null, null, null, null);
adminAccount.suspended = true;
binding.disableAction.setText(R.string.unsuspend);
binding.suspended.setText(R.string.yes);
}
});
//Retrieve relationship with the connected account
List<String> accountListToCheck = new ArrayList<>();
accountListToCheck.add(account.id);
//Animate emojis //Animate emojis
if (account.emojis != null && account.emojis.size() > 0) { if (adminAccount.account.emojis != null && adminAccount.account.emojis.size() > 0) {
boolean disableAnimatedEmoji = sharedpreferences.getBoolean(getString(R.string.SET_DISABLE_ANIMATED_EMOJI), false); boolean disableAnimatedEmoji = sharedpreferences.getBoolean(getString(R.string.SET_DISABLE_ANIMATED_EMOJI), false);
if (!disableAnimatedEmoji) { if (!disableAnimatedEmoji) {
scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(); scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
@ -244,7 +244,7 @@ public class AdminAccountActivity extends BaseActivity {
//Tablayout for timelines/following/followers //Tablayout for timelines/following/followers
boolean disableGif = sharedpreferences.getBoolean(getString(R.string.SET_DISABLE_GIF), false); boolean disableGif = sharedpreferences.getBoolean(getString(R.string.SET_DISABLE_GIF), false);
String targetedUrl = disableGif ? account.avatar_static : account.avatar; String targetedUrl = disableGif ? adminAccount.account.avatar_static : adminAccount.account.avatar;
Glide.with(AdminAccountActivity.this) Glide.with(AdminAccountActivity.this)
.asDrawable() .asDrawable()
.dontTransform() .dontTransform()
@ -269,10 +269,10 @@ public class AdminAccountActivity extends BaseActivity {
} }
); );
//Load header //Load header
MastodonHelper.loadProfileMediaMastodon(binding.bannerPp, account, MastodonHelper.MediaAccountType.HEADER); MastodonHelper.loadProfileMediaMastodon(AdminAccountActivity.this, binding.bannerPp, adminAccount.account, MastodonHelper.MediaAccountType.HEADER);
//Redraws icon for locked accounts //Redraws icon for locked accounts
final float scale = getResources().getDisplayMetrics().density; final float scale = getResources().getDisplayMetrics().density;
if (account.locked) { if (adminAccount.account.locked) {
Drawable img = ContextCompat.getDrawable(AdminAccountActivity.this, R.drawable.ic_baseline_lock_24); Drawable img = ContextCompat.getDrawable(AdminAccountActivity.this, R.drawable.ic_baseline_lock_24);
assert img != null; assert img != null;
img.setBounds(0, 0, (int) (16 * scale + 0.5f), (int) (16 * scale + 0.5f)); img.setBounds(0, 0, (int) (16 * scale + 0.5f), (int) (16 * scale + 0.5f));
@ -282,41 +282,41 @@ public class AdminAccountActivity extends BaseActivity {
} }
//Peertube account watched by a Mastodon account //Peertube account watched by a Mastodon account
//Bot account //Bot account
if (account.bot) { if (adminAccount.account.bot) {
binding.accountBot.setVisibility(View.VISIBLE); binding.accountBot.setVisibility(View.VISIBLE);
} }
if (account.acct != null) { if (adminAccount.account.acct != null) {
setTitle(account.acct); setTitle(adminAccount.account.acct);
} }
final SpannableString content = new SpannableString(getString(R.string.disclaimer_full)); final SpannableString content = new SpannableString(getString(R.string.disclaimer_full));
content.setSpan(new UnderlineSpan(), 0, content.length(), 0); content.setSpan(new UnderlineSpan(), 0, content.length(), 0);
content.setSpan(new ForegroundColorSpan(ContextCompat.getColor(AdminAccountActivity.this, R.color.cyanea_accent_reference)), 0, content.length(), content.setSpan(new ForegroundColorSpan(ThemeHelper.getAttColor(this, R.attr.colorPrimary)), 0, content.length(),
Spanned.SPAN_INCLUSIVE_EXCLUSIVE); Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
//This account was moved to another one //This account was moved to another one
if (account.moved != null) { if (adminAccount.account.moved != null) {
binding.accountMoved.setVisibility(View.VISIBLE); binding.accountMoved.setVisibility(View.VISIBLE);
Drawable imgTravel = ContextCompat.getDrawable(AdminAccountActivity.this, R.drawable.ic_baseline_card_travel_24); Drawable imgTravel = ContextCompat.getDrawable(AdminAccountActivity.this, R.drawable.ic_baseline_card_travel_24);
assert imgTravel != null; assert imgTravel != null;
imgTravel.setBounds(0, 0, (int) (20 * scale + 0.5f), (int) (20 * scale + 0.5f)); imgTravel.setBounds(0, 0, (int) (20 * scale + 0.5f), (int) (20 * scale + 0.5f));
binding.accountMoved.setCompoundDrawables(imgTravel, null, null, null); binding.accountMoved.setCompoundDrawables(imgTravel, null, null, null);
//Retrieves content and make account names clickable //Retrieves content and make account names clickable
SpannableString spannableString = SpannableHelper.moveToText(AdminAccountActivity.this, account); SpannableString spannableString = SpannableHelper.moveToText(AdminAccountActivity.this, adminAccount.account);
binding.accountMoved.setText(spannableString, TextView.BufferType.SPANNABLE); binding.accountMoved.setText(spannableString, TextView.BufferType.SPANNABLE);
binding.accountMoved.setMovementMethod(LinkMovementMethod.getInstance()); binding.accountMoved.setMovementMethod(LinkMovementMethod.getInstance());
} }
binding.accountDn.setText( binding.accountDn.setText(
account.getSpanDisplayName(AdminAccountActivity.this, adminAccount.account.getSpanDisplayName(AdminAccountActivity.this,
new WeakReference<>(binding.accountDn)), new WeakReference<>(binding.accountDn)),
TextView.BufferType.SPANNABLE); TextView.BufferType.SPANNABLE);
binding.accountUn.setText(String.format("@%s", account.acct)); binding.accountUn.setText(String.format("@%s", adminAccount.account.acct));
binding.accountUn.setOnLongClickListener(v -> { binding.accountUn.setOnLongClickListener(v -> {
ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
String account_id = account.acct; String account_id = adminAccount.account.acct;
if (account_id.split("@").length == 1) if (account_id.split("@").length == 1)
account_id += "@" + BaseMainActivity.currentInstance; account_id += "@" + BaseMainActivity.currentInstance;
ClipData clip = ClipData.newPlainText("mastodon_account_id", "@" + account_id); ClipData clip = ClipData.newPlainText("mastodon_account_id", "@" + account_id);
@ -326,15 +326,15 @@ public class AdminAccountActivity extends BaseActivity {
return false; return false;
}); });
MastodonHelper.loadPPMastodon(binding.accountPp, account); MastodonHelper.loadPPMastodon(binding.accountPp, adminAccount.account);
binding.accountPp.setOnClickListener(v -> { binding.accountPp.setOnClickListener(v -> {
Intent intent = new Intent(AdminAccountActivity.this, MediaActivity.class); Intent intent = new Intent(AdminAccountActivity.this, MediaActivity.class);
Bundle b = new Bundle(); Bundle b = new Bundle();
Attachment attachment = new Attachment(); Attachment attachment = new Attachment();
attachment.description = account.acct; attachment.description = adminAccount.account.acct;
attachment.preview_url = account.avatar; attachment.preview_url = adminAccount.account.avatar;
attachment.url = account.avatar; attachment.url = adminAccount.account.avatar;
attachment.remote_url = account.avatar; attachment.remote_url = adminAccount.account.avatar;
attachment.type = "image"; attachment.type = "image";
ArrayList<Attachment> attachments = new ArrayList<>(); ArrayList<Attachment> attachments = new ArrayList<>();
attachments.add(attachment); attachments.add(attachment);
@ -348,10 +348,10 @@ public class AdminAccountActivity extends BaseActivity {
}); });
binding.accountDate.setText(Helper.shortDateToString(account.created_at)); binding.accountDate.setText(Helper.shortDateToString(adminAccount.created_at));
binding.accountDate.setVisibility(View.VISIBLE); binding.accountDate.setVisibility(View.VISIBLE);
String[] accountInstanceArray = account.acct.split("@"); String[] accountInstanceArray = adminAccount.account.acct.split("@");
String accountInstance = BaseMainActivity.currentInstance; String accountInstance = BaseMainActivity.currentInstance;
if (accountInstanceArray.length > 1) { if (accountInstanceArray.length > 1) {
accountInstance = accountInstanceArray[1]; accountInstance = accountInstanceArray[1];
@ -365,12 +365,11 @@ public class AdminAccountActivity extends BaseActivity {
binding.instanceInfo.setVisibility(View.VISIBLE); binding.instanceInfo.setVisibility(View.VISIBLE);
binding.instanceInfo.setOnClickListener(v -> { binding.instanceInfo.setOnClickListener(v -> {
Intent intent = new Intent(AdminAccountActivity.this, InstanceProfileActivity.class); InstanceProfileActivity instanceProfileActivity = new InstanceProfileActivity();
Bundle b = new Bundle(); Bundle b = new Bundle();
b.putString(Helper.ARG_INSTANCE, finalAccountInstance); b.putString(Helper.ARG_INSTANCE, finalAccountInstance);
intent.putExtras(b); instanceProfileActivity.setArguments(b);
startActivity(intent); instanceProfileActivity.show(getSupportFragmentManager(), null);
}); });
} }
}); });

View file

@ -1,4 +1,4 @@
package app.fedilab.android.activities; package app.fedilab.android.mastodon.activities.admin;
/* Copyright 2022 Thomas Schneider /* Copyright 2022 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -14,9 +14,14 @@ package app.fedilab.android.activities;
* You should have received a copy of the GNU General Public License along with Fedilab; if not, * You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import static app.fedilab.android.activities.AdminActionActivity.AdminEnum.REPORT; import static app.fedilab.android.mastodon.activities.admin.AdminActionActivity.AdminEnum.ACCOUNT;
import static app.fedilab.android.mastodon.activities.admin.AdminActionActivity.AdminEnum.DOMAIN;
import static app.fedilab.android.mastodon.activities.admin.AdminActionActivity.AdminEnum.REPORT;
import android.graphics.drawable.ColorDrawable; import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle; import android.os.Bundle;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
@ -24,50 +29,74 @@ import android.view.MenuItem;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction; import androidx.fragment.app.FragmentTransaction;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.gson.annotations.SerializedName; import com.google.gson.annotations.SerializedName;
import app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.databinding.ActivityAdminActionsBinding; import app.fedilab.android.databinding.ActivityAdminActionsBinding;
import app.fedilab.android.databinding.PopupAdminFilterAccountsBinding; import app.fedilab.android.databinding.PopupAdminFilterAccountsBinding;
import app.fedilab.android.databinding.PopupAdminFilterReportsBinding; import app.fedilab.android.databinding.PopupAdminFilterReportsBinding;
import app.fedilab.android.helper.Helper; import app.fedilab.android.mastodon.activities.BaseBarActivity;
import app.fedilab.android.helper.ThemeHelper; import app.fedilab.android.mastodon.client.entities.api.admin.AdminDomainBlock;
import app.fedilab.android.ui.fragment.admin.FragmentAdminAccount; import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.ui.fragment.admin.FragmentAdminReport; import app.fedilab.android.mastodon.helper.ThemeHelper;
import app.fedilab.android.mastodon.ui.fragment.admin.FragmentAdminAccount;
import app.fedilab.android.mastodon.ui.fragment.admin.FragmentAdminDomain;
import app.fedilab.android.mastodon.ui.fragment.admin.FragmentAdminReport;
public class AdminActionActivity extends BaseActivity { public class AdminActionActivity extends BaseBarActivity {
public static Boolean local = true, remote = true, active = true, pending = true, disabled = true, silenced = true, suspended = true, staff = null, orderByMostRecent = true; public static Boolean local = true, remote = true, active = true, pending = true, disabled = true, silenced = true, suspended = true, staff = null, orderByMostRecent = true;
public static Boolean resolved = false, reportLocal = true, reportRemote = true; public static Boolean resolved = null, reportLocal = true, reportRemote = true;
private ActivityAdminActionsBinding binding; private ActivityAdminActionsBinding binding;
private boolean canGoBack; private boolean canGoBack;
private FragmentAdminReport fragmentAdminReport; private FragmentAdminReport fragmentAdminReport;
private FragmentAdminAccount fragmentAdminAccount; private FragmentAdminAccount fragmentAdminAccount;
private FragmentAdminDomain fragmentAdminDomain;
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Bundle b = intent.getExtras();
if (b != null) {
AdminDomainBlock adminDomainBlock = (AdminDomainBlock) b.getSerializable(Helper.ARG_ADMIN_DOMAINBLOCK);
AdminDomainBlock adminDomainBlockDelete = (AdminDomainBlock) b.getSerializable(Helper.ARG_ADMIN_DOMAINBLOCK_DELETE);
if (adminDomainBlock != null && adminDomainBlock.domain != null && fragmentAdminDomain != null) {
fragmentAdminDomain.update(adminDomainBlock);
}
if (adminDomainBlockDelete != null && fragmentAdminDomain != null) {
fragmentAdminDomain.delete(adminDomainBlockDelete);
}
}
}
};
@Override @Override
protected void onCreate(@Nullable Bundle savedInstanceState) { protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
ThemeHelper.applyThemeBar(this);
binding = ActivityAdminActionsBinding.inflate(getLayoutInflater()); binding = ActivityAdminActionsBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); setContentView(binding.getRoot());
LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, new IntentFilter(Helper.BROADCAST_DATA));
if (getSupportActionBar() != null) { if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
} }
canGoBack = false; canGoBack = false;
binding.reports.setOnClickListener(v -> displayTimeline(REPORT)); binding.reports.setOnClickListener(v -> displayTimeline(REPORT));
binding.accounts.setOnClickListener(v -> displayTimeline(AdminEnum.ACCOUNT)); binding.accounts.setOnClickListener(v -> displayTimeline(ACCOUNT));
binding.domains.setOnClickListener(v -> displayTimeline(DOMAIN));
} }
private void displayTimeline(AdminEnum type) { private void displayTimeline(AdminEnum type) {
canGoBack = true; canGoBack = true;
if (type == REPORT) { if (type == REPORT) {
ThemeHelper.slideViewsToLeft(binding.buttonContainer, binding.fragmentContainer, () -> { ThemeHelper.slideViewsToLeft(binding.buttonContainer, binding.fragmentContainer, () -> {
fragmentAdminReport = new FragmentAdminReport(); fragmentAdminReport = new FragmentAdminReport();
Bundle bundle = new Bundle(); Bundle bundle = new Bundle();
@ -80,9 +109,7 @@ public class AdminActionActivity extends BaseActivity {
fragmentTransaction.replace(R.id.fragment_container, fragmentAdminReport); fragmentTransaction.replace(R.id.fragment_container, fragmentAdminReport);
fragmentTransaction.commit(); fragmentTransaction.commit();
}); });
} else if (type == ACCOUNT) {
} else {
ThemeHelper.slideViewsToLeft(binding.buttonContainer, binding.fragmentContainer, () -> { ThemeHelper.slideViewsToLeft(binding.buttonContainer, binding.fragmentContainer, () -> {
fragmentAdminAccount = new FragmentAdminAccount(); fragmentAdminAccount = new FragmentAdminAccount();
Bundle bundle = new Bundle(); Bundle bundle = new Bundle();
@ -95,7 +122,19 @@ public class AdminActionActivity extends BaseActivity {
fragmentTransaction.replace(R.id.fragment_container, fragmentAdminAccount); fragmentTransaction.replace(R.id.fragment_container, fragmentAdminAccount);
fragmentTransaction.commit(); fragmentTransaction.commit();
}); });
} else if (type == DOMAIN) {
ThemeHelper.slideViewsToLeft(binding.buttonContainer, binding.fragmentContainer, () -> {
fragmentAdminDomain = new FragmentAdminDomain();
Bundle bundle = new Bundle();
bundle.putSerializable(Helper.ARG_TIMELINE_TYPE, type);
bundle.putString(Helper.ARG_VIEW_MODEL_KEY, "FEDILAB_" + type.getValue());
fragmentAdminDomain.setArguments(bundle);
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction =
fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.fragment_container, fragmentAdminDomain);
fragmentTransaction.commit();
});
} }
switch (type) { switch (type) {
case REPORT: case REPORT:
@ -104,13 +143,16 @@ public class AdminActionActivity extends BaseActivity {
case ACCOUNT: case ACCOUNT:
setTitle(R.string.accounts); setTitle(R.string.accounts);
break; break;
case DOMAIN:
setTitle(R.string.domains);
break;
} }
invalidateOptionsMenu(); invalidateOptionsMenu();
} }
@Override @Override
public boolean onCreateOptionsMenu(@NonNull Menu menu) { public boolean onCreateOptionsMenu(@NonNull Menu menu) {
if (canGoBack) { if (canGoBack && fragmentAdminAccount != null) {
getMenuInflater().inflate(R.menu.menu_admin_account, menu); getMenuInflater().inflate(R.menu.menu_admin_account, menu);
} }
return super.onCreateOptionsMenu(menu); return super.onCreateOptionsMenu(menu);
@ -123,7 +165,7 @@ public class AdminActionActivity extends BaseActivity {
return true; return true;
} else if (item.getItemId() == R.id.action_filter) { } else if (item.getItemId() == R.id.action_filter) {
if (getTitle().toString().equalsIgnoreCase(getString(R.string.accounts))) { if (getTitle().toString().equalsIgnoreCase(getString(R.string.accounts))) {
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(AdminActionActivity.this, Helper.dialogStyle()); AlertDialog.Builder alertDialogBuilder = new MaterialAlertDialogBuilder(AdminActionActivity.this);
PopupAdminFilterAccountsBinding binding = PopupAdminFilterAccountsBinding.inflate(getLayoutInflater()); PopupAdminFilterAccountsBinding binding = PopupAdminFilterAccountsBinding.inflate(getLayoutInflater());
alertDialogBuilder.setView(binding.getRoot()); alertDialogBuilder.setView(binding.getRoot());
if (local != null && remote == null) { if (local != null && remote == null) {
@ -155,6 +197,8 @@ public class AdminActionActivity extends BaseActivity {
binding.moderationAll.setChecked(true); binding.moderationAll.setChecked(true);
} }
binding.moderation.setOnCheckedChangeListener((group, checkedId) -> { binding.moderation.setOnCheckedChangeListener((group, checkedId) -> {
disabled = null;
silenced = null;
if (checkedId == R.id.moderation_all) { if (checkedId == R.id.moderation_all) {
active = true; active = true;
suspended = true; suspended = true;
@ -215,7 +259,7 @@ public class AdminActionActivity extends BaseActivity {
AlertDialog alert = alertDialogBuilder.create(); AlertDialog alert = alertDialogBuilder.create();
alert.show(); alert.show();
} else { } else {
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(AdminActionActivity.this, Helper.dialogStyle()); AlertDialog.Builder alertDialogBuilder = new MaterialAlertDialogBuilder(AdminActionActivity.this);
PopupAdminFilterReportsBinding binding = PopupAdminFilterReportsBinding.inflate(getLayoutInflater()); PopupAdminFilterReportsBinding binding = PopupAdminFilterReportsBinding.inflate(getLayoutInflater());
alertDialogBuilder.setView(binding.getRoot()); alertDialogBuilder.setView(binding.getRoot());
if (resolved == null) { if (resolved == null) {
@ -227,7 +271,7 @@ public class AdminActionActivity extends BaseActivity {
if (checkedId == R.id.status_resolved) { if (checkedId == R.id.status_resolved) {
resolved = true; resolved = true;
} else if (checkedId == R.id.status_unresolved) { } else if (checkedId == R.id.status_unresolved) {
resolved = false; resolved = null;
} }
}); });
if (reportLocal != null && reportRemote == null) { if (reportLocal != null && reportRemote == null) {
@ -270,6 +314,14 @@ public class AdminActionActivity extends BaseActivity {
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }
@Override
protected void onDestroy() {
super.onDestroy();
if (mReceiver != null) {
LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver);
}
}
@Override @Override
public void onBackPressed() { public void onBackPressed() {
if (canGoBack) { if (canGoBack) {
@ -277,9 +329,15 @@ public class AdminActionActivity extends BaseActivity {
ThemeHelper.slideViewsToRight(binding.fragmentContainer, binding.buttonContainer, () -> { ThemeHelper.slideViewsToRight(binding.fragmentContainer, binding.buttonContainer, () -> {
if (fragmentAdminReport != null) { if (fragmentAdminReport != null) {
fragmentAdminReport.onDestroyView(); fragmentAdminReport.onDestroyView();
fragmentAdminReport = null;
} }
if (fragmentAdminAccount != null) { if (fragmentAdminAccount != null) {
fragmentAdminAccount.onDestroyView(); fragmentAdminAccount.onDestroyView();
fragmentAdminAccount = null;
}
if (fragmentAdminDomain != null) {
fragmentAdminDomain.onDestroyView();
fragmentAdminDomain = null;
} }
setTitle(R.string.administration); setTitle(R.string.administration);
invalidateOptionsMenu(); invalidateOptionsMenu();
@ -294,8 +352,9 @@ public class AdminActionActivity extends BaseActivity {
@SerializedName("REPORT") @SerializedName("REPORT")
REPORT("REPORT"), REPORT("REPORT"),
@SerializedName("ACCOUNT") @SerializedName("ACCOUNT")
ACCOUNT("ACCOUNT"); ACCOUNT("ACCOUNT"),
@SerializedName("DOMAIN")
DOMAIN("DOMAIN");
private final String value; private final String value;
AdminEnum(String value) { AdminEnum(String value) {

View file

@ -0,0 +1,155 @@
package app.fedilab.android.mastodon.activities.admin;
/* Copyright 2022 Thomas Schneider
*
* This file is a part of Fedilab
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.lifecycle.ViewModelProvider;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import app.fedilab.android.R;
import app.fedilab.android.activities.MainActivity;
import app.fedilab.android.databinding.ActivityAdminDomainblockBinding;
import app.fedilab.android.mastodon.activities.BaseBarActivity;
import app.fedilab.android.mastodon.client.entities.api.admin.AdminDomainBlock;
import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.mastodon.viewmodel.mastodon.AdminVM;
import es.dmoral.toasty.Toasty;
public class AdminDomainBlockActivity extends BaseBarActivity {
private final String[] severityChoices = {"silence", "suspend", "noop"};
private AdminVM adminVM;
private AdminDomainBlock adminDomainBlock;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityAdminDomainblockBinding binding = ActivityAdminDomainblockBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
Bundle b = getIntent().getExtras();
if (b != null) {
adminDomainBlock = (AdminDomainBlock) b.getSerializable(Helper.ARG_ADMIN_DOMAINBLOCK);
}
ArrayAdapter<CharSequence> adapterResize = ArrayAdapter.createFromResource(this,
R.array.admin_block_severity, android.R.layout.simple_spinner_dropdown_item);
binding.severity.setAdapter(adapterResize);
if (adminDomainBlock != null) {
binding.domain.setText(adminDomainBlock.domain);
binding.domain.setEnabled(false);
for (int i = 0; i < severityChoices.length; i++) {
if (adminDomainBlock.severity.equalsIgnoreCase(severityChoices[i])) {
binding.severity.setSelection(i, false);
break;
}
}
binding.obfuscate.setChecked(adminDomainBlock.obfuscate);
binding.rejectMedia.setChecked(adminDomainBlock.reject_media);
binding.rejectReports.setChecked(adminDomainBlock.reject_reports);
binding.privateComment.setText(adminDomainBlock.private_comment);
binding.publicComment.setText(adminDomainBlock.public_comment);
} else {
adminDomainBlock = new AdminDomainBlock();
}
binding.severity.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int position, long l) {
adminDomainBlock.severity = severityChoices[position];
}
@Override
public void onNothingSelected(AdapterView<?> adapterView) {
}
});
binding.obfuscate.setOnCheckedChangeListener((compoundButton, checked) -> adminDomainBlock.obfuscate = checked);
binding.rejectMedia.setOnCheckedChangeListener((compoundButton, checked) -> adminDomainBlock.reject_media = checked);
binding.rejectReports.setOnCheckedChangeListener((compoundButton, checked) -> adminDomainBlock.reject_reports = checked);
adminVM = new ViewModelProvider(AdminDomainBlockActivity.this).get(AdminVM.class);
binding.saveChanges.setOnClickListener(v -> {
adminDomainBlock.domain = binding.domain.getText().toString().trim();
adminDomainBlock.public_comment = binding.publicComment.getText().toString().trim();
adminDomainBlock.private_comment = binding.privateComment.getText().toString().trim();
adminVM.createOrUpdateDomainBlock(MainActivity.currentInstance, MainActivity.currentToken, adminDomainBlock)
.observe(AdminDomainBlockActivity.this, adminDomainBlockResult -> {
if (adminDomainBlockResult != null) {
Toasty.success(AdminDomainBlockActivity.this, getString(R.string.saved_changes), Toasty.LENGTH_SHORT).show();
} else {
Toasty.error(AdminDomainBlockActivity.this, getString(R.string.toast_error), Toasty.LENGTH_SHORT).show();
}
Intent intent = new Intent(Helper.BROADCAST_DATA).putExtra(Helper.ARG_ADMIN_DOMAINBLOCK, adminDomainBlockResult);
LocalBroadcastManager.getInstance(AdminDomainBlockActivity.this).sendBroadcast(intent);
finish();
}
);
});
}
@Override
public boolean onCreateOptionsMenu(@NonNull Menu menu) {
getMenuInflater().inflate(R.menu.menu_admin_domain, menu);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int itemId = item.getItemId();
if (itemId == android.R.id.home) {
finish();
return true;
} else if (itemId == R.id.action_delete) {
if (adminDomainBlock.id != null) {
AlertDialog.Builder builder = new MaterialAlertDialogBuilder(AdminDomainBlockActivity.this);
builder.setMessage(getString(R.string.unblock_domain_confirm, adminDomainBlock.domain));
builder
.setPositiveButton(R.string.unblock_domain, (dialog, which) -> {
adminVM.deleteDomain(MainActivity.currentInstance, MainActivity.currentToken, adminDomainBlock.id)
.observe(AdminDomainBlockActivity.this, adminDomainBlockResult -> {
Intent intent = new Intent(Helper.BROADCAST_DATA).putExtra(Helper.ARG_ADMIN_DOMAINBLOCK_DELETE, adminDomainBlock);
LocalBroadcastManager.getInstance(AdminDomainBlockActivity.this).sendBroadcast(intent);
finish();
}
);
dialog.dismiss();
})
.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss())
.show();
} else {
finish();
}
}
return true;
}
}

View file

@ -1,4 +1,4 @@
package app.fedilab.android.activities; package app.fedilab.android.mastodon.activities.admin;
/* Copyright 2022 Thomas Schneider /* Copyright 2022 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -19,7 +19,6 @@ import android.content.ClipData;
import android.content.ClipboardManager; import android.content.ClipboardManager;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.os.Bundle; import android.os.Bundle;
import android.text.SpannableString; import android.text.SpannableString;
@ -55,20 +54,24 @@ import java.util.concurrent.TimeUnit;
import app.fedilab.android.BaseMainActivity; import app.fedilab.android.BaseMainActivity;
import app.fedilab.android.R; import app.fedilab.android.R;
import app.fedilab.android.client.entities.api.Account;
import app.fedilab.android.client.entities.api.AdminAccount;
import app.fedilab.android.client.entities.api.Attachment;
import app.fedilab.android.databinding.ActivityAdminAccountBinding; import app.fedilab.android.databinding.ActivityAdminAccountBinding;
import app.fedilab.android.helper.Helper; import app.fedilab.android.mastodon.activities.BaseBarActivity;
import app.fedilab.android.helper.MastodonHelper; import app.fedilab.android.mastodon.activities.InstanceProfileActivity;
import app.fedilab.android.helper.SpannableHelper; import app.fedilab.android.mastodon.activities.MediaActivity;
import app.fedilab.android.helper.ThemeHelper; import app.fedilab.android.mastodon.client.entities.api.Account;
import app.fedilab.android.viewmodel.mastodon.AdminVM; import app.fedilab.android.mastodon.client.entities.api.Attachment;
import app.fedilab.android.viewmodel.mastodon.NodeInfoVM; import app.fedilab.android.mastodon.client.entities.api.admin.AdminAccount;
import app.fedilab.android.mastodon.client.entities.api.admin.AdminIp;
import app.fedilab.android.mastodon.helper.Helper;
import app.fedilab.android.mastodon.helper.MastodonHelper;
import app.fedilab.android.mastodon.helper.SpannableHelper;
import app.fedilab.android.mastodon.helper.ThemeHelper;
import app.fedilab.android.mastodon.viewmodel.mastodon.AdminVM;
import app.fedilab.android.mastodon.viewmodel.mastodon.NodeInfoVM;
import es.dmoral.toasty.Toasty; import es.dmoral.toasty.Toasty;
public class AdminReportActivity extends BaseActivity { public class AdminReportActivity extends BaseBarActivity {
private AdminAccount adminAccount; private AdminAccount adminAccount;
private Account account; private Account account;
@ -79,7 +82,7 @@ public class AdminReportActivity extends BaseActivity {
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
ThemeHelper.applyTheme(this);
binding = ActivityAdminAccountBinding.inflate(getLayoutInflater()); binding = ActivityAdminAccountBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); setContentView(binding.getRoot());
setSupportActionBar(binding.toolbar); setSupportActionBar(binding.toolbar);
@ -96,7 +99,6 @@ public class AdminReportActivity extends BaseActivity {
//Remove title //Remove title
if (actionBar != null) { if (actionBar != null) {
actionBar.setDisplayShowTitleEnabled(false); actionBar.setDisplayShowTitleEnabled(false);
actionBar.setBackgroundDrawable(new ColorDrawable(ContextCompat.getColor(this, R.color.cyanea_primary)));
} }
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(this); SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(this);
float scale = sharedpreferences.getFloat(getString(R.string.SET_FONT_SCALE), 1.1f); float scale = sharedpreferences.getFloat(getString(R.string.SET_FONT_SCALE), 1.1f);
@ -105,11 +107,10 @@ public class AdminReportActivity extends BaseActivity {
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true); getSupportActionBar().setDisplayShowHomeEnabled(true);
} }
binding.toolbar.setPopupTheme(Helper.popupStyle());
if (account != null) { if (account != null) {
initializeView(account); initializeView(account);
} else { } else {
Toasty.error(AdminReportActivity.this, getString(R.string.toast_error_loading_account), Toast.LENGTH_LONG).show(); Toasty.error(AdminReportActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show();
finish(); finish();
} }
} }
@ -117,7 +118,7 @@ public class AdminReportActivity extends BaseActivity {
private void initializeView(Account account) { private void initializeView(Account account) {
SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(AdminReportActivity.this); SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(AdminReportActivity.this);
if (account == null) { if (account == null) {
Toasty.error(AdminReportActivity.this, getString(R.string.toast_error_loading_account), Toast.LENGTH_LONG).show(); Toasty.error(AdminReportActivity.this, getString(R.string.toast_error), Toast.LENGTH_LONG).show();
finish(); finish();
return; return;
} }
@ -141,7 +142,7 @@ public class AdminReportActivity extends BaseActivity {
binding.email.setText(adminAccount.email); binding.email.setText(adminAccount.email);
StringBuilder lastActive = new StringBuilder(); StringBuilder lastActive = new StringBuilder();
if (adminAccount.ips != null) { if (adminAccount.ips != null) {
for (AdminAccount.IP ip : adminAccount.ips) { for (AdminIp ip : adminAccount.ips) {
lastActive.append(Helper.shortDateToString(ip.used_at)).append(" - ").append(ip.ip).append("\r\n"); lastActive.append(Helper.shortDateToString(ip.used_at)).append(" - ").append(ip.ip).append("\r\n");
} }
} }
@ -287,7 +288,7 @@ public class AdminReportActivity extends BaseActivity {
} }
); );
//Load header //Load header
MastodonHelper.loadProfileMediaMastodon(binding.bannerPp, account, MastodonHelper.MediaAccountType.HEADER); MastodonHelper.loadProfileMediaMastodon(AdminReportActivity.this, binding.bannerPp, account, MastodonHelper.MediaAccountType.HEADER);
//Redraws icon for locked accounts //Redraws icon for locked accounts
final float scale = getResources().getDisplayMetrics().density; final float scale = getResources().getDisplayMetrics().density;
if (account.locked) { if (account.locked) {
@ -310,7 +311,7 @@ public class AdminReportActivity extends BaseActivity {
final SpannableString content = new SpannableString(getString(R.string.disclaimer_full)); final SpannableString content = new SpannableString(getString(R.string.disclaimer_full));
content.setSpan(new UnderlineSpan(), 0, content.length(), 0); content.setSpan(new UnderlineSpan(), 0, content.length(), 0);
content.setSpan(new ForegroundColorSpan(ContextCompat.getColor(AdminReportActivity.this, R.color.cyanea_accent_reference)), 0, content.length(), content.setSpan(new ForegroundColorSpan(ThemeHelper.getAttColor(this, R.attr.colorPrimary)), 0, content.length(),
Spanned.SPAN_INCLUSIVE_EXCLUSIVE); Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
//This account was moved to another one //This account was moved to another one
@ -382,12 +383,11 @@ public class AdminReportActivity extends BaseActivity {
binding.instanceInfo.setVisibility(View.VISIBLE); binding.instanceInfo.setVisibility(View.VISIBLE);
binding.instanceInfo.setOnClickListener(v -> { binding.instanceInfo.setOnClickListener(v -> {
Intent intent = new Intent(AdminReportActivity.this, InstanceProfileActivity.class); InstanceProfileActivity instanceProfileActivity = new InstanceProfileActivity();
Bundle b = new Bundle(); Bundle b = new Bundle();
b.putString(Helper.ARG_INSTANCE, finalAccountInstance); b.putString(Helper.ARG_INSTANCE, finalAccountInstance);
intent.putExtras(b); instanceProfileActivity.setArguments(b);
startActivity(intent); instanceProfileActivity.show(getSupportFragmentManager(), null);
}); });
} }
}); });

View file

@ -1,4 +1,4 @@
package app.fedilab.android.broadcastreceiver; package app.fedilab.android.mastodon.broadcastreceiver;
/* Copyright 2021 Thomas Schneider /* Copyright 2021 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab

View file

@ -1,4 +1,4 @@
package app.fedilab.android.broadcastreceiver; package app.fedilab.android.mastodon.broadcastreceiver;
/* Copyright 2021 Thomas Schneider /* Copyright 2021 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -14,15 +14,12 @@ package app.fedilab.android.broadcastreceiver;
* You should have received a copy of the GNU General Public License along with Fedilab; if not, * You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import static app.fedilab.android.helper.Helper.RECEIVE_TOAST_CONTENT;
import static app.fedilab.android.helper.Helper.RECEIVE_TOAST_TYPE;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import app.fedilab.android.helper.Helper; import app.fedilab.android.mastodon.helper.Helper;
import es.dmoral.toasty.Toasty; import es.dmoral.toasty.Toasty;
public class ToastMessage extends BroadcastReceiver { public class ToastMessage extends BroadcastReceiver {
@ -30,8 +27,8 @@ public class ToastMessage extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
Bundle b = intent.getExtras(); Bundle b = intent.getExtras();
if (b != null) { if (b != null) {
String type = b.getString(RECEIVE_TOAST_TYPE, null); String type = b.getString(Helper.RECEIVE_TOAST_TYPE, null);
String content = b.getString(RECEIVE_TOAST_CONTENT, null); String content = b.getString(Helper.RECEIVE_TOAST_CONTENT, null);
if (type != null && content != null) { if (type != null && content != null) {
switch (type) { switch (type) {
case Helper.RECEIVE_TOAST_TYPE_ERROR: case Helper.RECEIVE_TOAST_TYPE_ERROR:

View file

@ -1,4 +1,4 @@
package app.fedilab.android.client; package app.fedilab.android.mastodon.client;
/* Copyright 2021 Thomas Schneider /* Copyright 2021 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -15,7 +15,7 @@ package app.fedilab.android.client;
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import app.fedilab.android.client.entities.app.WellKnownNodeinfo; import app.fedilab.android.mastodon.client.entities.app.WellKnownNodeinfo;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.http.GET; import retrofit2.http.GET;
import retrofit2.http.Path; import retrofit2.http.Path;

View file

@ -1,4 +1,4 @@
package app.fedilab.android.client.endpoints; package app.fedilab.android.mastodon.client.endpoints;
/* Copyright 2021 Thomas Schneider /* Copyright 2021 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -17,7 +17,7 @@ package app.fedilab.android.client.endpoints;
import java.util.List; import java.util.List;
import app.fedilab.android.client.entities.api.JoinMastodonInstance; import app.fedilab.android.mastodon.client.entities.api.JoinMastodonInstance;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.http.GET; import retrofit2.http.GET;
import retrofit2.http.Query; import retrofit2.http.Query;

View file

@ -1,4 +1,4 @@
package app.fedilab.android.client.endpoints; package app.fedilab.android.mastodon.client.endpoints;
/* Copyright 2021 Thomas Schneider /* Copyright 2021 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -17,17 +17,18 @@ package app.fedilab.android.client.endpoints;
import java.util.List; import java.util.List;
import app.fedilab.android.client.entities.api.Account; import app.fedilab.android.mastodon.client.entities.api.Account;
import app.fedilab.android.client.entities.api.FeaturedTag; import app.fedilab.android.mastodon.client.entities.api.FamiliarFollowers;
import app.fedilab.android.client.entities.api.Filter; import app.fedilab.android.mastodon.client.entities.api.FeaturedTag;
import app.fedilab.android.client.entities.api.IdentityProof; import app.fedilab.android.mastodon.client.entities.api.IdentityProof;
import app.fedilab.android.client.entities.api.MastodonList; import app.fedilab.android.mastodon.client.entities.api.MastodonList;
import app.fedilab.android.client.entities.api.Preferences; import app.fedilab.android.mastodon.client.entities.api.Preferences;
import app.fedilab.android.client.entities.api.RelationShip; import app.fedilab.android.mastodon.client.entities.api.RelationShip;
import app.fedilab.android.client.entities.api.Report; import app.fedilab.android.mastodon.client.entities.api.Report;
import app.fedilab.android.client.entities.api.Status; import app.fedilab.android.mastodon.client.entities.api.Status;
import app.fedilab.android.client.entities.api.Tag; import app.fedilab.android.mastodon.client.entities.api.Suggestion;
import app.fedilab.android.client.entities.api.Token; import app.fedilab.android.mastodon.client.entities.api.Tag;
import app.fedilab.android.mastodon.client.entities.api.Token;
import okhttp3.MultipartBody; import okhttp3.MultipartBody;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.http.Body; import retrofit2.http.Body;
@ -40,7 +41,6 @@ import retrofit2.http.Headers;
import retrofit2.http.Multipart; import retrofit2.http.Multipart;
import retrofit2.http.PATCH; import retrofit2.http.PATCH;
import retrofit2.http.POST; import retrofit2.http.POST;
import retrofit2.http.PUT;
import retrofit2.http.Part; import retrofit2.http.Part;
import retrofit2.http.Path; import retrofit2.http.Path;
import retrofit2.http.Query; import retrofit2.http.Query;
@ -96,7 +96,7 @@ public interface MastodonAccountsService {
@Field("source[privacy]") String privacy, @Field("source[privacy]") String privacy,
@Field("source[sensitive]") Boolean sensitive, @Field("source[sensitive]") Boolean sensitive,
@Field("source[language]") String language, @Field("source[language]") String language,
@Field("fields_attributes") List<app.fedilab.android.client.entities.api.Field.FieldParams> fields @Field("fields_attributes") List<app.fedilab.android.mastodon.client.entities.api.Field.FieldParams> fields
); );
//Get Account //Get Account
@ -106,6 +106,12 @@ public interface MastodonAccountsService {
@Path("id") String id @Path("id") String id
); );
//Get Account
@GET("accounts/lookup")
Call<Account> lookUpAccount(
@Query("acct") String acct
);
//Get Account statuses //Get Account statuses
@GET("accounts/{id}/statuses") @GET("accounts/{id}/statuses")
Call<List<Status>> getAccountStatuses( Call<List<Status>> getAccountStatuses(
@ -153,6 +159,7 @@ public interface MastodonAccountsService {
@Path("id") String id @Path("id") String id
); );
//Get Identity proofs //Get Identity proofs
@GET("accounts/{id}/identity_proofs") @GET("accounts/{id}/identity_proofs")
Call<List<IdentityProof>> getIdentityProofs( Call<List<IdentityProof>> getIdentityProofs(
@ -167,7 +174,8 @@ public interface MastodonAccountsService {
@Header("Authorization") String app_token, @Header("Authorization") String app_token,
@Path("id") String id, @Path("id") String id,
@Field("reblogs") boolean reblogs, @Field("reblogs") boolean reblogs,
@Field("notify") boolean notify @Field("notify") boolean notify,
@Field("languages[]") List<String> languages
); );
//Follow account //Follow account
@ -247,6 +255,13 @@ public interface MastodonAccountsService {
@Query("id[]") List<String> ids @Query("id[]") List<String> ids
); );
//Get familiar followers
@GET("accounts/familiar_followers ")
Call<List<FamiliarFollowers>> getFamiliarFollowers(
@Header("Authorization") String token,
@Query("id[]") List<String> ids
);
//Get search //Get search
@GET("accounts/search") @GET("accounts/search")
Call<List<Account>> searchAccounts( Call<List<Account>> searchAccounts(
@ -316,51 +331,9 @@ public interface MastodonAccountsService {
@DELETE("domain_blocks") @DELETE("domain_blocks")
Call<Void> removeDomainBlocks( Call<Void> removeDomainBlocks(
@Header("Authorization") String token, @Header("Authorization") String token,
@Field("domain") String domain @Query("domain") String domain
); );
//Get filters
@GET("filters")
Call<List<Filter>> getFilters(
@Header("Authorization") String token);
//Get a filter with its id
@GET("filters/{id}")
Call<Filter> getFilter(
@Header("Authorization") String token,
@Path("id") String id);
//Add a filter
@FormUrlEncoded
@POST("filters")
Call<Filter> addFilter(
@Header("Authorization") String token,
@Field("phrase") String phrase,
@Field("context[]") List<String> context,
@Field("irreversible") boolean irreversible,
@Field("whole_word") boolean whole_word,
@Field("expires_in") String expires_in
);
//Edit a filter
@FormUrlEncoded
@PUT("filters/{id}")
Call<Filter> editFilter(
@Header("Authorization") String token,
@Path("id") String id,
@Field("phrase") String phrase,
@Field("context[]") List<String> context,
@Field("irreversible") boolean irreversible,
@Field("whole_word") boolean whole_word,
@Field("expires_in") String expires_in
);
//Remove a filter
@DELETE("filters/{id}")
Call<Void> removeFilter(
@Header("Authorization") String token,
@Path("id") String id
);
//Post a report //Post a report
@Headers({"Accept: application/json"}) @Headers({"Accept: application/json"})
@ -434,7 +407,7 @@ public interface MastodonAccountsService {
//Get user suggestions //Get user suggestions
@GET("suggestions") @GET("suggestions")
Call<List<Account>> getSuggestions( Call<List<Suggestion>> getSuggestions(
@Header("Authorization") String token, @Header("Authorization") String token,
@Query("limit") String limit @Query("limit") String limit
); );
@ -445,4 +418,15 @@ public interface MastodonAccountsService {
@Header("Authorization") String token, @Header("Authorization") String token,
@Path("account_id") String account_id @Path("account_id") String account_id
); );
//Get user suggestions
@GET("directory")
Call<List<Account>> getDirectory(
@Header("Authorization") String token,
@Query("offset") Integer offset,
@Query("limit") Integer limit,
@Query("order") String order,
@Query("local") Boolean local
);
} }

View file

@ -1,4 +1,4 @@
package app.fedilab.android.client.endpoints; package app.fedilab.android.mastodon.client.endpoints;
/* Copyright 2021 Thomas Schneider /* Copyright 2021 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -17,14 +17,17 @@ package app.fedilab.android.client.endpoints;
import java.util.List; import java.util.List;
import app.fedilab.android.client.entities.api.AdminAccount; import app.fedilab.android.mastodon.client.entities.api.admin.AdminAccount;
import app.fedilab.android.client.entities.api.AdminReport; import app.fedilab.android.mastodon.client.entities.api.admin.AdminDomainBlock;
import app.fedilab.android.mastodon.client.entities.api.admin.AdminReport;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.http.DELETE;
import retrofit2.http.Field; import retrofit2.http.Field;
import retrofit2.http.FormUrlEncoded; import retrofit2.http.FormUrlEncoded;
import retrofit2.http.GET; import retrofit2.http.GET;
import retrofit2.http.Header; import retrofit2.http.Header;
import retrofit2.http.POST; import retrofit2.http.POST;
import retrofit2.http.PUT;
import retrofit2.http.Path; import retrofit2.http.Path;
import retrofit2.http.Query; import retrofit2.http.Query;
@ -57,6 +60,7 @@ public interface MastodonAdminService {
@Path("id") String id @Path("id") String id
); );
@FormUrlEncoded
@POST("admin/accounts/{account_id}/action") @POST("admin/accounts/{account_id}/action")
Call<Void> performAction( Call<Void> performAction(
@Header("Authorization") String app_token, @Header("Authorization") String app_token,
@ -68,35 +72,35 @@ public interface MastodonAdminService {
@Field("send_email_notification") Boolean send_email_notification @Field("send_email_notification") Boolean send_email_notification
); );
@FormUrlEncoded
@POST("admin/accounts/{account_id}/approve") @POST("admin/accounts/{account_id}/approve")
Call<AdminAccount> approve( Call<AdminAccount> approve(
@Header("Authorization") String app_token, @Header("Authorization") String app_token,
@Path("account_id") String account_id @Path("account_id") String account_id
); );
@FormUrlEncoded
@POST("admin/accounts/{account_id}/reject") @POST("admin/accounts/{account_id}/reject")
Call<AdminAccount> reject( Call<AdminAccount> reject(
@Header("Authorization") String app_token, @Header("Authorization") String app_token,
@Path("account_id") String account_id @Path("account_id") String account_id
); );
@FormUrlEncoded
@POST("admin/accounts/{account_id}/enable") @POST("admin/accounts/{account_id}/enable")
Call<AdminAccount> enable( Call<AdminAccount> enable(
@Header("Authorization") String app_token, @Header("Authorization") String app_token,
@Path("account_id") String account_id @Path("account_id") String account_id
); );
@FormUrlEncoded
@POST("admin/accounts/{account_id}/unsilence") @POST("admin/accounts/{account_id}/unsilence")
Call<AdminAccount> unsilence( Call<AdminAccount> unsilence(
@Header("Authorization") String app_token, @Header("Authorization") String app_token,
@Path("account_id") String account_id @Path("account_id") String account_id
); );
@FormUrlEncoded
@POST("admin/accounts/{account_id}/unsuspend") @POST("admin/accounts/{account_id}/unsuspend")
Call<AdminAccount> unsuspend( Call<AdminAccount> unsuspend(
@Header("Authorization") String app_token, @Header("Authorization") String app_token,
@ -114,38 +118,115 @@ public interface MastodonAdminService {
@Query("limit") int limit @Query("limit") int limit
); );
@FormUrlEncoded //***************** ADMIN REPORTS **************
@GET("admin/reports/{id}") @GET("admin/reports/{id}")
Call<AdminReport> getReport( Call<AdminReport> getReport(
@Header("Authorization") String token, @Header("Authorization") String token,
@Path("id") String id @Path("id") String id
); );
@FormUrlEncoded
@POST("admin/reports/{id}/assign_to_self") @POST("admin/reports/{id}/assign_to_self")
Call<AdminReport> assignToSelf( Call<AdminReport> assignToSelf(
@Header("Authorization") String app_token, @Header("Authorization") String app_token,
@Path("id") String id @Path("id") String id
); );
@FormUrlEncoded
@POST("admin/reports/{id}/unassign") @POST("admin/reports/{id}/unassign")
Call<AdminReport> unassign( Call<AdminReport> unassign(
@Header("Authorization") String app_token, @Header("Authorization") String app_token,
@Path("id") String id @Path("id") String id
); );
@FormUrlEncoded
@POST("admin/reports/{id}/resolve") @POST("admin/reports/{id}/resolve")
Call<AdminReport> resolved( Call<AdminReport> resolved(
@Header("Authorization") String app_token, @Header("Authorization") String app_token,
@Path("id") String id @Path("id") String id
); );
@FormUrlEncoded
@POST("admin/reports/{id}/reopen") @POST("admin/reports/{id}/reopen")
Call<AdminReport> reopen( Call<AdminReport> reopen(
@Header("Authorization") String app_token, @Header("Authorization") String app_token,
@Path("id") String id @Path("id") String id
); );
//*************** ADMIN DOMAINS ****************
@GET("admin/domain_blocks")
Call<List<AdminDomainBlock>> getDomainBlocks(
@Header("Authorization") String token,
@Query("max_id") String max_id,
@Query("limit") int limit
);
@GET("admin/domain_allows")
Call<List<AdminDomainBlock>> getDomainAllows(
@Header("Authorization") String token,
@Query("max_id") String max_id,
@Query("limit") int limit
);
@GET("admin/domain_blocks/{id}")
Call<AdminDomainBlock> getDomainBlock(
@Header("Authorization") String token,
@Path("id") String id
);
@GET("admin/domain_allows/{id}")
Call<AdminDomainBlock> getDomainAllow(
@Header("Authorization") String token,
@Path("id") String id
);
@FormUrlEncoded
@POST("admin/domain_blocks")
Call<AdminDomainBlock> blockDomain(
@Header("Authorization") String app_token,
@Field("domain") String domain,
@Field("severity") String severity,
@Field("reject_media") Boolean reject_media,
@Field("reject_reports") Boolean reject_reports,
@Field("private_comment") String private_comment,
@Field("public_comment") String public_comment,
@Field("obfuscate") Boolean obfuscate
);
@FormUrlEncoded
@POST("admin/domain_allows")
Call<AdminDomainBlock> allowDomain(
@Header("Authorization") String app_token,
@Path("domain") String domain
);
@FormUrlEncoded
@PUT("admin/domain_blocks/{id}")
Call<AdminDomainBlock> updateBlockDomain(
@Header("Authorization") String app_token,
@Path("id") String id,
@Field("severity") String severity,
@Field("reject_media") Boolean reject_media,
@Field("reject_reports") Boolean reject_reports,
@Field("private_comment") String private_comment,
@Field("public_comment") String public_comment,
@Field("obfuscate") Boolean obfuscate
);
@DELETE("admin/domain_blocks/{id}")
Call<Void> deleteBlockDomain(
@Header("Authorization") String app_token,
@Path("id") String id
);
@DELETE("admin/domain_allows/{id}")
Call<Void> deleteAllowDomain(
@Header("Authorization") String app_token,
@Path("id") String id
);
} }

View file

@ -1,4 +1,4 @@
package app.fedilab.android.client.endpoints; package app.fedilab.android.mastodon.client.endpoints;
/* Copyright 2021 Thomas Schneider /* Copyright 2021 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -17,7 +17,7 @@ package app.fedilab.android.client.endpoints;
import java.util.List; import java.util.List;
import app.fedilab.android.client.entities.api.Announcement; import app.fedilab.android.mastodon.client.entities.api.Announcement;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.http.DELETE; import retrofit2.http.DELETE;
import retrofit2.http.FormUrlEncoded; import retrofit2.http.FormUrlEncoded;

View file

@ -1,4 +1,4 @@
package app.fedilab.android.client.endpoints; package app.fedilab.android.mastodon.client.endpoints;
/* Copyright 2021 Thomas Schneider /* Copyright 2021 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -15,8 +15,8 @@ package app.fedilab.android.client.endpoints;
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import app.fedilab.android.client.entities.api.App; import app.fedilab.android.mastodon.client.entities.api.App;
import app.fedilab.android.client.entities.api.Token; import app.fedilab.android.mastodon.client.entities.api.Token;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.http.Field; import retrofit2.http.Field;
import retrofit2.http.FormUrlEncoded; import retrofit2.http.FormUrlEncoded;

View file

@ -0,0 +1,106 @@
package app.fedilab.android.mastodon.client.endpoints;
/* Copyright 2022 Thomas Schneider
*
* This file is a part of Fedilab
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */
import java.util.List;
import app.fedilab.android.mastodon.client.entities.api.Filter;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.DELETE;
import retrofit2.http.Field;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.GET;
import retrofit2.http.Header;
import retrofit2.http.Headers;
import retrofit2.http.POST;
import retrofit2.http.PUT;
import retrofit2.http.Path;
public interface MastodonFiltersService {
//Get filters
@GET("filters")
Call<List<Filter>> getFilters(
@Header("Authorization") String token);
//Get a filter with its id
@GET("filters/{id}")
Call<Filter> getFilter(
@Header("Authorization") String token,
@Path("id") String id);
//Add a filter
@Headers({"Accept: application/json"})
@POST("filters")
Call<Filter> addFilter(
@Header("Authorization") String token,
@Body Filter.FilterParams filter
);
//Edit a filter
@Headers({"Accept: application/json"})
@PUT("filters/{id}")
Call<Filter> editFilter(
@Header("Authorization") String token,
@Path("id") String id,
@Body Filter.FilterParams filter
);
//Remove a filter
@DELETE("filters/{id}")
Call<Void> removeFilter(
@Header("Authorization") String token,
@Path("id") String id
);
//Get a keywords for a filter
@GET("filters/{id}/keywords")
Call<List<Filter.KeywordsAttributes>> getKeywordFilter(
@Header("Authorization") String token,
@Path("id") String id);
//Add a keyword to a filter
@FormUrlEncoded
@POST("filters/{filter_id}/keywords/{id}")
Call<Filter.KeywordsAttributes> addKeywordFilter(
@Header("Authorization") String token,
@Path("filter_id") String filter_id,
@Path("id") String id,
@Field("keyword") Filter.KeywordsAttributes keyword
);
//Edit a keyword for a filter
@FormUrlEncoded
@PUT("filter_keywords/{id}")
Call<Filter.KeywordsAttributes> editKeywordFilter(
@Header("Authorization") String token,
@Path("id") String id,
@Field("keyword") Filter.KeywordsAttributes keyword
);
//Remove a keyword for a filter
@DELETE("filters/keywords/{id}")
Call<Void> removeKeywordFilter(
@Header("Authorization") String token,
@Path("id") String id
);
}

View file

@ -1,4 +1,4 @@
package app.fedilab.android.client.endpoints; package app.fedilab.android.mastodon.client.endpoints;
/* Copyright 2021 Thomas Schneider /* Copyright 2021 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -17,11 +17,11 @@ package app.fedilab.android.client.endpoints;
import java.util.List; import java.util.List;
import app.fedilab.android.client.entities.api.Account; import app.fedilab.android.mastodon.client.entities.api.Account;
import app.fedilab.android.client.entities.api.Activity; import app.fedilab.android.mastodon.client.entities.api.Activity;
import app.fedilab.android.client.entities.api.Emoji; import app.fedilab.android.mastodon.client.entities.api.Emoji;
import app.fedilab.android.client.entities.api.Instance; import app.fedilab.android.mastodon.client.entities.api.Instance;
import app.fedilab.android.client.entities.api.Tag; import app.fedilab.android.mastodon.client.entities.api.Tag;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.http.GET; import retrofit2.http.GET;
import retrofit2.http.Query; import retrofit2.http.Query;

View file

@ -1,4 +1,4 @@
package app.fedilab.android.client.endpoints; package app.fedilab.android.mastodon.client.endpoints;
/* Copyright 2021 Thomas Schneider /* Copyright 2021 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -16,8 +16,8 @@ package app.fedilab.android.client.endpoints;
import java.util.List; import java.util.List;
import app.fedilab.android.client.entities.api.Notification; import app.fedilab.android.mastodon.client.entities.api.Notification;
import app.fedilab.android.client.entities.api.PushSubscription; import app.fedilab.android.mastodon.client.entities.api.PushSubscription;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.http.DELETE; import retrofit2.http.DELETE;
import retrofit2.http.Field; import retrofit2.http.Field;
@ -71,7 +71,12 @@ public interface MastodonNotificationsService {
@Field("data[alerts][favourite]") boolean favourite, @Field("data[alerts][favourite]") boolean favourite,
@Field("data[alerts][reblog]") boolean reblog, @Field("data[alerts][reblog]") boolean reblog,
@Field("data[alerts][mention]") boolean mention, @Field("data[alerts][mention]") boolean mention,
@Field("data[alerts][poll]") boolean poll @Field("data[alerts][poll]") boolean poll,
@Field("data[alerts][status]") boolean status,
@Field("data[alerts][update]") boolean update,
@Field("data[alerts][admin.sign_up]") boolean admin_sign_up,
@Field("data[alerts][admin.report]") boolean admin_report
); );
@GET("push/subscription") @GET("push/subscription")

View file

@ -1,4 +1,4 @@
package app.fedilab.android.client.endpoints; package app.fedilab.android.mastodon.client.endpoints;
/* Copyright 2021 Thomas Schneider /* Copyright 2021 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -15,7 +15,7 @@ package app.fedilab.android.client.endpoints;
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import app.fedilab.android.client.entities.api.Oembed; import app.fedilab.android.mastodon.client.entities.api.Oembed;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.http.GET; import retrofit2.http.GET;
import retrofit2.http.Query; import retrofit2.http.Query;

View file

@ -1,4 +1,4 @@
package app.fedilab.android.client.endpoints; package app.fedilab.android.mastodon.client.endpoints;
/* Copyright 2021 Thomas Schneider /* Copyright 2021 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -15,7 +15,7 @@ package app.fedilab.android.client.endpoints;
* see <http://www.gnu.org/licenses>. */ * see <http://www.gnu.org/licenses>. */
import app.fedilab.android.client.entities.api.Results; import app.fedilab.android.mastodon.client.entities.api.Results;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.http.GET; import retrofit2.http.GET;
import retrofit2.http.Header; import retrofit2.http.Header;

View file

@ -1,4 +1,4 @@
package app.fedilab.android.client.endpoints; package app.fedilab.android.mastodon.client.endpoints;
/* Copyright 2021 Thomas Schneider /* Copyright 2021 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -17,14 +17,14 @@ package app.fedilab.android.client.endpoints;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import app.fedilab.android.client.entities.api.Account; import app.fedilab.android.mastodon.client.entities.api.Account;
import app.fedilab.android.client.entities.api.Attachment; import app.fedilab.android.mastodon.client.entities.api.Attachment;
import app.fedilab.android.client.entities.api.Card; import app.fedilab.android.mastodon.client.entities.api.Card;
import app.fedilab.android.client.entities.api.Context; import app.fedilab.android.mastodon.client.entities.api.Context;
import app.fedilab.android.client.entities.api.Poll; import app.fedilab.android.mastodon.client.entities.api.Poll;
import app.fedilab.android.client.entities.api.ScheduledStatus; import app.fedilab.android.mastodon.client.entities.api.ScheduledStatus;
import app.fedilab.android.client.entities.api.Status; import app.fedilab.android.mastodon.client.entities.api.Status;
import app.fedilab.android.client.entities.api.StatusSource; import app.fedilab.android.mastodon.client.entities.api.StatusSource;
import okhttp3.MultipartBody; import okhttp3.MultipartBody;
import okhttp3.RequestBody; import okhttp3.RequestBody;
import retrofit2.Call; import retrofit2.Call;
@ -59,7 +59,9 @@ public interface MastodonStatusesService {
@Field("sensitive") Boolean sensitive, @Field("sensitive") Boolean sensitive,
@Field("spoiler_text") String spoiler_text, @Field("spoiler_text") String spoiler_text,
@Field("visibility") String visibility, @Field("visibility") String visibility,
@Field("language") String language @Field("language") String language,
@Field("quote_id") String quote_id,
@Field("content_type") String content_type
); );
@GET("statuses/{id}/source") @GET("statuses/{id}/source")
@ -89,10 +91,12 @@ public interface MastodonStatusesService {
@Field("sensitive") Boolean sensitive, @Field("sensitive") Boolean sensitive,
@Field("spoiler_text") String spoiler_text, @Field("spoiler_text") String spoiler_text,
@Field("visibility") String visibility, @Field("visibility") String visibility,
@Field("language") String language @Field("language") String language,
@Field("media_attributes[id][]") List<String> media_id,
@Field("media_attributes[description][]") List<String> media_description,
@Field("media_attributes[focus][]") List<String> focus
); );
//Post a scheduled status //Post a scheduled status
@FormUrlEncoded @FormUrlEncoded
@POST("statuses") @POST("statuses")
@ -251,7 +255,7 @@ public interface MastodonStatusesService {
@Part MultipartBody.Part file, @Part MultipartBody.Part file,
@Part MultipartBody.Part thumbnail, @Part MultipartBody.Part thumbnail,
@Part("description") RequestBody description, @Part("description") RequestBody description,
@Part("focus") String focus @Part("focus") RequestBody focus
); );
//Edit a Media //Edit a Media
@ -314,4 +318,18 @@ public interface MastodonStatusesService {
@Header("Authorization") String token, @Header("Authorization") String token,
@Path("id") String id @Path("id") String id
); );
@POST("statuses/{id}/react/{name}")
Call<Void> addReaction(
@Header("Authorization") String app_token,
@Path("id") String id,
@Path("name") String name
);
@POST("statuses/{id}/unreact/{name}")
Call<Void> removeReaction(
@Header("Authorization") String app_token,
@Path("id") String id,
@Path("name") String name
);
} }

View file

@ -0,0 +1,56 @@
package app.fedilab.android.mastodon.client.endpoints;
/* Copyright 2022 Thomas Schneider
*
* This file is a part of Fedilab
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* Fedilab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with Fedilab; if not,
* see <http://www.gnu.org/licenses>. */
import java.util.List;
import app.fedilab.android.mastodon.client.entities.api.Tag;
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Header;
import retrofit2.http.POST;
import retrofit2.http.Path;
public interface MastodonTagService {
//Get followed tags
@GET("followed_tags")
Call<List<Tag>> getFollowedTags(
@Header("Authorization") String token
);
//Get followed tags
@GET("tags/{name}")
Call<Tag> getTag(
@Header("Authorization") String token,
@Path("name") String name
);
//Follow tag
@POST("tags/{name}/follow")
Call<Tag> follow(
@Header("Authorization") String app_token,
@Path("name") String name
);
//Unfollow tag
@POST("tags/{name}/unfollow")
Call<Tag> unfollow(
@Header("Authorization") String app_token,
@Path("name") String name
);
}

View file

@ -1,4 +1,4 @@
package app.fedilab.android.client.endpoints; package app.fedilab.android.mastodon.client.endpoints;
/* Copyright 2021 Thomas Schneider /* Copyright 2021 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab
@ -16,15 +16,15 @@ package app.fedilab.android.client.endpoints;
import java.util.List; import java.util.List;
import app.fedilab.android.client.entities.api.Account; import app.fedilab.android.mastodon.client.entities.api.Account;
import app.fedilab.android.client.entities.api.Conversation; import app.fedilab.android.mastodon.client.entities.api.Conversation;
import app.fedilab.android.client.entities.api.Marker; import app.fedilab.android.mastodon.client.entities.api.Marker;
import app.fedilab.android.client.entities.api.MastodonList; import app.fedilab.android.mastodon.client.entities.api.MastodonList;
import app.fedilab.android.client.entities.api.Status; import app.fedilab.android.mastodon.client.entities.api.Status;
import app.fedilab.android.client.entities.api.Tag; import app.fedilab.android.mastodon.client.entities.api.Tag;
import app.fedilab.android.client.entities.misskey.MisskeyNote; import app.fedilab.android.mastodon.client.entities.misskey.MisskeyNote;
import app.fedilab.android.client.entities.nitter.Nitter; import app.fedilab.android.mastodon.client.entities.nitter.Nitter;
import app.fedilab.android.client.entities.peertube.PeertubeVideo; import app.fedilab.android.mastodon.client.entities.peertube.PeertubeVideo;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.http.Body; import retrofit2.http.Body;
import retrofit2.http.DELETE; import retrofit2.http.DELETE;
@ -53,13 +53,31 @@ public interface MastodonTimelinesService {
@Query("limit") Integer limit @Query("limit") Integer limit
); );
@GET("timelines/bubble")
Call<List<Status>> getBubble(
@Header("Authorization") String token,
@Query("only_media") Boolean only_media,
@Query("remote") Boolean remote,
@Query("with_muted") Boolean with_muted,
@Query("exclude_visibilities") List<String> exclude_visibilities,
@Query("reply_visibility") String reply_visibility,
@Query("max_id") String max_id,
@Query("since_id") String since_id,
@Query("min_id") String min_id,
@Query("limit") Integer limit
);
@GET("trends/statuses") @GET("trends/statuses")
Call<List<Status>> getStatusTrends(@Header("Authorization") String token); Call<List<Status>> getStatusTrends(
@Header("Authorization") String token,
@Query("offset") String offset,
@Query("limit") Integer limit);
@GET("trends/tags") @GET("trends/tags")
Call<List<Tag>> getTagTrends(@Header("Authorization") String token); Call<List<Tag>> getTagTrends(@Header("Authorization") String token,
@Query("offset") Integer offset,
@Query("limit") Integer limit);
//Public Tags timelines //Public Tags timelines
@GET("timelines/tag/{hashtag}") @GET("timelines/tag/{hashtag}")

View file

@ -1,4 +1,4 @@
package app.fedilab.android.client.endpoints; package app.fedilab.android.mastodon.client.endpoints;
/* Copyright 2022 Thomas Schneider /* Copyright 2022 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab

View file

@ -1,4 +1,4 @@
package app.fedilab.android.client.endpoints; package app.fedilab.android.mastodon.client.endpoints;
/* Copyright 2021 Thomas Schneider /* Copyright 2021 Thomas Schneider
* *
* This file is a part of Fedilab * This file is a part of Fedilab

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