Compare commits

..

99 Commits

Author SHA1 Message Date
sk
5ed6f97846 update changelog 2022-12-31 03:08:08 +01:00
sk
7dc195606c fix empty space when no text 2022-12-31 03:07:14 +01:00
sk
b2377a3353 tweak scroll height/timing 2022-12-31 02:54:33 +01:00
sk
0fdae0c775 Revert "match navigation bar color with toolbar"
This reverts commit 43d334259b.
2022-12-31 02:50:18 +01:00
sk
113bbd960f move settings items 2022-12-31 02:40:49 +01:00
sk
a48e09e77b bump version 2022-12-31 02:02:38 +01:00
sk
3c3f759d9a update readme 2022-12-31 02:02:00 +01:00
sk
5f0986d03b add option to reduce motion 2022-12-31 02:00:40 +01:00
sk
579794d7e0 peek original post before scrolling 2022-12-31 01:52:07 +01:00
sk
4956543eac update changelog 2022-12-31 01:37:39 +01:00
sk
87043f19bc Merge remote-tracking branch 'weblate/main' 2022-12-31 01:35:07 +01:00
sk
375f8ceb27 display original post when replying
closes sk22#193
2022-12-31 01:33:37 +01:00
sk
496ad6a442 change interact with strings 2022-12-30 23:08:04 +01:00
ihor_ck
83a09c4af2 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (12 of 12 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/uk/
2022-12-30 21:54:40 +00:00
gallegonovato
fac6985d01 Translated using Weblate (Spanish)
Currently translated at 100.0% (12 of 12 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/es/
2022-12-30 21:54:40 +00:00
Linerly
43670ba62b Translated using Weblate (Indonesian)
Currently translated at 100.0% (12 of 12 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/id/
2022-12-30 21:54:40 +00:00
Choukajohn
ef511349f8 Translated using Weblate (French)
Currently translated at 100.0% (12 of 12 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/fr/
2022-12-30 21:54:40 +00:00
ihor_ck
eea0199a21 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (121 of 121 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/uk/
2022-12-30 21:54:40 +00:00
Linerly
7e0f02ecc7 Translated using Weblate (Indonesian)
Currently translated at 100.0% (121 of 121 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/id/
2022-12-30 21:54:40 +00:00
Choukajohn
7b26a65ea0 Translated using Weblate (French)
Currently translated at 100.0% (121 of 121 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/fr/
2022-12-30 21:54:40 +00:00
gallegonovato
c5b92d7162 Translated using Weblate (Spanish)
Currently translated at 100.0% (121 of 121 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/es/
2022-12-30 21:54:40 +00:00
bubblineyuri
f8387f0a81 Translated using Weblate (German)
Currently translated at 100.0% (12 of 12 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/de/
2022-12-30 21:54:40 +00:00
bubblineyuri
8486326b00 Translated using Weblate (German)
Currently translated at 100.0% (121 of 121 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/de/
2022-12-30 21:54:40 +00:00
edxkl
8f64747e75 Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (11 of 11 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/pt_BR/
2022-12-30 21:54:40 +00:00
ihor_ck
f75efdaa03 Translated using Weblate (Ukrainian)
Currently translated at 96.6% (115 of 119 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/uk/
2022-12-30 21:54:40 +00:00
Linerly
ee8d9d0c07 Translated using Weblate (Indonesian)
Currently translated at 100.0% (119 of 119 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/id/
2022-12-30 21:54:40 +00:00
ling0412
6791adef46 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (119 of 119 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/zh_Hans/
2022-12-30 21:54:40 +00:00
irure
80bcb84acf Translated using Weblate (Basque)
Currently translated at 18.1% (2 of 11 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/eu/
2022-12-30 21:54:40 +00:00
AiOO
7d6b0f9ca8 Translated using Weblate (Korean)
Currently translated at 100.0% (11 of 11 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/ko/
2022-12-30 21:54:39 +00:00
nkufideal
594f6d4fc5 Translated using Weblate (Russian)
Currently translated at 100.0% (11 of 11 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/ru/
2022-12-30 21:54:39 +00:00
nkufideal
e40acb9bf6 Translated using Weblate (Russian)
Currently translated at 100.0% (102 of 102 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/ru/
2022-12-30 21:54:39 +00:00
itslameni
e372871108 Translated using Weblate (Russian)
Currently translated at 100.0% (102 of 102 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/ru/
2022-12-30 21:54:39 +00:00
edxkl
1c01469a3e Translated using Weblate (Portuguese (Brazil))
Currently translated at 80.3% (82 of 102 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/pt_BR/
2022-12-30 21:54:39 +00:00
irure
90c2e45be2 Translated using Weblate (Basque)
Currently translated at 100.0% (102 of 102 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/eu/
2022-12-30 21:54:39 +00:00
AiOO
1a783d4faf Translated using Weblate (Korean)
Currently translated at 100.0% (102 of 102 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/ko/
2022-12-30 21:54:39 +00:00
edxkl
d2944983a4 Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (11 of 11 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/pt_BR/
2022-12-30 21:54:39 +00:00
ihor_ck
26459eecb0 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (102 of 102 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/uk/
2022-12-30 21:54:39 +00:00
Linerly
90dea16222 Translated using Weblate (Indonesian)
Currently translated at 100.0% (102 of 102 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/id/
2022-12-30 21:54:39 +00:00
Choukajohn
8897a326b3 Translated using Weblate (French)
Currently translated at 100.0% (102 of 102 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/fr/
2022-12-30 21:54:39 +00:00
gallegonovato
f3e69ddef1 Translated using Weblate (Spanish)
Currently translated at 100.0% (102 of 102 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/es/
2022-12-30 21:54:39 +00:00
ling0412
8ecaa2c4d1 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (102 of 102 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/zh_Hans/
2022-12-30 21:54:39 +00:00
sk
3a607a81f6 add compose fab to drafts list 2022-12-30 22:54:07 +01:00
sk
4b113e4a82 handle recursive compose/drafts list 2022-12-30 22:34:41 +01:00
sk
3e0e0c1484 move and refine scheduling and drafting 2022-12-30 21:48:59 +01:00
sk
676e166459 move compose action to layout file 2022-12-30 19:24:30 +01:00
sk
f0a704b93b schedule/draft in overflow menu 2022-12-29 20:24:37 +01:00
sk
cfc528df9a Merge remote-tracking branch 'origin/main' 2022-12-29 20:12:31 +01:00
sk
9231ea1446 offer to save when scheduledAt changed
closes sk22#218
2022-12-29 20:12:08 +01:00
sk22
f2f9138435 Update README.md 2022-12-29 18:39:32 +01:00
sk
624700497b update changelog 2022-12-29 18:28:24 +01:00
sk
5794a64da6 use rules fragment in settings 2022-12-29 18:27:37 +01:00
sk
7e6e9d3dcd move translation availability
closes sk22#213
2022-12-29 18:14:26 +01:00
sk
07634edfa3 don't finish compose fragment 2022-12-29 18:14:00 +01:00
sk
4a03fbfbf0 update changelog, bump version 2022-12-29 18:01:50 +01:00
sk
000cdb08ec change fab styles
closes sk22#211
2022-12-29 18:01:22 +01:00
sk
e0521b3c95 add mention with quasi-quoting 2022-12-29 17:14:38 +01:00
sk22
9547be89e1 Drafts and scheduled posts (#217)
closes #100
closes #59 

* enable saving as draft (scheduled)
* add scheduled posts list
* fix NoSuchMethodError
* editable drafts/scheduled posts
* ui for drafts
* use instants between 9999-01-01 and 9999-12-31
* use save and draft strings
* map scheduled status params to status
* implement scheduling posts
* improve save/discard draft dialog
* persist scheduled date in state
* add unsent posts button to toolbar
* clean up imports
2022-12-29 16:53:18 +01:00
sk
40d44269fc Merge branch 'main' of github.com:sk22/megalodon 2022-12-29 16:46:14 +01:00
sk
3bf0903453 fix toolbar inset not applying in true black 2022-12-29 16:46:10 +01:00
Thiago 'Jedi' Abreu
c3abf8c05c Better handling of filter expiration date (#212)
* Better handling of filter expiration date
* Simplify Thread and Home Timeline filtering
2022-12-29 13:32:22 +01:00
Thiago 'Jedi' Abreu
6220ce6780 Implement 4.0 filters with hide action (#202)
* adding new "filtered" field for status
* respect "hide" filter action on status
* handling expire date for filter

closes #161
2022-12-28 14:49:45 +01:00
sk
6107698a76 optionally forward report
closes #205
2022-12-28 14:45:20 +01:00
sk
a3c3fec9b4 add icon indicating the existence of replies
closes #207
2022-12-28 14:13:49 +01:00
sk
f3a9b19104 replace mute icon 2022-12-28 01:58:44 +01:00
sk
d9ed6f600b change animation duration 2022-12-28 01:39:06 +01:00
sk
8a6d86727c remove unused menu item 2022-12-28 01:35:53 +01:00
sk
d9abf82918 fix null pointer on loading profile 2022-12-28 01:35:04 +01:00
sk
3cd9020ee0 use short usernames in popup menus
improves upon mastodon#148
2022-12-28 00:58:57 +01:00
sk
6d22a4d014 icons. icons everywhere 2022-12-28 00:53:06 +01:00
sk
ccad5d40ec fix akkoma crash on edit 2022-12-27 22:00:19 +01:00
ihor_ck
5b7b022d9f Translated using Weblate (Ukrainian)
Currently translated at 100.0% (11 of 11 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/uk/
2022-12-27 20:46:35 +00:00
Andrewblasco
387139b5d3 Translated using Weblate (Spanish)
Currently translated at 100.0% (11 of 11 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/es/
2022-12-27 20:46:35 +00:00
edxkl
0ae10d5fbe Translated using Weblate (Portuguese (Brazil))
Currently translated at 100.0% (11 of 11 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/pt_BR/
2022-12-27 20:46:35 +00:00
ihor_ck
d53397d8f8 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (91 of 91 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/uk/
2022-12-27 20:46:35 +00:00
sk
b1006d2a14 rename string 2022-12-27 21:46:21 +01:00
sk
c5cf318cdd Merge remote-tracking branch 'weblate/main' 2022-12-27 21:45:01 +01:00
sk
671338c16a different icon for delete notifs setting 2022-12-27 21:41:08 +01:00
sk
bbaa70e396 add option to use uniform notif icon 2022-12-27 21:39:41 +01:00
sk
536d6cf63e revert broken color attribute for notif icon
closes #190
2022-12-27 21:30:35 +01:00
sk
b564a297ab fix #200 2022-12-27 21:20:20 +01:00
sk
6d34ae1a50 long-click to compose from other account 2022-12-27 21:02:05 +01:00
sk
0d2457f39e restore accidentally deleted functionality 2022-12-27 20:43:29 +01:00
sk
420505328c fix default visibility crashing akkoma session
closes #196
2022-12-27 20:36:06 +01:00
sk
0e60d71006 refine long-clicks
and disable long-click for multiple accounts when logged in to single account
2022-12-27 20:30:58 +01:00
sk
0f1456819b interact as other accounts
closes #199
2022-12-27 19:53:01 +01:00
sk
3af89a6175 update readme 2022-12-27 13:32:23 +01:00
gallegonovato
2fac871493 Translated using Weblate (Spanish)
Currently translated at 100.0% (11 of 11 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/es/
2022-12-26 09:34:47 +00:00
Pointifurry
0166133b61 Translated using Weblate (Spanish)
Currently translated at 100.0% (11 of 11 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/es/
2022-12-26 09:34:46 +00:00
Linerly
a9e0911796 Translated using Weblate (Indonesian)
Currently translated at 100.0% (11 of 11 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/id/
2022-12-26 09:34:46 +00:00
ling0412
213f341257 Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (11 of 11 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/zh_Hans/
2022-12-26 09:34:46 +00:00
Choukajohn
01cc2a2c67 Translated using Weblate (French)
Currently translated at 100.0% (11 of 11 strings)

Translation: Megalodon/metadata
Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/fr/
2022-12-26 09:34:46 +00:00
nitrogenez
6c7a040c02 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (91 of 91 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/uk/
2022-12-26 09:34:46 +00:00
itslameni
4e40944b26 Translated using Weblate (Russian)
Currently translated at 96.7% (88 of 91 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/ru/
2022-12-26 09:34:46 +00:00
goliv
af859438b4 Translated using Weblate (Portuguese (Brazil))
Currently translated at 74.7% (68 of 91 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/pt_BR/
2022-12-26 09:34:46 +00:00
Linerly
7ef80e87b8 Translated using Weblate (Indonesian)
Currently translated at 100.0% (91 of 91 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/id/
2022-12-26 09:34:45 +00:00
Choukajohn
f8e873cd78 Translated using Weblate (French)
Currently translated at 100.0% (91 of 91 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/fr/
2022-12-26 09:34:45 +00:00
Pointifurry
5cc97f7cf1 Translated using Weblate (Spanish)
Currently translated at 100.0% (91 of 91 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/es/
2022-12-26 09:34:45 +00:00
ling0412
44c56331fd Translated using Weblate (Chinese (Simplified))
Currently translated at 100.0% (91 of 91 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/zh_Hans/
2022-12-26 09:34:45 +00:00
sk22
1272c3962e Translated using Weblate (German)
Currently translated at 100.0% (91 of 91 strings)

Translation: Megalodon/values
Translate-URL: https://translate.codeberg.org/projects/megalodon/values/de/
2022-12-26 09:34:45 +00:00
153 changed files with 2650 additions and 748 deletions

View File

@@ -8,7 +8,7 @@
<a href="https://play.google.com/store/apps/details?id=org.joinmastodon.android.sk"><img height="50" alt="Get it on Google Play" src="img/google-play-badge.png"></a>
&nbsp;
<a href="https://apt.izzysoft.de/fdroid/index/apk/org.joinmastodon.android.sk"><img height="50" alt="Get it on F-Droid" src="img/f-droid-badge.png"></a>
<a href="#installation"><img height="50" alt="Get it on IzzyOnDroid" src="img/izzy-badge.png"></a>
> A fork of the [official Mastodon Android app](https://github.com/mastodon/mastodon-android) adding important features that are missing in the official app and possibly wont ever be implemented, such as the federated timeline, unlisted posting and an image description viewer.
@@ -31,6 +31,12 @@ Despite being one of the main features of federated social media, the Federated
Thats one of the reasons why choosing a small, **well-moderated instance is important**. Instance admins and moderators should always make sure to ban abusive users and stop federating with instances who platform them. On well-moderated instances, the Federated timeline can be a welcoming place to meet new people!
### Draft and schedule posts
**Allows for preparing a post and scheduling it to send it automatically at a specific time.**
You can create drafts, edit them, send them manually later or set a scheduled date. Drafts are technically saved as scheduled posts, so you can view and edit them from other apps that support scheduled posts. Scheduled posts are handled by your home instance, so they'll work even if you uninstall Megalodon.
### **Image description viewer**
**Allows you to quickly check whether an image or video has an alternative text attached to it.**
@@ -45,21 +51,29 @@ On the Fediverse, its quite common for people to pin posts they want others t
## Installation
### From app stores
### IzzyOnDroid
* **[Izzy's F-Droid repository](https://apt.izzysoft.de/fdroid/repo)**: [apt.izzysoft.de/fdroid/index/apk/org.joinmastodon.android.sk](https://apt.izzysoft.de/fdroid/index/apk/org.joinmastodon.android.sk)
[apt.izzysoft.de/fdroid/index/apk/org.joinmastodon.android.sk](https://apt.izzysoft.de/fdroid/index/apk/org.joinmastodon.android.sk)
<a href="#installation"><img height="50" alt="Get it on IzzyOnDroid" src="img/izzy-badge.png"></a>
Note that you'll need to add Izzy's F-Droid repository to your F-Droid app first:
`https://apt.izzysoft.de/fdroid/repo`
[`https://apt.izzysoft.de/fdroid/repo`](https://apt.izzysoft.de/fdroid/repo)
* **[Google Play Store](https://play.google.com/store/apps/details?id=org.joinmastodon.android.sk)**: [play.google.com/store/apps/details?id=org.joinmastodon.android.sk](https://play.google.com/store/apps/details?id=org.joinmastodon.android.sk)
### Google Play Store
* **[F-Droid.org](https://f-droid.org)?** Not yet, sorry!
[play.google.com/store/apps/details?id=org.joinmastodon.android.sk](https://play.google.com/store/apps/details?id=org.joinmastodon.android.sk)
<a href="https://play.google.com/store/apps/details?id=org.joinmastodon.android.sk"><img height="50" alt="Get it on Google Play" src="img/google-play-badge.png"></a>
### F-Droid
**[F-Droid.org?](https://f-droid.org)** Not yet, sorry!
If you want, you can help me figure out if something's missing in the [Issue #47: F-Droid.org](https://github.com/sk22/megalodon/issues/47)
### Directly from GitHub
### Direct
Press the download button to download the APK. Open the downloaded file on your Android device to install it. Megalodon will automatically notify you about new updates inside the app.
@@ -127,13 +141,14 @@ There's also a handful of custom strings exclusive to this projects that would n
* [Add notifications tab for posts](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/posts-notifications-tab)
* [Show visibility of original post when replying](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/display-reply-visibility)
* [Clickable reply/boost line above posts](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:clickable-boost-reply-line)
* [Clickable reply line while replying to open original post](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/clickable-reply-line-compose)
* [Add push notification setting for post notifications](https://github.com/sk22/megalodon/commit/b190480d7739be47f23543d9e7644660f9b4b4ee)
* [Add option to allow voting for multiple options on polls](https://github.com/sk22/megalodon/commit/5b28468efd49387b4f8b83f142f3adf3104ca60c)
* [Add translate function](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/translate-button)
* [Add language selector](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/language-selector)
* [Implement deleting notifications](https://github.com/sk22/megalodon/commit/b0f9ce081f69f29ad59658fc00ca41372cd2677d) (disabled by default)
* [Long-click boost button to "quote" a post](https://github.com/sk22/megalodon/commit/b25a237c20c6a924ed4d9b357999867c3a32b32b)
* [Draft and schedule posts](https://github.com/sk22/megalodon/pull/217)
* [Display original post when replying](https://github.com/sk22/megalodon/commit/375f8ceb2747705fedf43686681cc0e0b812f899)
### Behavior

BIN
img/izzy-badge.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

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

View File

@@ -12,7 +12,6 @@ import android.widget.Toast;
import org.joinmastodon.android.api.session.AccountSession;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.fragments.ComposeFragment;
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
import org.joinmastodon.android.ui.utils.UiUtils;
import java.util.ArrayList;
@@ -36,13 +35,10 @@ public class ExternalShareActivity extends FragmentStackActivity{
openComposeFragment(sessions.get(0).getID());
}else{
getWindow().setBackgroundDrawable(new ColorDrawable(0xff000000));
new M3AlertDialogBuilder(this)
.setItems(sessions.stream().map(as->"@"+as.self.username+"@"+as.domain).toArray(String[]::new), (dialog, which)->{
openComposeFragment(sessions.get(which).getID());
})
.setTitle(R.string.choose_account)
.setOnCancelListener(dialog -> finish())
.show();
UiUtils.pickAccount(this, null, R.string.choose_account, 0,
session -> openComposeFragment(session.getID()),
b -> b.setOnCancelListener(d -> finish())
);
}
}
}

View File

@@ -28,6 +28,8 @@ public class GlobalUserPreferences{
public static boolean voteButtonForSingleChoice;
public static boolean enableDeleteNotifications;
public static boolean translateButtonOpenedOnly;
public static boolean uniformNotificationIcon;
public static boolean reduceMotion;
public static String publishButtonText;
public static ThemePreference theme;
public static ColorPreference color;
@@ -60,6 +62,8 @@ public class GlobalUserPreferences{
voteButtonForSingleChoice=prefs.getBoolean("voteButtonForSingleChoice", true);
enableDeleteNotifications=prefs.getBoolean("enableDeleteNotifications", false);
translateButtonOpenedOnly=prefs.getBoolean("translateButtonOpenedOnly", false);
uniformNotificationIcon=prefs.getBoolean("uniformNotificationIcon", false);
reduceMotion=prefs.getBoolean("reduceMotion", false);
publishButtonText=prefs.getString("publishButtonText", "");
theme=ThemePreference.values()[prefs.getInt("theme", 0)];
recentLanguages=fromJson(prefs.getString("recentLanguages", "{}"), recentLanguagesType, new HashMap<>());
@@ -87,6 +91,8 @@ public class GlobalUserPreferences{
.putBoolean("disableSwipe", disableSwipe)
.putBoolean("enableDeleteNotifications", enableDeleteNotifications)
.putBoolean("translateButtonOpenedOnly", translateButtonOpenedOnly)
.putBoolean("uniformNotificationIcon", uniformNotificationIcon)
.putBoolean("reduceMotion", reduceMotion)
.putString("publishButtonText", publishButtonText)
.putInt("theme", theme.ordinal())
.putString("color", color.name())

View File

@@ -8,9 +8,7 @@ import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.os.Build;
import android.os.Bundle;
import android.text.TextUtils;
@@ -137,20 +135,22 @@ public class PushNotificationReceiver extends BroadcastReceiver{
builder.setContentTitle(pn.title)
.setContentText(pn.body)
.setStyle(new Notification.BigTextStyle().bigText(pn.body))
.setSmallIcon(R.drawable.ic_ntf_logo)
.setContentIntent(PendingIntent.getActivity(context, accountID.hashCode() & 0xFFFF, contentIntent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT))
.setWhen(notification==null ? System.currentTimeMillis() : notification.createdAt.toEpochMilli())
.setShowWhen(true)
.setCategory(Notification.CATEGORY_SOCIAL)
.setAutoCancel(true)
.setColor(UiUtils.getThemeColor(context, R.attr.colorPrimary700));
switch (pn.notificationType) {
.setColor(context.getColor(R.color.primary_700));
if (!GlobalUserPreferences.uniformNotificationIcon) switch (pn.notificationType) {
case FAVORITE -> builder.setSmallIcon(R.drawable.ic_fluent_star_24_filled);
case REBLOG -> builder.setSmallIcon(R.drawable.ic_fluent_arrow_repeat_all_24_filled);
case FOLLOW -> builder.setSmallIcon(R.drawable.ic_fluent_person_add_24_filled);
case MENTION -> builder.setSmallIcon(R.drawable.ic_fluent_mention_24_filled);
case POLL -> builder.setSmallIcon(R.drawable.ic_fluent_poll_24_filled);
default -> builder.setSmallIcon(R.drawable.ic_ntf_logo);
}
if(avatar!=null){
builder.setLargeIcon(UiUtils.getBitmapFromDrawable(avatar));
}

View File

@@ -19,12 +19,18 @@ import me.grishka.appkit.api.ErrorResponse;
public class StatusInteractionController{
private final String accountID;
private final boolean updateCounters;
private final HashMap<String, SetStatusFavorited> runningFavoriteRequests=new HashMap<>();
private final HashMap<String, SetStatusReblogged> runningReblogRequests=new HashMap<>();
private final HashMap<String, SetStatusBookmarked> runningBookmarkRequests=new HashMap<>();
public StatusInteractionController(String accountID){
public StatusInteractionController(String accountID, boolean updateCounters) {
this.accountID=accountID;
this.updateCounters=updateCounters;
}
public StatusInteractionController(String accountID){
this(accountID, true);
}
public void setFavorited(Status status, boolean favorited, Consumer<Status> cb){
@@ -42,7 +48,7 @@ public class StatusInteractionController{
runningFavoriteRequests.remove(status.id);
result.favouritesCount = Math.max(0, status.favouritesCount) + (favorited ? 1 : -1);
cb.accept(result);
E.post(new StatusCountersUpdatedEvent(result));
if (updateCounters) E.post(new StatusCountersUpdatedEvent(result));
}
@Override
@@ -51,13 +57,13 @@ public class StatusInteractionController{
error.showToast(MastodonApp.context);
status.favourited=!favorited;
cb.accept(status);
E.post(new StatusCountersUpdatedEvent(status));
if (updateCounters) E.post(new StatusCountersUpdatedEvent(status));
}
})
.exec(accountID);
runningFavoriteRequests.put(status.id, req);
status.favourited=favorited;
E.post(new StatusCountersUpdatedEvent(status));
if (updateCounters) E.post(new StatusCountersUpdatedEvent(status));
}
public void setReblogged(Status status, boolean reblogged, StatusPrivacy visibility, Consumer<Status> cb){
@@ -76,7 +82,7 @@ public class StatusInteractionController{
runningReblogRequests.remove(status.id);
result.reblogsCount = Math.max(0, status.reblogsCount) + (reblogged ? 1 : -1);
cb.accept(result);
E.post(new StatusCountersUpdatedEvent(result));
if (updateCounters) E.post(new StatusCountersUpdatedEvent(result));
}
@Override
@@ -85,13 +91,13 @@ public class StatusInteractionController{
error.showToast(MastodonApp.context);
status.reblogged=!reblogged;
cb.accept(status);
E.post(new StatusCountersUpdatedEvent(status));
if (updateCounters) E.post(new StatusCountersUpdatedEvent(status));
}
})
.exec(accountID);
runningReblogRequests.put(status.id, req);
status.reblogged=reblogged;
E.post(new StatusCountersUpdatedEvent(status));
if (updateCounters) E.post(new StatusCountersUpdatedEvent(status));
}
public void setBookmarked(Status status, boolean bookmarked){
@@ -112,7 +118,7 @@ public class StatusInteractionController{
public void onSuccess(Status result){
runningBookmarkRequests.remove(status.id);
cb.accept(result);
E.post(new StatusCountersUpdatedEvent(result));
if (updateCounters) E.post(new StatusCountersUpdatedEvent(result));
}
@Override
@@ -121,12 +127,12 @@ public class StatusInteractionController{
error.showToast(MastodonApp.context);
status.bookmarked=!bookmarked;
cb.accept(status);
E.post(new StatusCountersUpdatedEvent(status));
if (updateCounters) E.post(new StatusCountersUpdatedEvent(status));
}
})
.exec(accountID);
runningBookmarkRequests.put(status.id, req);
status.bookmarked=bookmarked;
E.post(new StatusCountersUpdatedEvent(status));
if (updateCounters) E.post(new StatusCountersUpdatedEvent(status));
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,12 +1,16 @@
package org.joinmastodon.android.fragments;
import static org.joinmastodon.android.GlobalUserPreferences.recentLanguages;
import static org.joinmastodon.android.api.requests.statuses.CreateStatus.DRAFTS_AFTER_INSTANT;
import static org.joinmastodon.android.api.requests.statuses.CreateStatus.getDraftInstant;
import static org.joinmastodon.android.utils.MastodonLanguage.allLanguages;
import static org.joinmastodon.android.utils.MastodonLanguage.defaultRecentLanguages;
import android.animation.ObjectAnimator;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.DatePickerDialog;
import android.app.TimePickerDialog;
import android.content.ClipData;
import android.content.Context;
import android.content.Intent;
@@ -29,11 +33,12 @@ import android.provider.OpenableColumns;
import android.text.Editable;
import android.text.InputFilter;
import android.text.Layout;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.text.format.DateFormat;
import android.util.Log;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.Menu;
@@ -55,6 +60,7 @@ import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.PopupMenu;
import android.widget.ProgressBar;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;
@@ -67,13 +73,15 @@ import org.joinmastodon.android.R;
import org.joinmastodon.android.api.MastodonAPIController;
import org.joinmastodon.android.api.MastodonErrorResponse;
import org.joinmastodon.android.api.ProgressListener;
import org.joinmastodon.android.api.requests.accounts.GetPreferences;
import org.joinmastodon.android.api.requests.statuses.CreateStatus;
import org.joinmastodon.android.api.requests.statuses.DeleteStatus;
import org.joinmastodon.android.api.requests.statuses.EditStatus;
import org.joinmastodon.android.api.requests.statuses.GetAttachmentByID;
import org.joinmastodon.android.api.requests.statuses.UploadAttachment;
import org.joinmastodon.android.api.session.AccountSession;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.events.ScheduledStatusCreatedEvent;
import org.joinmastodon.android.events.ScheduledStatusDeletedEvent;
import org.joinmastodon.android.events.StatusCountersUpdatedEvent;
import org.joinmastodon.android.events.StatusCreatedEvent;
import org.joinmastodon.android.events.StatusUpdatedEvent;
@@ -85,6 +93,7 @@ import org.joinmastodon.android.model.Instance;
import org.joinmastodon.android.model.Mention;
import org.joinmastodon.android.model.Poll;
import org.joinmastodon.android.model.Preferences;
import org.joinmastodon.android.model.ScheduledStatus;
import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.model.StatusPrivacy;
import org.joinmastodon.android.ui.ComposeAutocompleteViewController;
@@ -100,6 +109,7 @@ import org.joinmastodon.android.ui.utils.TransferSpeedTracker;
import org.joinmastodon.android.ui.utils.UiUtils;
import org.joinmastodon.android.ui.views.ComposeEditText;
import org.joinmastodon.android.ui.views.ComposeMediaLayout;
import org.joinmastodon.android.ui.views.LinkedTextView;
import org.joinmastodon.android.ui.views.ReorderableLinearLayout;
import org.joinmastodon.android.ui.views.SizeListenerLinearLayout;
import org.joinmastodon.android.utils.MastodonLanguage;
@@ -109,6 +119,12 @@ import org.parceler.Parcels;
import java.io.InterruptedIOException;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
@@ -131,6 +147,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
private static final int MEDIA_RESULT=717;
private static final int IMAGE_DESCRIPTION_RESULT=363;
private static final int SCHEDULED_STATUS_OPENED_RESULT=161;
private static final int MAX_ATTACHMENTS=4;
private static final String TAG="ComposeFragment";
@@ -154,9 +171,9 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
private String accountID;
private int charCount, charLimit, trimmedCharCount;
private Button publishButton, languageButton;
private PopupMenu languagePopup, visibilityPopup;
private ImageButton mediaBtn, pollBtn, emojiBtn, spoilerBtn, visibilityBtn;
private Button publishButton, languageButton, scheduleTimeBtn, draftsBtn;
private PopupMenu languagePopup, visibilityPopup, draftOptionsPopup;
private ImageButton mediaBtn, pollBtn, emojiBtn, spoilerBtn, visibilityBtn, scheduleDraftDismiss;
private ImageView sensitiveIcon;
private ComposeMediaLayout attachmentsView;
private TextView replyText;
@@ -165,8 +182,13 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
private View addPollOptionBtn;
private View sensitiveItem;
private View pollAllowMultipleItem;
private View scheduleDraftView;
private ScrollView scrollView;
private boolean initiallyScrolled = false;
private TextView scheduleDraftText;
private CheckBox pollAllowMultipleCheckbox;
private TextView pollDurationView;
private MenuItem draftMenuItem, undraftMenuItem, scheduleMenuItem, unscheduleMenuItem;
private ArrayList<DraftPollOption> pollOptions=new ArrayList<>();
@@ -182,6 +204,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
private EditText spoilerEdit;
private boolean hasSpoiler;
private boolean sensitive;
private Instant scheduledAt = null;
private ProgressBar sendProgress;
private ImageView sendError;
private View sendingOverlay;
@@ -194,6 +217,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
private boolean attachmentsErrorShowing;
private Status editingStatus;
private ScheduledStatus scheduledStatus;
private boolean redraftStatus;
private boolean pollChanged;
private boolean creatingView;
@@ -203,14 +227,10 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
private String language;
private MastodonLanguage.LanguageResolver languageResolver;
private int navigationBarColorBefore;
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setRetainInstance(true);
navigationBarColorBefore = getActivity().getWindow().getNavigationBarColor();
getActivity().getWindow().setNavigationBarColor(UiUtils.getThemeColor(getActivity(), R.attr.colorBackgroundLightest));
accountID=getArguments().getString("account");
AccountSession session=AccountSessionManager.getInstance().getAccount(accountID);
@@ -219,9 +239,9 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
customEmojis=AccountSessionManager.getInstance().getCustomEmojis(instanceDomain);
instance=AccountSessionManager.getInstance().getInstanceInfo(instanceDomain);
languageResolver=new MastodonLanguage.LanguageResolver(instance);
redraftStatus=getArguments().getBoolean("redraftStatus", false);
if(getArguments().containsKey("editStatus")){
editingStatus=Parcels.unwrap(getArguments().getParcelable("editStatus"));
redraftStatus=getArguments().getBoolean("redraftStatus");
}
if(instance==null){
Nav.finish(this);
@@ -231,6 +251,10 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
AccountSessionManager.getInstance().updateInstanceInfo(instanceDomain);
}
Bundle bundle = savedInstanceState != null ? savedInstanceState : getArguments();
if (bundle.containsKey("scheduledStatus")) scheduledStatus=Parcels.unwrap(bundle.getParcelable("scheduledStatus"));
if (bundle.containsKey("scheduledAt")) scheduledAt=(Instant) bundle.getSerializable("scheduledAt");
if(instance.maxTootChars>0)
charLimit=instance.maxTootChars;
else if(instance.configuration!=null && instance.configuration.statuses!=null && instance.configuration.statuses.maxCharacters>0)
@@ -250,7 +274,6 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
UiUtils.removeCallbacks(updateUploadEtaRunnable);
updateUploadEtaRunnable=null;
}
getActivity().getWindow().setNavigationBarColor(navigationBarColorBefore);
}
@Override
@@ -272,10 +295,11 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
mainEditTextWrap=view.findViewById(R.id.toot_text_wrap);
charCounter=view.findViewById(R.id.char_counter);
charCounter.setText(String.valueOf(charLimit));
scrollView=view.findViewById(R.id.scroll_view);
selfName=view.findViewById(R.id.name);
selfUsername=view.findViewById(R.id.username);
selfAvatar=view.findViewById(R.id.avatar);
selfName=view.findViewById(R.id.self_name);
selfUsername=view.findViewById(R.id.self_username);
selfAvatar=view.findViewById(R.id.self_avatar);
HtmlParser.setTextWithCustomEmoji(selfName, self.displayName, self.emojis);
selfUsername.setText('@'+self.username+'@'+instanceDomain);
ViewImageLoader.load(selfAvatar, null, new UrlImageLoaderRequest(self.avatar));
@@ -293,6 +317,10 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
emojiBtn=view.findViewById(R.id.btn_emoji);
spoilerBtn=view.findViewById(R.id.btn_spoiler);
visibilityBtn=view.findViewById(R.id.btn_visibility);
scheduleDraftView=view.findViewById(R.id.schedule_draft_view);
scheduleDraftText=view.findViewById(R.id.schedule_draft_text);
scheduleDraftDismiss=view.findViewById(R.id.schedule_draft_dismiss);
scheduleTimeBtn=view.findViewById(R.id.scheduled_time_btn);
sensitiveIcon=view.findViewById(R.id.sensitive_icon);
sensitiveItem=view.findViewById(R.id.sensitive_item);
replyText=view.findViewById(R.id.reply_text);
@@ -304,6 +332,10 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
buildVisibilityPopup(visibilityBtn);
visibilityBtn.setOnClickListener(v->visibilityPopup.show());
visibilityBtn.setOnTouchListener(visibilityPopup.getDragToOpenListener());
scheduleDraftDismiss.setOnClickListener(v->updateScheduledAt(null));
scheduleTimeBtn.setOnClickListener(v->pickScheduledDateTime());
sensitiveItem.setOnClickListener(v->toggleSensitive());
emojiKeyboard.setOnIconChangedListener(new PopupKeyboard.OnIconChangeListener(){
@Override
@@ -353,8 +385,12 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
DraftPollOption opt=createDraftPollOption();
opt.edit.setText(eopt.title);
}
pollDuration=(int)editingStatus.poll.expiresAt.minus(System.currentTimeMillis(), ChronoUnit.MILLIS).getEpochSecond();
pollDurationStr=UiUtils.formatTimeLeft(getActivity(), editingStatus.poll.expiresAt);
pollDuration=scheduledStatus == null
? (int)editingStatus.poll.expiresAt.minus(System.currentTimeMillis(), ChronoUnit.MILLIS).getEpochSecond()
: Integer.parseInt(scheduledStatus.params.poll.expiresIn);
pollDurationStr=UiUtils.formatTimeLeft(getActivity(), scheduledStatus == null
? editingStatus.poll.expiresAt
: Instant.now().plus(pollDuration, ChronoUnit.SECONDS));
updatePollOptionHints();
pollDurationView.setText(getString(R.string.compose_poll_duration, pollDurationStr));
}else{
@@ -440,6 +476,8 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
outState.putParcelableArrayList("attachments", serializedAttachments);
}
outState.putSerializable("visibility", statusVisibility);
if (scheduledAt != null) outState.putSerializable("scheduledAt", scheduledAt);
if (scheduledStatus != null) outState.putParcelable("scheduledStatus", Parcels.wrap(scheduledStatus));
}
@Override
@@ -522,6 +560,77 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
});
spoilerEdit.addTextChangedListener(new SimpleTextWatcher(e->updateCharCounter()));
if(replyTo!=null){
View replyWrap = view.findViewById(R.id.reply_wrap);
scrollView.getViewTreeObserver().addOnGlobalLayoutListener(() -> {
int scrollHeight = scrollView.getHeight();
if (replyWrap.getMinimumHeight() != scrollHeight) {
replyWrap.setMinimumHeight(scrollHeight);
if (!initiallyScrolled) {
initiallyScrolled = true;
scrollView.post(() -> {
int bottom = scrollView.getChildAt(0).getBottom();
int delta = bottom - (scrollView.getScrollY() + scrollView.getHeight());
int space = GlobalUserPreferences.reduceMotion ? 0 : Math.min(V.dp(120), delta);
scrollView.scrollBy(0, delta - space);
if (!GlobalUserPreferences.reduceMotion) {
scrollView.postDelayed(() -> scrollView.smoothScrollBy(0, space), 100);
}
});
}
}
});
View originalPost = view.findViewById(R.id.original_post);
originalPost.setVisibility(View.VISIBLE);
originalPost.setOnClickListener(v->{
Bundle args=new Bundle();
args.putString("account", accountID);
args.putParcelable("status", Parcels.wrap(replyTo));
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
Nav.go(getActivity(), ThreadFragment.class, args);
});
ImageView avatar = view.findViewById(R.id.avatar);
ViewImageLoader.load(avatar, null, new UrlImageLoaderRequest(replyTo.account.avatar));
ViewOutlineProvider roundCornersOutline=new ViewOutlineProvider(){
@Override
public void getOutline(View view, Outline outline){
outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), V.dp(12));
}
};
avatar.setOutlineProvider(roundCornersOutline);
avatar.setClipToOutline(true);
avatar.setOnClickListener(v->{
Bundle args=new Bundle();
args.putString("account", accountID);
args.putParcelable("profileAccount", Parcels.wrap(replyTo.account));
Nav.go(getActivity(), ProfileFragment.class, args);
});
((TextView) view.findViewById(R.id.name)).setText(replyTo.account.displayName);
((TextView) view.findViewById(R.id.username)).setText(replyTo.account.getDisplayUsername());
view.findViewById(R.id.visibility).setVisibility(View.GONE);
Drawable visibilityIcon = getActivity().getDrawable(switch(replyTo.visibility){
case PUBLIC -> R.drawable.ic_fluent_earth_20_regular;
case UNLISTED -> R.drawable.ic_fluent_people_community_20_regular;
case PRIVATE -> R.drawable.ic_fluent_people_checkmark_20_regular;
case DIRECT -> R.drawable.ic_fluent_mention_20_regular;
});
ImageView moreBtn = view.findViewById(R.id.more);
moreBtn.setImageDrawable(visibilityIcon);
moreBtn.setBackground(null);
TextView timestamp = view.findViewById(R.id.timestamp);
if (replyTo.editedAt==null) timestamp.setText(UiUtils.formatRelativeTimestamp(getContext(), replyTo.createdAt));
else timestamp.setText(getString(R.string.edited_timestamp, UiUtils.formatRelativeTimestamp(getContext(), replyTo.editedAt)));
if (replyTo.spoilerText != null && !replyTo.spoilerText.isBlank()) {
view.findViewById(R.id.spoiler_header).setVisibility(View.VISIBLE);
((TextView) view.findViewById(R.id.spoiler_title_inline)).setText(replyTo.spoilerText);
}
SpannableStringBuilder content = HtmlParser.parse(replyTo.content, replyTo.emojis, replyTo.mentions, replyTo.tags, accountID);
LinkedTextView text = view.findViewById(R.id.text);
if (content.length() > 0) text.setText(content);
else view.findViewById(R.id.display_item_text).setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, V.dp(16)));
replyText.setText(getString(R.string.in_reply_to, replyTo.account.displayName));
int visibilityNameRes = switch (replyTo.visibility) {
case PUBLIC -> R.string.visibility_public;
@@ -530,24 +639,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
case DIRECT -> R.string.visibility_private;
};
replyText.setContentDescription(getString(R.string.in_reply_to, replyTo.account.displayName) + ". " + getString(R.string.post_visibility) + ": " + getString(visibilityNameRes));
Drawable visibilityIcon = getActivity().getDrawable(switch(replyTo.visibility){
case PUBLIC -> R.drawable.ic_fluent_earth_20_regular;
case UNLISTED -> R.drawable.ic_fluent_people_community_20_regular;
case PRIVATE -> R.drawable.ic_fluent_people_checkmark_20_regular;
case DIRECT -> R.drawable.ic_fluent_mention_20_regular;
});
visibilityIcon.setBounds(0, 0, V.dp(20), V.dp(20));
Drawable replyArrow = getActivity().getDrawable(R.drawable.ic_fluent_arrow_reply_20_filled);
replyArrow.setBounds(0, 0, V.dp(20), V.dp(20));
replyText.setCompoundDrawables(replyArrow, null, visibilityIcon, null);
replyText.setOnClickListener(v->{
Bundle args=new Bundle();
args.putString("account", accountID);
args.putParcelable("status", Parcels.wrap(replyTo));
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
Nav.go(getActivity(), ThreadFragment.class, args);
});
ArrayList<String> mentions=new ArrayList<>();
String ownID=AccountSessionManager.getInstance().getAccount(accountID).self.id;
if(!replyTo.account.id.equals(ownID))
@@ -631,49 +723,59 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater){
publishButton=new Button(getActivity());
int publishText = editingStatus==null || redraftStatus ? R.string.publish : R.string.save;
if (publishText == R.string.publish && !GlobalUserPreferences.publishButtonText.isEmpty()) {
publishButton.setText(GlobalUserPreferences.publishButtonText);
} else {
publishButton.setText(publishText);
}
publishButton.setSingleLine();
publishButton.setEllipsize(TextUtils.TruncateAt.END);
publishButton.setOnClickListener(this::onPublishClick);
LinearLayout wrap=new LinearLayout(getActivity());
wrap.setOrientation(LinearLayout.HORIZONTAL);
sendProgress=new ProgressBar(getActivity());
LinearLayout.LayoutParams progressLP=new LinearLayout.LayoutParams(V.dp(24), V.dp(24));
progressLP.setMarginEnd(V.dp(16));
progressLP.gravity=Gravity.CENTER_VERTICAL;
wrap.addView(sendProgress, progressLP);
sendError=new ImageView(getActivity());
sendError.setImageResource(R.drawable.ic_fluent_error_circle_24_regular);
sendError.setImageTintList(getResources().getColorStateList(R.color.error_600));
sendError.setScaleType(ImageView.ScaleType.CENTER);
wrap.addView(sendError, progressLP);
sendError.setVisibility(View.GONE);
sendProgress.setVisibility(View.GONE);
LinearLayout.LayoutParams langParams=new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
langParams.setMarginEnd(V.dp(8));
wrap.addView(buildLanguageSelector(), langParams);
wrap.addView(publishButton, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
wrap.setPadding(V.dp(16), V.dp(4), V.dp(16), V.dp(8));
wrap.setClipToPadding(false);
MenuItem item=menu.add(editingStatus==null ? R.string.publish : R.string.save);
LinearLayout wrap=new LinearLayout(getActivity());
getActivity().getLayoutInflater().inflate(R.layout.compose_action, wrap);
item.setActionView(wrap);
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
updatePublishButtonState();
draftsBtn = wrap.findViewById(R.id.drafts_btn);
draftOptionsPopup = new PopupMenu(getContext(), draftsBtn);
draftOptionsPopup.inflate(R.menu.compose_more);
draftMenuItem = draftOptionsPopup.getMenu().findItem(R.id.draft);
undraftMenuItem = draftOptionsPopup.getMenu().findItem(R.id.undraft);
scheduleMenuItem = draftOptionsPopup.getMenu().findItem(R.id.schedule);
unscheduleMenuItem = draftOptionsPopup.getMenu().findItem(R.id.unschedule);
draftOptionsPopup.setOnMenuItemClickListener(i->{
int id = i.getItemId();
if (id == R.id.draft) updateScheduledAt(getDraftInstant());
else if (id == R.id.schedule) pickScheduledDateTime();
else if (id == R.id.unschedule || id == R.id.undraft) updateScheduledAt(null);
else navigateToUnsentPosts();
return true;
});
UiUtils.enablePopupMenuIcons(getContext(), draftOptionsPopup);
publishButton = wrap.findViewById(R.id.publish_btn);
languageButton = wrap.findViewById(R.id.language_btn);
sendProgress = wrap.findViewById(R.id.send_progress);
sendError = wrap.findViewById(R.id.send_error);
publishButton.setOnClickListener(this::onPublishClick);
draftsBtn.setOnClickListener(v-> draftOptionsPopup.show());
draftsBtn.setOnTouchListener(draftOptionsPopup.getDragToOpenListener());
updateScheduledAt(scheduledAt != null ? scheduledAt : scheduledStatus != null ? scheduledStatus.scheduledAt : null);
buildLanguageSelector(languageButton);
}
private void navigateToUnsentPosts() {
Bundle args=new Bundle();
args.putString("account", accountID);
args.putBoolean("hide_fab", true);
InputMethodManager imm=getActivity().getSystemService(InputMethodManager.class);
imm.hideSoftInputFromWindow(draftsBtn.getWindowToken(), 0);
if (hasDraft()) {
Nav.go(getActivity(), ScheduledStatusListFragment.class, args);
} else {
// result for the previous ScheduledStatusList
setResult(true, null);
// finishing fragment in "onFragmentResult"
Nav.goForResult(getActivity(), ScheduledStatusListFragment.class, args, SCHEDULED_STATUS_OPENED_RESULT, this);
}
}
private void updateLanguage(String lang) {
updateLanguage(languageResolver.from(lang));
updateLanguage(lang == null ? languageResolver.getDefault() : languageResolver.from(lang));
}
private void updateLanguage(MastodonLanguage loc) {
@@ -683,18 +785,10 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
}
@SuppressLint("ClickableViewAccessibility")
private Button buildLanguageSelector() {
languageButton=new Button(getActivity());
languageButton.setTextColor(UiUtils.getThemeColor(getActivity(), android.R.attr.textColorSecondary));
languageButton.setBackground(getActivity().getDrawable(R.drawable.bg_text_button));
languageButton.setPadding(V.dp(8), 0, V.dp(8), 0);
languageButton.setCompoundDrawablesRelativeWithIntrinsicBounds(getActivity().getDrawable(R.drawable.ic_fluent_local_language_16_regular), null, null, null);
languageButton.setCompoundDrawableTintList(languageButton.getTextColors());
languageButton.setCompoundDrawablePadding(V.dp(6));
private void buildLanguageSelector(Button btn) {
languagePopup=new PopupMenu(getActivity(), languageButton);
languageButton.setOnTouchListener(languagePopup.getDragToOpenListener());
languageButton.setOnClickListener(v->languagePopup.show());
btn.setOnTouchListener(languagePopup.getDragToOpenListener());
btn.setOnClickListener(v->languagePopup.show());
Preferences prefs = AccountSessionManager.getInstance().getAccount(accountID).preferences;
updateLanguage(prefs != null && prefs.postingDefaultLanguage != null && prefs.postingDefaultLanguage.length() > 0
@@ -718,8 +812,6 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
updateLanguage(allLanguages.get(i.getItemId()));
return true;
});
return languageButton;
}
@Override
@@ -756,6 +848,15 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
updatePublishButtonState();
}
private void resetPublishButtonText() {
int publishText = editingStatus==null || redraftStatus ? R.string.publish : R.string.save;
if (publishText == R.string.publish && !GlobalUserPreferences.publishButtonText.isEmpty()) {
publishButton.setText(GlobalUserPreferences.publishButtonText);
} else {
publishButton.setText(publishText);
}
}
private void updatePublishButtonState(){
uuid=null;
int nonEmptyPollOptionsCount=0;
@@ -771,6 +872,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
nonDoneAttachmentCount++;
}
publishButton.setEnabled((trimmedCharCount>0 || !attachments.isEmpty()) && charCount<=charLimit && nonDoneAttachmentCount==0 && (pollOptions.isEmpty() || nonEmptyPollOptionsCount>1));
sendError.setVisibility(View.GONE);
}
private void onCustomEmojiClick(Emoji emoji){
@@ -789,6 +891,43 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
publish();
}
private void publishErrorCallback(ErrorResponse error) {
wm.removeView(sendingOverlay);
sendingOverlay=null;
sendProgress.setVisibility(View.GONE);
sendError.setVisibility(View.VISIBLE);
publishButton.setEnabled(true);
if (error != null) error.showToast(getActivity());
}
private void createScheduledStatusFinish(ScheduledStatus result) {
wm.removeView(sendingOverlay);
sendingOverlay=null;
Toast.makeText(getContext(), scheduledAt.isAfter(DRAFTS_AFTER_INSTANT) ?
R.string.sk_draft_saved : R.string.sk_post_scheduled, Toast.LENGTH_SHORT).show();
Nav.finish(ComposeFragment.this);
E.post(new ScheduledStatusCreatedEvent(result, accountID));
}
private void maybeDeleteScheduledPost(Runnable callback) {
if (scheduledStatus != null) {
new DeleteStatus.Scheduled(scheduledStatus.id).setCallback(new Callback<>() {
@Override
public void onSuccess(Object o) {
E.post(new ScheduledStatusDeletedEvent(scheduledStatus.id, accountID));
callback.run();
}
@Override
public void onError(ErrorResponse error) {
publishErrorCallback(error);
}
}).exec(accountID);
} else {
callback.run();
}
}
private void publish(){
String text=mainEditText.getText().toString();
CreateStatus.Request req=new CreateStatus.Request();
@@ -796,6 +935,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
req.visibility=statusVisibility;
req.sensitive=sensitive;
req.language=language;
req.scheduledAt = scheduledAt;
if(!attachments.isEmpty()){
req.mediaIds=attachments.stream().map(a->a.serverAttachment.id).collect(Collectors.toList());
}
@@ -832,6 +972,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
Callback<Status> resCallback=new Callback<>(){
@Override
public void onSuccess(Status result){
maybeDeleteScheduledPost(() -> {
wm.removeView(sendingOverlay);
sendingOverlay=null;
if(editingStatus==null){
@@ -851,16 +992,12 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
if(replyTo!=null) args.putParcelable("inReplyToAccount", Parcels.wrap(replyTo));
Nav.go(getActivity(), ThreadFragment.class, args);
}
});
}
@Override
public void onError(ErrorResponse error){
wm.removeView(sendingOverlay);
sendingOverlay=null;
sendProgress.setVisibility(View.GONE);
sendError.setVisibility(View.VISIBLE);
publishButton.setEnabled(true);
error.showToast(getActivity());
publishErrorCallback(error);
}
};
@@ -868,10 +1005,37 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
new EditStatus(req, editingStatus.id)
.setCallback(resCallback)
.exec(accountID);
}else{
}else if(req.scheduledAt == null){
new CreateStatus(req, uuid)
.setCallback(resCallback)
.exec(accountID);
}else if(req.scheduledAt.isAfter(Instant.now().plus(10, ChronoUnit.MINUTES))){
// checking for 10 instead of 5 minutes (as per mastodon) because i really don't want
// bugs to occur because the client's clock is wrong by a minute or two - the api
// returns a status instead of a scheduled status if scheduled time is less than 5
// minutes into the future and this is 1. unexpected for the user and 2. hard to handle
new CreateStatus.Scheduled(req, uuid)
.setCallback(new Callback<>() {
@Override
public void onSuccess(ScheduledStatus result) {
maybeDeleteScheduledPost(() -> {
createScheduledStatusFinish(result);
});
}
@Override
public void onError(ErrorResponse error) {
publishErrorCallback(error);
}
}).exec(accountID);
}else{
new M3AlertDialogBuilder(getActivity())
.setTitle(R.string.sk_scheduled_too_soon_title)
.setMessage(R.string.sk_scheduled_too_soon)
.setPositiveButton(R.string.ok, (a, b)->{})
.show();
publishErrorCallback(null);
publishButton.setEnabled(false);
}
if (replyTo == null) {
@@ -891,6 +1055,8 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
List<String> existingMediaIDs=editingStatus.mediaAttachments.stream().map(a->a.id).collect(Collectors.toList());
if(!existingMediaIDs.equals(attachments.stream().map(a->a.serverAttachment.id).collect(Collectors.toList())))
return true;
if(!statusVisibility.equals(editingStatus.visibility)) return true;
if(scheduledStatus != null && !scheduledStatus.scheduledAt.equals(scheduledAt)) return true;
return pollChanged;
}
boolean pollFieldsHaveContent=false;
@@ -935,14 +1101,19 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
break;
}
}
} else if (reqCode == SCHEDULED_STATUS_OPENED_RESULT && success && getActivity() != null) {
Nav.finish(this);
}
}
private void confirmDiscardDraftAndFinish(){
new M3AlertDialogBuilder(getActivity())
.setTitle(editingStatus==null ? R.string.discard_draft : R.string.discard_changes)
.setPositiveButton(R.string.discard, (dialog, which)->Nav.finish(this))
.setNegativeButton(R.string.cancel, null)
.setTitle(editingStatus != null ? R.string.sk_confirm_save_changes : R.string.sk_confirm_save_draft)
.setPositiveButton(R.string.save, (d, w) -> {
updateScheduledAt(scheduledAt == null ? getDraftInstant() : scheduledAt);
publish();
})
.setNegativeButton(R.string.discard, (d, w) -> Nav.finish(this))
.show();
}
@@ -1121,7 +1292,10 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
@Override
public void onProgress(long transferred, long total){
if(updateUploadEtaRunnable==null){
UiUtils.runOnUiThread(updateUploadEtaRunnable=ComposeFragment.this::updateUploadETAs, 100);
// getting a NoSuchMethodError: No static method -$$Nest$mupdateUploadETAs(ComposeFragment;)V in class ComposeFragment
// when using method reference out of nowhere after changing code elsewhere. no idea. programming is awful, actually
// noinspection Convert2MethodRef
UiUtils.runOnUiThread(updateUploadEtaRunnable=()->ComposeFragment.this.updateUploadETAs(), 50);
}
int progress=Math.round(transferred/(float)total*attachment.progressBar.getMax());
if(Build.VERSION.SDK_INT>=24)
@@ -1284,7 +1458,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
att.uploadStateText.setText(getString(R.string.file_upload_time_remaining, time));
}
}
UiUtils.runOnUiThread(updateUploadEtaRunnable, 100);
UiUtils.runOnUiThread(updateUploadEtaRunnable, 50);
}
private void onEditMediaDescriptionClick(View v){
@@ -1416,6 +1590,58 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
if (attachments.isEmpty()) sensitive = false;
}
private void pickScheduledDateTime() {
LocalDateTime soon = LocalDateTime.now()
.plus(15, ChronoUnit.MINUTES) // so 14:59 doesn't get rounded up to…
.plus(1, ChronoUnit.HOURS) // …15:00, but rather 16:00
.withMinute(0);
new DatePickerDialog(getActivity(), (datePicker, year, arrayMonth, dayOfMonth) -> {
new TimePickerDialog(getActivity(), (timePicker, hour, minute) -> {
updateScheduledAt(LocalDateTime.of(year, arrayMonth + 1, dayOfMonth, hour, minute)
.toInstant(OffsetDateTime.now().getOffset()));
}, soon.getHour(), soon.getMinute(), DateFormat.is24HourFormat(getActivity())).show();
}, soon.getYear(), soon.getMonthValue() - 1, soon.getDayOfMonth()).show();
}
private void updateScheduledAt(Instant scheduledAt) {
this.scheduledAt = scheduledAt;
updatePublishButtonState();
scheduleDraftView.setVisibility(scheduledAt == null ? View.GONE : View.VISIBLE);
draftMenuItem.setVisible(true);
scheduleMenuItem.setVisible(true);
undraftMenuItem.setVisible(false);
unscheduleMenuItem.setVisible(false);
if (scheduledAt != null) {
DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM).withLocale(Locale.getDefault());
if (scheduledAt.isAfter(DRAFTS_AFTER_INSTANT)) {
draftMenuItem.setVisible(false);
undraftMenuItem.setVisible(true);
scheduleTimeBtn.setVisibility(View.GONE);
scheduleDraftText.setText(R.string.sk_compose_draft);
scheduleDraftText.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_fluent_drafts_20_regular, 0, 0, 0);
scheduleDraftDismiss.setContentDescription(getString(R.string.sk_compose_no_draft));
draftsBtn.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_fluent_drafts_20_filled, 0, 0, 0);
publishButton.setText(scheduledStatus != null && scheduledStatus.scheduledAt.isAfter(DRAFTS_AFTER_INSTANT)
? R.string.save : R.string.sk_draft);
} else {
scheduleMenuItem.setVisible(false);
unscheduleMenuItem.setVisible(true);
String at = scheduledAt.atZone(ZoneId.systemDefault()).format(formatter);
scheduleTimeBtn.setVisibility(View.VISIBLE);
scheduleTimeBtn.setText(at);
scheduleDraftText.setText(R.string.sk_compose_scheduled);
scheduleDraftText.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
scheduleDraftDismiss.setContentDescription(getString(R.string.sk_compose_no_schedule));
draftsBtn.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_fluent_clock_20_filled, 0, 0, 0);
publishButton.setText(scheduledStatus != null && scheduledStatus.scheduledAt.equals(scheduledAt)
? R.string.save : R.string.sk_schedule);
}
} else {
draftsBtn.setCompoundDrawablesWithIntrinsicBounds(R.drawable.ic_fluent_clock_20_regular, 0, 0, 0);
resetPublishButtonText();
}
}
private int getMediaAttachmentsCount(){
return attachments.size();
}

View File

@@ -16,6 +16,7 @@ import org.joinmastodon.android.api.requests.tags.SetHashtagFollowed;
import org.joinmastodon.android.api.requests.timelines.GetHashtagTimeline;
import org.joinmastodon.android.model.Hashtag;
import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.ui.utils.UiUtils;
import java.util.List;
@@ -117,6 +118,7 @@ public class HashtagTimelineFragment extends StatusListFragment{
super.onViewCreated(view, savedInstanceState);
fab=view.findViewById(R.id.fab);
fab.setOnClickListener(this::onFabClick);
fab.setOnLongClickListener(v -> UiUtils.pickAccountForCompose(getActivity(), accountID, '#'+hashtag+' '));
}
private void onFabClick(View v){

View File

@@ -19,9 +19,11 @@ import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toolbar;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.squareup.otto.Subscribe;
import org.joinmastodon.android.E;
@@ -43,12 +45,9 @@ import org.joinmastodon.android.utils.StatusFilterPredicate;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.stream.Collectors;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import me.grishka.appkit.Nav;
import me.grishka.appkit.api.Callback;
import me.grishka.appkit.api.ErrorResponse;
@@ -106,6 +105,8 @@ public class HomeTimelineFragment extends StatusListFragment{
super.onViewCreated(view, savedInstanceState);
fab=view.findViewById(R.id.fab);
fab.setOnClickListener(this::onFabClick);
fab.setOnLongClickListener(v->UiUtils.pickAccountForCompose(getActivity(), accountID));
updateToolbarLogo();
list.addOnScrollListener(new RecyclerView.OnScrollListener(){
@Override
@@ -264,19 +265,15 @@ public class HomeTimelineFragment extends StatusListFragment{
List<StatusDisplayItem> targetList=displayItems.subList(gapPos, gapPos+1);
targetList.clear();
List<Status> insertedPosts=data.subList(gapPostIndex+1, gapPostIndex+1);
List<Filter> filters=AccountSessionManager.getInstance().getAccount(accountID).wordFilters.stream().filter(f->f.context.contains(Filter.FilterContext.HOME)).collect(Collectors.toList());
outer:
StatusFilterPredicate filterPredicate=new StatusFilterPredicate(accountID, Filter.FilterContext.HOME);
for(Status s:result){
if(idsBelowGap.contains(s.id))
break;
for(Filter filter:filters){
if(filter.matches(s)){
continue outer;
}
}
if(filterPredicate.test(s)){
targetList.addAll(buildDisplayItems(s));
insertedPosts.add(s);
}
}
if(targetList.isEmpty()){
// oops. We didn't add new posts, but at least we know there are none.
adapter.notifyItemRemoved(getMainAdapterOffset()+gapPos);

View File

@@ -1,7 +1,6 @@
package org.joinmastodon.android.fragments;
import android.app.Activity;
import android.media.MediaRouter;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;
@@ -12,6 +11,7 @@ import android.widget.ImageButton;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.requests.timelines.GetListTimeline;
import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.ui.utils.UiUtils;
import java.util.List;
@@ -69,6 +69,7 @@ public class ListTimelineFragment extends StatusListFragment {
super.onViewCreated(view, savedInstanceState);
fab=view.findViewById(R.id.fab);
fab.setOnClickListener(this::onFabClick);
fab.setOnLongClickListener(v -> UiUtils.pickAccountForCompose(getActivity(), accountID));
}
private void onFabClick(View v){

View File

@@ -75,6 +75,7 @@ public class NotificationsFragment extends MastodonToolbarFragment implements Sc
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater){
inflater.inflate(R.menu.notifications, menu);
menu.findItem(R.id.clear_notifications).setVisible(GlobalUserPreferences.enableDeleteNotifications);
UiUtils.enableOptionsMenuIcons(getActivity(), menu, R.id.follow_requests);
}
@Override

View File

@@ -79,7 +79,7 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
case FAVORITE -> getString(R.string.user_favorited);
case POLL -> getString(R.string.poll_ended);
};
HeaderStatusDisplayItem titleItem=extraText!=null ? new HeaderStatusDisplayItem(n.id, n.account, n.createdAt, this, accountID, null, extraText, n) : null;
HeaderStatusDisplayItem titleItem=extraText!=null ? new HeaderStatusDisplayItem(n.id, n.account, n.createdAt, this, accountID, null, extraText, n, null) : null;
if(n.status!=null){
ArrayList<StatusDisplayItem> items=StatusDisplayItem.buildItems(this, n.status, accountID, n, knownAccounts, titleItem!=null, titleItem==null, n);
if(titleItem!=null){

View File

@@ -164,6 +164,11 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
}
}
private String getPrefilledText() {
return account == null || AccountSessionManager.getInstance().isSelf(accountID, account)
? null : '@'+account.acct+' ';
}
@Override
public void onAttach(Activity activity){
super.onAttach(activity);
@@ -275,6 +280,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
cover.setOnClickListener(this::onCoverClick);
refreshLayout.setOnRefreshListener(this);
fab.setOnClickListener(this::onFabClick);
fab.setOnLongClickListener(v->UiUtils.pickAccountForCompose(getActivity(), accountID, getPrefilledText()));
if(loaded){
bindHeaderView();
@@ -549,17 +555,25 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
if(relationship==null && !isOwnProfile)
return;
inflater.inflate(isOwnProfile ? R.menu.profile_own : R.menu.profile, menu);
menu.findItem(R.id.share).setTitle(getString(R.string.share_user, account.getDisplayUsername()));
UiUtils.enableOptionsMenuIcons(getActivity(), menu, R.id.bookmarks, R.id.followed_hashtags);
menu.findItem(R.id.share).setTitle(getString(R.string.share_user, account.getShortUsername()));
if(isOwnProfile)
return;
menu.findItem(R.id.mute).setTitle(getString(relationship.muting ? R.string.unmute_user : R.string.mute_user, account.getDisplayUsername()));
menu.findItem(R.id.block).setTitle(getString(relationship.blocking ? R.string.unblock_user : R.string.block_user, account.getDisplayUsername()));
menu.findItem(R.id.report).setTitle(getString(R.string.report_user, account.getDisplayUsername()));
MenuItem mute = menu.findItem(R.id.mute);
mute.setTitle(getString(relationship.muting ? R.string.unmute_user : R.string.mute_user, account.getShortUsername()));
mute.setIcon(relationship.muting ? R.drawable.ic_fluent_speaker_0_24_regular : R.drawable.ic_fluent_speaker_off_24_regular);
UiUtils.insetPopupMenuIcon(getContext(), mute);
menu.findItem(R.id.block).setTitle(getString(relationship.blocking ? R.string.unblock_user : R.string.block_user, account.getShortUsername()));
menu.findItem(R.id.report).setTitle(getString(R.string.report_user, account.getShortUsername()));
MenuItem manageUserLists=menu.findItem(R.id.manage_user_lists);
if(relationship.following) {
menu.findItem(R.id.hide_boosts).setTitle(getString(relationship.showingReblogs ? R.string.hide_boosts_from_user : R.string.show_boosts_from_user, account.getDisplayUsername()));
manageUserLists.setTitle(getString(R.string.sk_lists_with_user, account.getDisplayUsername()));
MenuItem hideBoosts = menu.findItem(R.id.hide_boosts);
hideBoosts.setTitle(getString(relationship.showingReblogs ? R.string.hide_boosts_from_user : R.string.show_boosts_from_user, account.getShortUsername()));
hideBoosts.setIcon(relationship.showingReblogs ? R.drawable.ic_fluent_arrow_repeat_all_off_24_regular : R.drawable.ic_fluent_arrow_repeat_all_24_regular);
UiUtils.insetPopupMenuIcon(getContext(), hideBoosts);
manageUserLists.setTitle(getString(R.string.sk_lists_with_user, account.getShortUsername()));
manageUserLists.setVisible(true);
}else {
menu.findItem(R.id.hide_boosts).setVisible(false);
@@ -628,6 +642,10 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
Bundle args=new Bundle();
args.putString("account", accountID);
Nav.go(getActivity(), FollowedHashtagsFragment.class, args);
}else if(id==R.id.scheduled){
Bundle args=new Bundle();
args.putString("account", accountID);
Nav.go(getActivity(), ScheduledStatusListFragment.class, args);
}
return true;
}
@@ -939,9 +957,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
private void onFabClick(View v){
Bundle args=new Bundle();
args.putString("account", accountID);
if(!AccountSessionManager.getInstance().isSelf(accountID, account)){
args.putString("prefilledText", '@'+account.acct+' ');
}
if(getPrefilledText() != null) args.putString("prefilledText", getPrefilledText());
Nav.go(getActivity(), ComposeFragment.class, args);
}

View File

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

View File

@@ -46,6 +46,7 @@ import org.joinmastodon.android.api.requests.oauth.RevokeOauthToken;
import org.joinmastodon.android.api.session.AccountSession;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.events.SelfUpdateStateChangedEvent;
import org.joinmastodon.android.fragments.onboarding.InstanceRulesFragment;
import org.joinmastodon.android.model.Instance;
import org.joinmastodon.android.model.PushNotification;
import org.joinmastodon.android.model.PushSubscription;
@@ -53,6 +54,7 @@ import org.joinmastodon.android.ui.M3AlertDialogBuilder;
import org.joinmastodon.android.ui.OutlineProviders;
import org.joinmastodon.android.ui.utils.UiUtils;
import org.joinmastodon.android.updater.GithubSelfUpdater;
import org.parceler.Parcels;
import java.util.ArrayList;
import java.util.function.Consumer;
@@ -62,6 +64,8 @@ import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import me.grishka.appkit.Nav;
import me.grishka.appkit.api.Callback;
import me.grishka.appkit.api.ErrorResponse;
import me.grishka.appkit.imageloader.ImageCache;
@@ -91,6 +95,7 @@ public class SettingsFragment extends MastodonToolbarFragment{
accountID=getArguments().getString("account");
AccountSession session=AccountSessionManager.getInstance().getAccount(accountID);
Instance instance = AccountSessionManager.getInstance().getInstanceInfo(session.domain);
String instanceName = UiUtils.getInstanceName(accountID);
if(GithubSelfUpdater.needSelfUpdating()){
GithubSelfUpdater updater=GithubSelfUpdater.getInstance();
@@ -103,10 +108,6 @@ public class SettingsFragment extends MastodonToolbarFragment{
items.add(new HeaderItem(R.string.settings_theme));
items.add(themeItem=new ThemeItem());
items.add(new SwitchItem(R.string.theme_true_black, R.drawable.ic_fluent_dark_theme_24_regular, GlobalUserPreferences.trueBlackTheme, this::onTrueBlackThemeChanged));
items.add(new SwitchItem(R.string.sk_disable_marquee, R.drawable.ic_fluent_text_more_24_regular, GlobalUserPreferences.disableMarquee, i->{
GlobalUserPreferences.disableMarquee=i.checked;
GlobalUserPreferences.save();
}));
items.add(new ButtonItem(R.string.sk_settings_color_palette, R.drawable.ic_fluent_color_24_regular, b->{
PopupMenu popupMenu=new PopupMenu(getActivity(), b, Gravity.CENTER_HORIZONTAL);
popupMenu.inflate(R.menu.color_palettes);
@@ -152,6 +153,18 @@ public class SettingsFragment extends MastodonToolbarFragment{
.show();
});
}));
items.add(new SwitchItem(R.string.sk_settings_uniform_icon_for_notifications, R.drawable.ic_ntf_logo, GlobalUserPreferences.uniformNotificationIcon, i->{
GlobalUserPreferences.uniformNotificationIcon=i.checked;
GlobalUserPreferences.save();
}));
items.add(new SwitchItem(R.string.sk_disable_marquee, R.drawable.ic_fluent_text_more_24_regular, GlobalUserPreferences.disableMarquee, i->{
GlobalUserPreferences.disableMarquee=i.checked;
GlobalUserPreferences.save();
}));
items.add(new SwitchItem(R.string.sk_settings_reduce_motion, R.drawable.ic_fluent_star_emphasis_24_regular, GlobalUserPreferences.reduceMotion, i->{
GlobalUserPreferences.reduceMotion=i.checked;
GlobalUserPreferences.save();
}));
items.add(new HeaderItem(R.string.settings_behavior));
items.add(new SwitchItem(R.string.sk_settings_show_federated_timeline, R.drawable.ic_fluent_earth_24_regular, GlobalUserPreferences.showFederatedTimeline, i->{
@@ -180,16 +193,20 @@ public class SettingsFragment extends MastodonToolbarFragment{
GlobalUserPreferences.save();
needAppRestart=true;
}));
items.add(new SwitchItem(R.string.sk_enable_delete_notifications, R.drawable.ic_fluent_delete_24_regular, GlobalUserPreferences.enableDeleteNotifications, i->{
items.add(new SwitchItem(R.string.sk_enable_delete_notifications, R.drawable.ic_fluent_mail_inbox_dismiss_24_regular, GlobalUserPreferences.enableDeleteNotifications, i->{
GlobalUserPreferences.enableDeleteNotifications=i.checked;
GlobalUserPreferences.save();
needAppRestart=true;
}));
items.add(new SwitchItem(R.string.sk_settings_hide_translate_in_timeline, R.drawable.ic_fluent_translate_24_regular, GlobalUserPreferences.translateButtonOpenedOnly, i->{
items.add(new SwitchItem(R.string.sk_settings_translate_only_opened, R.drawable.ic_fluent_translate_24_regular, GlobalUserPreferences.translateButtonOpenedOnly, i->{
GlobalUserPreferences.translateButtonOpenedOnly=i.checked;
GlobalUserPreferences.save();
needAppRestart=true;
}));
boolean translationAvailable = instance.v2 != null && instance.v2.configuration.translation != null && instance.v2.configuration.translation.enabled;
items.add(new SmallTextItem(getString(translationAvailable ?
R.string.sk_settings_translation_availability_note_available :
R.string.sk_settings_translation_availability_note_unavailable, instanceName)));
items.add(new HeaderItem(R.string.home_timeline));
items.add(new SwitchItem(R.string.sk_settings_show_replies, R.drawable.ic_fluent_chat_multiple_24_regular, GlobalUserPreferences.showReplies, i->{
@@ -220,17 +237,15 @@ public class SettingsFragment extends MastodonToolbarFragment{
items.add(new TextItem(R.string.sk_settings_filters, ()->UiUtils.launchWebBrowser(getActivity(), "https://"+session.domain+"/filters"), R.drawable.ic_fluent_open_24_regular));
items.add(new TextItem(R.string.sk_settings_auth, ()->UiUtils.launchWebBrowser(getActivity(), "https://"+session.domain+"/auth/edit"), R.drawable.ic_fluent_open_24_regular));
String instanceName = UiUtils.getInstanceName(accountID);
items.add(new HeaderItem(instanceName));
items.add(new TextItem(R.string.sk_settings_rules, ()->UiUtils.launchWebBrowser(getActivity(), "https://"+session.domain+"/about"), R.drawable.ic_fluent_open_24_regular));
items.add(new TextItem(R.string.sk_settings_rules, ()->{
Bundle args=new Bundle();
args.putParcelable("instance", Parcels.wrap(instance));
Nav.go(getActivity(), InstanceRulesFragment.class, args);
}, R.drawable.ic_fluent_task_list_ltr_24_regular));
items.add(new TextItem(R.string.settings_tos, ()->UiUtils.launchWebBrowser(getActivity(), "https://"+session.domain+"/terms"), R.drawable.ic_fluent_open_24_regular));
items.add(new TextItem(R.string.settings_privacy_policy, ()->UiUtils.launchWebBrowser(getActivity(), "https://"+session.domain+"/terms"), R.drawable.ic_fluent_open_24_regular));
items.add(new TextItem(R.string.log_out, this::confirmLogOut, R.drawable.ic_fluent_sign_out_24_regular));
boolean translationAvailable = instance.v2 != null && instance.v2.configuration.translation != null && instance.v2.configuration.translation.enabled;
items.add(new SmallTextItem(getString(translationAvailable ?
R.string.sk_settings_translation_availability_note_available :
R.string.sk_settings_translation_availability_note_unavailable, instanceName)));
items.add(new HeaderItem(R.string.sk_settings_about));
items.add(new TextItem(R.string.sk_settings_contribute, ()->UiUtils.launchWebBrowser(getActivity(), "https://github.com/sk22/megalodon"), R.drawable.ic_fluent_open_24_regular));

View File

@@ -5,7 +5,6 @@ import android.view.View;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.requests.statuses.GetStatusContext;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.events.StatusCreatedEvent;
import org.joinmastodon.android.model.Account;
import org.joinmastodon.android.model.Filter;
@@ -17,6 +16,7 @@ import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.TextStatusDisplayItem;
import org.joinmastodon.android.ui.text.HtmlParser;
import org.joinmastodon.android.ui.utils.UiUtils;
import org.joinmastodon.android.utils.StatusFilterPredicate;
import org.parceler.Parcels;
import java.util.Collections;
@@ -92,16 +92,10 @@ public class ThreadFragment extends StatusListFragment{
}
private List<Status> filterStatuses(List<Status> statuses){
List<Filter> filters=AccountSessionManager.getInstance().getAccount(accountID).wordFilters.stream().filter(f->f.context.contains(Filter.FilterContext.THREAD)).collect(Collectors.toList());
if(filters.isEmpty())
return statuses;
return statuses.stream().filter(status->{
for(Filter filter:filters){
if(filter.matches(status))
return false;
}
return true;
}).collect(Collectors.toList());
StatusFilterPredicate statusFilterPredicate=new StatusFilterPredicate(accountID,Filter.FilterContext.THREAD);
return statuses.stream()
.filter(statusFilterPredicate)
.collect(Collectors.toList());
}
@Override

View File

@@ -65,7 +65,7 @@ public class InstanceRulesFragment extends ToolbarFragment{
adapter.addAdapter(new SingleViewRecyclerAdapter(headerView));
adapter.addAdapter(new ItemsAdapter());
list.setAdapter(adapter);
list.addItemDecoration(new DividerItemDecoration(getActivity(), R.attr.colorM3SurfaceVariant, 1, 56, 0, DividerItemDecoration.NOT_FIRST));
list.addItemDecoration(new DividerItemDecoration(getActivity(), R.attr.colorPollVoted, 1, 56, 0, DividerItemDecoration.NOT_FIRST));
btn=view.findViewById(R.id.btn_next);
btn.setOnClickListener(v->onButtonClick());
@@ -77,8 +77,8 @@ public class InstanceRulesFragment extends ToolbarFragment{
@Override
public void onViewCreated(View view, Bundle savedInstanceState){
super.onViewCreated(view, savedInstanceState);
setStatusBarColor(UiUtils.getThemeColor(getActivity(), R.attr.colorM3Background));
view.setBackgroundColor(UiUtils.getThemeColor(getActivity(), R.attr.colorM3Background));
// setStatusBarColor(UiUtils.getThemeColor(getActivity(), R.attr.colorM3Background));
// view.setBackgroundColor(UiUtils.getThemeColor(getActivity(), R.attr.colorM3Background));
}
@Override

View File

@@ -9,6 +9,7 @@ import android.view.ViewGroup;
import android.view.WindowInsets;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Switch;
import android.widget.TextView;
import com.squareup.otto.Subscribe;
@@ -28,15 +29,17 @@ import java.util.ArrayList;
import me.grishka.appkit.Nav;
import me.grishka.appkit.api.Callback;
import me.grishka.appkit.api.ErrorResponse;
import me.grishka.appkit.fragments.ToolbarFragment;
import me.grishka.appkit.utils.V;
public class ReportCommentFragment extends MastodonToolbarFragment{
private String accountID;
private Account reportAccount;
private Button btn;
private View buttonBar;
private View buttonBar, forwardReportItem;
private TextView forwardReportText;
private Switch forwardReportSwitch;
private EditText commentEdit;
private boolean forwardReport;
@Override
public void onCreate(Bundle savedInstanceState){
@@ -77,7 +80,17 @@ public class ReportCommentFragment extends MastodonToolbarFragment{
view.findViewById(R.id.btn_back).setOnClickListener(this::onButtonClick);
buttonBar=view.findViewById(R.id.button_bar);
commentEdit=view.findViewById(R.id.text);
forwardReportSwitch = view.findViewById(R.id.forward_report_switch);
forwardReportItem = view.findViewById(R.id.forward_report);
forwardReportText = view.findViewById(R.id.forward_report_text);
String domain = reportAccount.getDomain();
if (domain == null) {
forwardReportItem.setVisibility(View.GONE);
} else {
forwardReportItem.setOnClickListener(this::onForwardReportClick);
forwardReportText.setText(getActivity().getString(R.string.sk_forward_report_to, domain));
forwardReportSwitch.setChecked(forwardReport = true);
}
return view;
}
@@ -102,7 +115,7 @@ public class ReportCommentFragment extends MastodonToolbarFragment{
ReportReason reason=ReportReason.valueOf(getArguments().getString("reason"));
ArrayList<String> statusIDs=getArguments().getStringArrayList("statusIDs");
ArrayList<String> ruleIDs=getArguments().getStringArrayList("ruleIDs");
new SendReport(reportAccount.id, reason, statusIDs, ruleIDs, v.getId()==R.id.btn_back ? null : commentEdit.getText().toString(), true)
new SendReport(reportAccount.id, reason, statusIDs, ruleIDs, v.getId()==R.id.btn_back ? null : commentEdit.getText().toString(), forwardReport)
.setCallback(new Callback<>(){
@Override
public void onSuccess(Object result){
@@ -123,6 +136,11 @@ public class ReportCommentFragment extends MastodonToolbarFragment{
.exec(accountID);
}
private void onForwardReportClick(View v) {
forwardReport = !forwardReport;
forwardReportSwitch.setChecked(forwardReport);
}
@Subscribe
public void onFinishReportFragments(FinishReportFragmentsEvent ev){
if(ev.reportAccountID.equals(reportAccount.id))

View File

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

View File

@@ -6,12 +6,14 @@ import com.google.gson.annotations.SerializedName;
import org.joinmastodon.android.api.ObjectValidationException;
import org.joinmastodon.android.api.RequiredField;
import org.parceler.Parcel;
import java.time.Instant;
import java.util.EnumSet;
import java.util.List;
import java.util.regex.Pattern;
@Parcel
public class Filter extends BaseModel{
@RequiredField
public String id;
@@ -21,6 +23,7 @@ public class Filter extends BaseModel{
public Instant expiresAt;
public boolean irreversible;
public boolean wholeWord;
public FilterAction filterAction;
@SerializedName("context")
private List<FilterContext> _context;
@@ -76,4 +79,11 @@ public class Filter extends BaseModel{
@SerializedName("thread")
THREAD
}
public enum FilterAction{
@SerializedName("hide")
HIDE,
@SerializedName("warn")
WARN
}
}

View File

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

View File

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

View File

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

View File

@@ -40,6 +40,7 @@ public class Status extends BaseModel implements DisplayItemsParent{
public long favouritesCount;
public long repliesCount;
public Instant editedAt;
public List<FilterResult> filtered;
public String url;
public String inReplyToId;

View File

@@ -4,6 +4,7 @@ import android.app.AlertDialog;
import android.content.Context;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import me.grishka.appkit.utils.V;
@@ -31,8 +32,16 @@ public class M3AlertDialogBuilder extends AlertDialog.Builder{
if(titleID!=0){
View title=alert.findViewById(titleID);
if(title!=null){
int iconID=getContext().getResources().getIdentifier("icon", "id", "android");
int alertTitleID=getContext().getResources().getIdentifier("alertTitle", "id", "android");
if (alertTitleID != 0 && iconID != 0) {
ImageView icon = title.findViewById(iconID);
if (icon.getDrawable() != null) {
title.findViewById(alertTitleID).setPadding(V.dp(8), 0, 0, 0);
}
}
int pad=V.dp(24);
title.setPadding(pad, pad, pad, V.dp(18));
title.setPadding(pad, pad, pad, V.dp(12));
}
}
int titleDividerID=getContext().getResources().getIdentifier("titleDividerNoCustom", "id", "android");

View File

@@ -60,7 +60,14 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
private static final Animation opacityOut, opacityIn;
private View touchingView = null;
private final Runnable longClickRunnable = () -> { if (touchingView != null) touchingView.performLongClick(); };
private boolean longClickPerformed = false;
private final Runnable longClickRunnable = () -> {
longClickPerformed = touchingView != null && touchingView.performLongClick();
if (longClickPerformed && touchingView != null) {
touchingView.startAnimation(opacityIn);
touchingView.animate().scaleX(1).scaleY(1).setInterpolator(CubicBezierInterpolator.DEFAULT).setDuration(150).start();
}
};
private final View.AccessibilityDelegate buttonAccessibilityDelegate=new View.AccessibilityDelegate(){
@Override
@@ -77,7 +84,7 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
opacityOut.setInterpolator(CubicBezierInterpolator.DEFAULT);
opacityOut.setFillAfter(true);
opacityIn = new AlphaAnimation(0.55f, 1);
opacityIn.setDuration(300);
opacityIn.setDuration(400);
opacityIn.setInterpolator(CubicBezierInterpolator.DEFAULT);
}
@@ -101,6 +108,7 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
View bookmark=findViewById(R.id.bookmark_btn);
reply.setOnTouchListener(this::onButtonTouch);
reply.setOnClickListener(this::onReplyClick);
reply.setOnLongClickListener(this::onReplyLongClick);
reply.setAccessibilityDelegate(buttonAccessibilityDelegate);
boost.setOnTouchListener(this::onButtonTouch);
boost.setOnClickListener(this::onBoostClick);
@@ -108,9 +116,11 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
boost.setAccessibilityDelegate(buttonAccessibilityDelegate);
favorite.setOnTouchListener(this::onButtonTouch);
favorite.setOnClickListener(this::onFavoriteClick);
favorite.setOnLongClickListener(this::onFavoriteLongClick);
favorite.setAccessibilityDelegate(buttonAccessibilityDelegate);
bookmark.setOnTouchListener(this::onButtonTouch);
bookmark.setOnClickListener(this::onBookmarkClick);
bookmark.setOnLongClickListener(this::onBookmarkLongClick);
bookmark.setAccessibilityDelegate(buttonAccessibilityDelegate);
share.setOnTouchListener(this::onButtonTouch);
share.setOnClickListener(this::onShareClick);
@@ -123,6 +133,7 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
bindButton(reply, item.status.repliesCount);
bindButton(boost, item.status.reblogsCount);
bindButton(favorite, item.status.favouritesCount);
reply.setSelected(item.status.repliesCount > 0);
boost.setSelected(item.status.reblogged);
favorite.setSelected(item.status.favourited);
bookmark.setSelected(item.status.bookmarked);
@@ -144,15 +155,15 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
boolean disabled = !v.isEnabled() || (v instanceof FrameLayout parentFrame &&
parentFrame.getChildCount() > 0 && !parentFrame.getChildAt(0).isEnabled());
int action = event.getAction();
long eventDuration = event.getEventTime() - event.getDownTime();
if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
touchingView = null;
v.removeCallbacks(longClickRunnable);
v.animate().scaleX(1).scaleY(1).setInterpolator(CubicBezierInterpolator.DEFAULT).setDuration(150).start();
if (!longClickPerformed) v.animate().scaleX(1).scaleY(1).setInterpolator(CubicBezierInterpolator.DEFAULT).setDuration(150).start();
if (disabled) return true;
if (action == MotionEvent.ACTION_UP && eventDuration <= ViewConfiguration.getLongPressTimeout()) v.performClick();
else v.startAnimation(opacityIn);
if (action == MotionEvent.ACTION_UP && !longClickPerformed) v.performClick();
else if (!longClickPerformed) v.startAnimation(opacityIn);
} else if (action == MotionEvent.ACTION_DOWN) {
longClickPerformed = false;
touchingView = v;
// 20dp to center in middle of icon, because: (icon width = 24dp) / 2 + (paddingStart = 8dp)
v.setPivotX(V.dp(20));
@@ -172,6 +183,20 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
Nav.go(item.parentFragment.getActivity(), ComposeFragment.class, args);
}
private boolean onReplyLongClick(View v) {
if (AccountSessionManager.getInstance().getLoggedInAccounts().size() < 2) return false;
UiUtils.pickAccount(v.getContext(), item.accountID, R.string.sk_reply_as, R.drawable.ic_fluent_arrow_reply_28_regular, session -> {
Bundle args=new Bundle();
String accountID = session.getID();
args.putString("account", accountID);
UiUtils.lookupStatus(v.getContext(), item.status, accountID, item.accountID, status -> {
args.putParcelable("replyTo", Parcels.wrap(status));
Nav.go(item.parentFragment.getActivity(), ComposeFragment.class, args);
});
}, null);
return true;
}
private void onBoostClick(View v){
boost.setSelected(!item.status.reblogged);
AccountSessionManager.getInstance().getAccount(item.accountID).getStatusInteractionController().setReblogged(item.status, !item.status.reblogged, null, r->boostConsumer(v, r));
@@ -198,6 +223,7 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
View separator = menu.findViewById(R.id.separator);
TextView reblogHeader = menu.findViewById(R.id.reblog_header);
TextView undoReblog = menu.findViewById(R.id.delete_reblog);
TextView reblogAs = menu.findViewById(R.id.reblog_as);
TextView itemPublic = menu.findViewById(R.id.vis_public);
TextView itemUnlisted = menu.findViewById(R.id.vis_unlisted);
TextView itemFollowers = menu.findViewById(R.id.vis_followers);
@@ -205,6 +231,7 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
undoReblog.setVisibility(item.status.reblogged ? View.VISIBLE : View.GONE);
separator.setVisibility(item.status.reblogged ? View.GONE : View.VISIBLE);
reblogHeader.setVisibility(item.status.reblogged ? View.GONE : View.VISIBLE);
reblogAs.setVisibility(AccountSessionManager.getInstance().getLoggedInAccounts().size() > 1 ? View.VISIBLE : View.GONE);
itemPublic.setVisibility(item.status.reblogged || item.status.visibility.isLessVisibleThan(StatusPrivacy.PUBLIC) ? View.GONE : View.VISIBLE);
itemUnlisted.setVisibility(item.status.reblogged || item.status.visibility.isLessVisibleThan(StatusPrivacy.UNLISTED) ? View.GONE : View.VISIBLE);
@@ -215,10 +242,10 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
Drawable unlistedDrawable = ctx.getDrawable(R.drawable.ic_fluent_people_community_24_regular);
Drawable followersDrawable = ctx.getDrawable(R.drawable.ic_fluent_people_checkmark_24_regular);
StatusPrivacy defaultVisibility = session.preferences.postingDefaultVisibility;
StatusPrivacy defaultVisibility = session.preferences != null ? session.preferences.postingDefaultVisibility : null;
// e.g. post visibility is unlisted, but default is public
// in this case, we want to display the check mark on the most visible visibility
if (item.status.visibility.isLessVisibleThan(defaultVisibility)) {
if (defaultVisibility != null && item.status.visibility.isLessVisibleThan(defaultVisibility)) {
for (StatusPrivacy vis : StatusPrivacy.values()) {
if (vis.equals(item.status.visibility)) {
defaultVisibility = vis;
@@ -234,13 +261,30 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
itemPublic.setOnClickListener(c->doReblog.accept(StatusPrivacy.PUBLIC));
itemUnlisted.setOnClickListener(c->doReblog.accept(StatusPrivacy.UNLISTED));
itemFollowers.setOnClickListener(c->doReblog.accept(StatusPrivacy.PRIVATE));
reblogAs.setOnClickListener(c->{
dialog.dismiss();
UiUtils.pickInteractAs(v.getContext(),
item.accountID, item.status,
s -> s.reblogged,
(ic, status, consumer) -> ic.setReblogged(status, true, null, consumer),
R.string.sk_reblog_as,
R.string.sk_reblogged_as,
R.string.sk_already_reblogged,
// TODO: replace once available: https://raw.githubusercontent.com/microsoft/fluentui-system-icons/main/android/library/src/main/res/drawable/ic_fluent_arrow_repeat_all_28_regular.xml
R.drawable.ic_fluent_arrow_repeat_all_24_regular
);
});
menu.findViewById(R.id.quote).setOnClickListener(c->{
dialog.dismiss();
v.startAnimation(opacityIn);
Bundle args=new Bundle();
args.putString("account", item.accountID);
args.putString("prefilledText", "\n\n" + item.status.url);
StringBuilder prefilledText = new StringBuilder().append("\n\n");
String ownID = AccountSessionManager.getInstance().getAccount(item.accountID).self.id;
if (!item.status.account.id.equals(ownID)) prefilledText.append('@').append(item.status.account.acct).append(' ');
prefilledText.append(item.status.url);
args.putString("prefilledText", prefilledText.toString());
args.putInt("selectionStart", 0);
Nav.go(item.parentFragment.getActivity(), ComposeFragment.class, args);
});
@@ -257,6 +301,20 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
});
}
private boolean onFavoriteLongClick(View v) {
if (AccountSessionManager.getInstance().getLoggedInAccounts().size() < 2) return false;
UiUtils.pickInteractAs(v.getContext(),
item.accountID, item.status,
s -> s.favourited,
(ic, status, consumer) -> ic.setFavorited(status, true, consumer),
R.string.sk_favorite_as,
R.string.sk_favorited_as,
R.string.sk_already_favorited,
R.drawable.ic_fluent_star_28_regular
);
return true;
}
private void onBookmarkClick(View v){
bookmark.setSelected(!item.status.bookmarked);
AccountSessionManager.getInstance().getAccount(item.accountID).getStatusInteractionController().setBookmarked(item.status, !item.status.bookmarked, r->{
@@ -264,6 +322,20 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
});
}
private boolean onBookmarkLongClick(View v) {
if (AccountSessionManager.getInstance().getLoggedInAccounts().size() < 2) return false;
UiUtils.pickInteractAs(v.getContext(),
item.accountID, item.status,
s -> s.bookmarked,
(ic, status, consumer) -> ic.setBookmarked(status, true, consumer),
R.string.sk_bookmark_as,
R.string.sk_bookmarked_as,
R.string.sk_already_bookmarked,
R.drawable.ic_fluent_bookmark_28_regular
);
return true;
}
private void onShareClick(View v){
v.startAnimation(opacityIn);
Intent intent=new Intent(Intent.ACTION_SEND);

View File

@@ -23,6 +23,7 @@ import android.widget.Toast;
import org.joinmastodon.android.GlobalUserPreferences;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.requests.accounts.GetAccountRelationships;
import org.joinmastodon.android.api.requests.statuses.CreateStatus;
import org.joinmastodon.android.api.requests.statuses.GetStatusSourceText;
import org.joinmastodon.android.api.session.AccountSession;
import org.joinmastodon.android.api.session.AccountSessionManager;
@@ -36,6 +37,7 @@ import org.joinmastodon.android.model.Account;
import org.joinmastodon.android.model.Attachment;
import org.joinmastodon.android.model.Notification;
import org.joinmastodon.android.model.Relationship;
import org.joinmastodon.android.model.ScheduledStatus;
import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.ui.text.HtmlParser;
import org.joinmastodon.android.ui.utils.CustomEmojiHelper;
@@ -43,9 +45,12 @@ import org.joinmastodon.android.ui.utils.UiUtils;
import org.parceler.Parcels;
import java.time.Instant;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import java.util.Locale;
import me.grishka.appkit.Nav;
import me.grishka.appkit.api.APIRequest;
@@ -68,9 +73,11 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
boolean needBottomPadding;
private String extraText;
private Notification notification;
private ScheduledStatus scheduledStatus;
public HeaderStatusDisplayItem(String parentID, Account user, Instant createdAt, BaseStatusListFragment parentFragment, String accountID, Status status, String extraText, Notification notification){
public HeaderStatusDisplayItem(String parentID, Account user, Instant createdAt, BaseStatusListFragment parentFragment, String accountID, Status status, String extraText, Notification notification, ScheduledStatus scheduledStatus){
super(parentID, parentFragment);
user=scheduledStatus != null ? AccountSessionManager.getInstance().getAccount(accountID).self : user;
this.user=user;
this.createdAt=createdAt;
avaRequest=new UrlImageLoaderRequest(GlobalUserPreferences.playGifs ? user.avatar : user.avatarStatic, V.dp(50), V.dp(50));
@@ -78,6 +85,7 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
parsedName=new SpannableStringBuilder(user.displayName);
this.status=status;
this.notification=notification;
this.scheduledStatus=scheduledStatus;
HtmlParser.parseCustomEmoji(parsedName, user.emojis);
emojiHelper.setText(parsedName);
if(status!=null){
@@ -140,12 +148,6 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
avatar.setOutlineProvider(roundCornersOutline);
avatar.setClipToOutline(true);
more.setOnClickListener(this::onMoreClick);
more.setOnLongClickListener((v) -> {
PopupMenu popup = new PopupMenu(itemView.getContext(), v);
populateAccountsMenu(popup.getMenu());
popup.show();
return true;
});
visibility.setOnClickListener(v->item.parentFragment.onVisibilityIconClick(this));
deleteNotification.setOnClickListener(v->UiUtils.confirmDeleteNotification(activity, item.parentFragment.getAccountID(), item.notification, ()->{
if (item.parentFragment instanceof NotificationsListFragment fragment) {
@@ -159,12 +161,6 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
Account account=item.user;
int id=menuItem.getItemId();
SubMenu accountsMenu=id==R.id.open_with_account ? menuItem.getSubMenu() : null;
if (accountsMenu != null) {
accountsMenu.clear();
populateAccountsMenu(accountsMenu);
}
if(id==R.id.edit || id==R.id.delete_and_redraft) {
final Bundle args=new Bundle();
args.putString("account", item.parentFragment.getAccountID());
@@ -179,6 +175,12 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
}
if(TextUtils.isEmpty(item.status.content) && TextUtils.isEmpty(item.status.spoilerText)){
Nav.go(item.parentFragment.getActivity(), ComposeFragment.class, args);
}else if(item.scheduledStatus!=null){
args.putString("sourceText", item.status.text);
args.putString("sourceSpoiler", item.status.spoilerText);
args.putBoolean("redraftStatus", true);
args.putParcelable("scheduledStatus", Parcels.wrap(item.scheduledStatus));
Nav.go(item.parentFragment.getActivity(), ComposeFragment.class, args);
}else{
new GetStatusSourceText(item.status.id)
.setCallback(new Callback<>(){
@@ -204,10 +206,17 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
.exec(item.parentFragment.getAccountID());
}
}else if(id==R.id.delete){
if (item.scheduledStatus != null) {
UiUtils.confirmDeleteScheduledPost(item.parentFragment.getActivity(), item.parentFragment.getAccountID(), item.scheduledStatus, ()->{});
} else {
UiUtils.confirmDeletePost(item.parentFragment.getActivity(), item.parentFragment.getAccountID(), item.status, s->{});
}
}else if(id==R.id.pin || id==R.id.unpin) {
UiUtils.confirmPinPost(item.parentFragment.getActivity(), item.parentFragment.getAccountID(), item.status, !item.status.pinned, s -> {
});
UiUtils.confirmPinPost(item.parentFragment.getActivity(), item.parentFragment.getAccountID(), item.status, !item.status.pinned, s->{});
}else if(id==R.id.mute){
UiUtils.confirmToggleMuteUser(item.parentFragment.getActivity(), item.parentFragment.getAccountID(), account, relationship!=null && relationship.muting, r->{});
}else if(id==R.id.block){
UiUtils.confirmToggleBlockUser(item.parentFragment.getActivity(), item.parentFragment.getAccountID(), account, relationship!=null && relationship.blocking, r->{});
}else if(id==R.id.report){
Bundle args=new Bundle();
args.putString("account", item.parentFragment.getAccountID());
@@ -231,7 +240,7 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
progress.dismiss();
}, rel->{
relationship=rel;
Toast.makeText(activity, activity.getString(rel.following ? R.string.followed_user : R.string.unfollowed_user, account.getDisplayUsername()), Toast.LENGTH_SHORT).show();
Toast.makeText(activity, activity.getString(rel.following ? R.string.followed_user : R.string.unfollowed_user, account.getShortUsername()), Toast.LENGTH_SHORT).show();
});
}else if(id==R.id.block_domain){
UiUtils.confirmToggleBlockDomain(activity, item.parentFragment.getAccountID(), account.getDomain(), relationship!=null && relationship.domainBlocking, ()->{});
@@ -240,6 +249,7 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
}
return true;
});
UiUtils.enablePopupMenuIcons(activity, optionsMenu);
}
private void populateAccountsMenu(Menu menu) {
@@ -257,7 +267,14 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
public void onBind(HeaderStatusDisplayItem item){
name.setText(item.parsedName);
username.setText('@'+item.user.acct);
if(item.status==null || item.status.editedAt==null)
if (item.scheduledStatus!=null)
if (item.scheduledStatus.scheduledAt.isAfter(CreateStatus.DRAFTS_AFTER_INSTANT)) {
timestamp.setText(R.string.sk_draft);
} else {
DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM).withLocale(Locale.getDefault());
timestamp.setText(item.scheduledStatus.scheduledAt.atZone(ZoneId.systemDefault()).format(formatter));
}
else if(item.status==null || item.status.editedAt==null)
timestamp.setText(UiUtils.formatRelativeTimestamp(itemView.getContext(), item.createdAt));
else
timestamp.setText(item.parentFragment.getString(R.string.edited_timestamp, UiUtils.formatRelativeTimestamp(itemView.getContext(), item.status.editedAt)));
@@ -335,16 +352,30 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
}
private void updateOptionsMenu(){
Account account=item.user;
boolean hasMultipleAccounts = AccountSessionManager.getInstance().getLoggedInAccounts().size() > 1;
Menu menu=optionsMenu.getMenu();
MenuItem openWithAccounts = menu.findItem(R.id.open_with_account);
SubMenu accountsMenu = openWithAccounts != null ? openWithAccounts.getSubMenu() : null;
if (hasMultipleAccounts && accountsMenu != null) {
openWithAccounts.setVisible(true);
accountsMenu.clear();
populateAccountsMenu(accountsMenu);
} else if (openWithAccounts != null) {
openWithAccounts.setVisible(false);
}
Account account=item.user;
boolean isOwnPost=AccountSessionManager.getInstance().isSelf(item.parentFragment.getAccountID(), account);
boolean isPostScheduled=item.scheduledStatus!=null;
menu.findItem(R.id.open_with_account).setVisible(!isPostScheduled && hasMultipleAccounts);
menu.findItem(R.id.edit).setVisible(item.status!=null && isOwnPost);
menu.findItem(R.id.delete).setVisible(item.status!=null && isOwnPost);
menu.findItem(R.id.delete_and_redraft).setVisible(item.status!=null && isOwnPost);
menu.findItem(R.id.pin).setVisible(item.status!=null && isOwnPost && !item.status.pinned);
menu.findItem(R.id.unpin).setVisible(item.status!=null && isOwnPost && item.status.pinned);
menu.findItem(R.id.open_in_browser).setVisible(item.status!=null);
menu.findItem(R.id.copy_link).setVisible(item.status!=null);
menu.findItem(R.id.delete_and_redraft).setVisible(!isPostScheduled && item.status!=null && isOwnPost);
menu.findItem(R.id.pin).setVisible(!isPostScheduled && item.status!=null && isOwnPost && !item.status.pinned);
menu.findItem(R.id.unpin).setVisible(!isPostScheduled && item.status!=null && isOwnPost && item.status.pinned);
menu.findItem(R.id.open_in_browser).setVisible(!isPostScheduled && item.status!=null);
menu.findItem(R.id.copy_link).setVisible(!isPostScheduled && item.status!=null);
MenuItem blockDomain=menu.findItem(R.id.block_domain);
MenuItem mute=menu.findItem(R.id.mute);
MenuItem block=menu.findItem(R.id.block);
@@ -360,7 +391,7 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
bookmark.setVisible(false);
}
*/
if(isOwnPost){
if(isPostScheduled || isOwnPost){
mute.setVisible(false);
block.setVisible(false);
report.setVisible(false);
@@ -371,9 +402,11 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
block.setVisible(true);
report.setVisible(true);
follow.setVisible(relationship==null || relationship.following || (!relationship.blocking && !relationship.blockedBy && !relationship.domainBlocking && !relationship.muting));
mute.setTitle(item.parentFragment.getString(relationship!=null && relationship.muting ? R.string.unmute_user : R.string.mute_user, account.getDisplayUsername()));
block.setTitle(item.parentFragment.getString(relationship!=null && relationship.blocking ? R.string.unblock_user : R.string.block_user, account.getDisplayUsername()));
report.setTitle(item.parentFragment.getString(R.string.report_user, account.getDisplayUsername()));
mute.setTitle(item.parentFragment.getString(relationship!=null && relationship.muting ? R.string.unmute_user : R.string.mute_user, account.getShortUsername()));
mute.setIcon(relationship!=null && relationship.muting ? R.drawable.ic_fluent_speaker_0_24_regular : R.drawable.ic_fluent_speaker_off_24_regular);
UiUtils.insetPopupMenuIcon(item.parentFragment.getContext(), mute);
block.setTitle(item.parentFragment.getString(relationship!=null && relationship.blocking ? R.string.unblock_user : R.string.block_user, account.getShortUsername()));
report.setTitle(item.parentFragment.getString(R.string.report_user, account.getShortUsername()));
// disabled in megalodon. domain blocks from a post clutters the context menu and looks out of place
// if(!account.isLocal()){
// blockDomain.setVisible(true);
@@ -381,7 +414,10 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
// }else{
blockDomain.setVisible(false);
// }
follow.setTitle(item.parentFragment.getString(relationship!=null && relationship.following ? R.string.unfollow_user : R.string.follow_user, account.getDisplayUsername()));
boolean following = relationship!=null && relationship.following;
follow.setTitle(item.parentFragment.getString(following ? R.string.unfollow_user : R.string.follow_user, account.getShortUsername()));
follow.setIcon(following ? R.drawable.ic_fluent_person_delete_24_regular : R.drawable.ic_fluent_person_add_24_regular);
UiUtils.insetPopupMenuIcon(item.parentFragment.getContext(), follow);
}
}
}

View File

@@ -94,6 +94,7 @@ public class PollOptionStatusDisplayItem extends StatusDisplayItem{
}else{
itemView.setSelected(item.poll.selectedOptions!=null && item.poll.selectedOptions.contains(item.option));
button.setBackgroundResource(R.drawable.bg_poll_option_clickable);
icon.setSelected(itemView.isSelected());
icon.setVisibility(View.VISIBLE);
}
}

View File

@@ -17,6 +17,7 @@ import org.joinmastodon.android.model.Attachment;
import org.joinmastodon.android.model.DisplayItemsParent;
import org.joinmastodon.android.model.Notification;
import org.joinmastodon.android.model.Poll;
import org.joinmastodon.android.model.ScheduledStatus;
import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.ui.PhotoLayoutHelper;
import org.joinmastodon.android.ui.text.HtmlParser;
@@ -81,6 +82,8 @@ public abstract class StatusDisplayItem{
Status statusForContent=status.getContentStatus();
Bundle args=new Bundle();
args.putString("account", accountID);
ScheduledStatus scheduledStatus = parentObject instanceof ScheduledStatus ? (ScheduledStatus) parentObject : null;
if(status.reblog!=null){
boolean isOwnPost = AccountSessionManager.getInstance().isSelf(fragment.getAccountID(), status.account);
items.add(new ReblogOrReplyLineStatusDisplayItem(parentID, fragment, fragment.getString(R.string.user_boosted, status.account.displayName), status.account.emojis, R.drawable.ic_fluent_arrow_repeat_all_20_filled, isOwnPost ? status.visibility : null, i->{
@@ -95,7 +98,7 @@ public abstract class StatusDisplayItem{
}));
}
HeaderStatusDisplayItem header;
items.add(header=new HeaderStatusDisplayItem(parentID, statusForContent.account, statusForContent.createdAt, fragment, accountID, statusForContent, null, notification));
items.add(header=new HeaderStatusDisplayItem(parentID, statusForContent.account, statusForContent.createdAt, fragment, accountID, statusForContent, null, notification, scheduledStatus));
if(!TextUtils.isEmpty(statusForContent.content))
items.add(new TextStatusDisplayItem(parentID, HtmlParser.parse(statusForContent.content, statusForContent.emojis, statusForContent.mentions, statusForContent.tags, accountID), fragment, statusForContent));
else

View File

@@ -5,6 +5,7 @@ import static org.joinmastodon.android.GlobalUserPreferences.trueBlackTheme;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.ActivityNotFoundException;
import android.content.ClipData;
@@ -26,8 +27,6 @@ import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.provider.OpenableColumns;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
@@ -46,6 +45,7 @@ import org.joinmastodon.android.E;
import org.joinmastodon.android.GlobalUserPreferences;
import org.joinmastodon.android.MastodonApp;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.StatusInteractionController;
import org.joinmastodon.android.api.requests.accounts.SetAccountBlocked;
import org.joinmastodon.android.api.requests.accounts.SetAccountFollowed;
import org.joinmastodon.android.api.requests.accounts.SetAccountMuted;
@@ -54,17 +54,20 @@ import org.joinmastodon.android.api.requests.accounts.AuthorizeFollowRequest;
import org.joinmastodon.android.api.requests.accounts.RejectFollowRequest;
import org.joinmastodon.android.api.requests.notifications.DismissNotification;
import org.joinmastodon.android.api.requests.search.GetSearchResults;
import org.joinmastodon.android.api.requests.statuses.CreateStatus;
import org.joinmastodon.android.api.requests.statuses.DeleteStatus;
import org.joinmastodon.android.api.requests.statuses.GetStatusByID;
import org.joinmastodon.android.api.requests.statuses.SetStatusPinned;
import org.joinmastodon.android.api.session.AccountSession;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.events.ScheduledStatusDeletedEvent;
import org.joinmastodon.android.events.StatusCountersUpdatedEvent;
import org.joinmastodon.android.events.FollowRequestHandledEvent;
import org.joinmastodon.android.events.NotificationDeletedEvent;
import org.joinmastodon.android.events.RemoveAccountPostsEvent;
import org.joinmastodon.android.events.StatusDeletedEvent;
import org.joinmastodon.android.events.StatusUnpinnedEvent;
import org.joinmastodon.android.fragments.ComposeFragment;
import org.joinmastodon.android.fragments.HashtagTimelineFragment;
import org.joinmastodon.android.fragments.ListTimelineFragment;
import org.joinmastodon.android.fragments.ProfileFragment;
@@ -75,11 +78,11 @@ import org.joinmastodon.android.model.Instance;
import org.joinmastodon.android.model.ListTimeline;
import org.joinmastodon.android.model.Notification;
import org.joinmastodon.android.model.Relationship;
import org.joinmastodon.android.model.ScheduledStatus;
import org.joinmastodon.android.model.SearchResults;
import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
import org.joinmastodon.android.ui.text.CustomEmojiSpan;
import org.joinmastodon.android.ui.text.SpacerSpan;
import org.parceler.Parcels;
import java.io.File;
@@ -96,10 +99,12 @@ import java.util.List;
import java.util.Map;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import androidx.annotation.AttrRes;
import androidx.annotation.DrawableRes;
import androidx.annotation.IdRes;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.browser.customtabs.CustomTabsIntent;
@@ -343,22 +348,29 @@ public class UiUtils{
}
public static void showConfirmationAlert(Context context, @StringRes int title, @StringRes int message, @StringRes int confirmButton, Runnable onConfirmed){
showConfirmationAlert(context, context.getString(title), context.getString(message), context.getString(confirmButton), onConfirmed);
showConfirmationAlert(context, title, message, confirmButton, 0, onConfirmed);
}
public static void showConfirmationAlert(Context context, CharSequence title, CharSequence message, CharSequence confirmButton, Runnable onConfirmed){
public static void showConfirmationAlert(Context context, @StringRes int title, @StringRes int message, @StringRes int confirmButton, @DrawableRes int icon, Runnable onConfirmed){
showConfirmationAlert(context, context.getString(title), context.getString(message), context.getString(confirmButton), icon, onConfirmed);
}
public static void showConfirmationAlert(Context context, CharSequence title, CharSequence message, CharSequence confirmButton, int icon, Runnable onConfirmed){
new M3AlertDialogBuilder(context)
.setTitle(title)
.setMessage(message)
.setPositiveButton(confirmButton, (dlg, i)->onConfirmed.run())
.setNegativeButton(R.string.cancel, null)
.setIcon(icon)
.show();
}
public static void confirmToggleBlockUser(Activity activity, String accountID, Account account, boolean currentlyBlocked, Consumer<Relationship> resultCallback){
showConfirmationAlert(activity, activity.getString(currentlyBlocked ? R.string.confirm_unblock_title : R.string.confirm_block_title),
activity.getString(currentlyBlocked ? R.string.confirm_unblock : R.string.confirm_block, account.displayName),
activity.getString(currentlyBlocked ? R.string.do_unblock : R.string.do_block), ()->{
activity.getString(currentlyBlocked ? R.string.do_unblock : R.string.do_block),
currentlyBlocked ? R.drawable.ic_fluent_person_28_regular : R.drawable.ic_fluent_person_prohibited_28_regular,
()->{
new SetAccountBlocked(account.id, !currentlyBlocked)
.setCallback(new Callback<>(){
@Override
@@ -382,7 +394,9 @@ public class UiUtils{
public static void confirmToggleBlockDomain(Activity activity, String accountID, String domain, boolean currentlyBlocked, Runnable resultCallback){
showConfirmationAlert(activity, activity.getString(currentlyBlocked ? R.string.confirm_unblock_domain_title : R.string.confirm_block_domain_title),
activity.getString(currentlyBlocked ? R.string.confirm_unblock : R.string.confirm_block, domain),
activity.getString(currentlyBlocked ? R.string.do_unblock : R.string.do_block), ()->{
activity.getString(currentlyBlocked ? R.string.do_unblock : R.string.do_block),
R.drawable.ic_fluent_shield_28_regular,
()->{
new SetDomainBlocked(domain, !currentlyBlocked)
.setCallback(new Callback<>(){
@Override
@@ -403,7 +417,9 @@ public class UiUtils{
public static void confirmToggleMuteUser(Activity activity, String accountID, Account account, boolean currentlyMuted, Consumer<Relationship> resultCallback){
showConfirmationAlert(activity, activity.getString(currentlyMuted ? R.string.confirm_unmute_title : R.string.confirm_mute_title),
activity.getString(currentlyMuted ? R.string.confirm_unmute : R.string.confirm_mute, account.displayName),
activity.getString(currentlyMuted ? R.string.do_unmute : R.string.do_mute), ()->{
activity.getString(currentlyMuted ? R.string.do_unmute : R.string.do_mute),
currentlyMuted ? R.drawable.ic_fluent_speaker_0_28_regular : R.drawable.ic_fluent_speaker_off_28_regular,
()->{
new SetAccountMuted(account.id, !currentlyMuted)
.setCallback(new Callback<>(){
@Override
@@ -428,8 +444,12 @@ public class UiUtils{
}
public static void confirmDeletePost(Activity activity, String accountID, Status status, Consumer<Status> resultCallback, boolean forRedraft){
showConfirmationAlert(activity, forRedraft ? R.string.sk_confirm_delete_and_redraft_title : R.string.confirm_delete_title, forRedraft ? R.string.sk_confirm_delete_and_redraft : R.string.confirm_delete, forRedraft ? R.string.sk_delete_and_redraft : R.string.delete, ()->{
new DeleteStatus(status.id)
showConfirmationAlert(activity,
forRedraft ? R.string.sk_confirm_delete_and_redraft_title : R.string.confirm_delete_title,
forRedraft ? R.string.sk_confirm_delete_and_redraft : R.string.confirm_delete,
forRedraft ? R.string.sk_delete_and_redraft : R.string.delete,
forRedraft ? R.drawable.ic_fluent_arrow_clockwise_28_regular : R.drawable.ic_fluent_delete_28_regular,
() -> new DeleteStatus(status.id)
.setCallback(new Callback<>(){
@Override
public void onSuccess(Status result){
@@ -444,8 +464,33 @@ public class UiUtils{
}
})
.wrapProgress(activity, R.string.deleting, false)
.exec(accountID);
});
.exec(accountID)
);
}
public static void confirmDeleteScheduledPost(Activity activity, String accountID, ScheduledStatus status, Runnable resultCallback){
boolean isDraft = status.scheduledAt.isAfter(CreateStatus.DRAFTS_AFTER_INSTANT);
showConfirmationAlert(activity,
isDraft ? R.string.sk_confirm_delete_draft_title : R.string.sk_confirm_delete_scheduled_post_title,
isDraft ? R.string.sk_confirm_delete_draft : R.string.sk_confirm_delete_scheduled_post,
R.string.delete,
R.drawable.ic_fluent_delete_28_regular,
() -> new DeleteStatus.Scheduled(status.id)
.setCallback(new Callback<>(){
@Override
public void onSuccess(Object nothing){
resultCallback.run();
E.post(new ScheduledStatusDeletedEvent(status.id, accountID));
}
@Override
public void onError(ErrorResponse error){
error.showToast(activity);
}
})
.wrapProgress(activity, R.string.deleting, false)
.exec(accountID)
);
}
public static void confirmPinPost(Activity activity, String accountID, Status status, boolean pinned, Consumer<Status> resultCallback){
@@ -453,6 +498,7 @@ public class UiUtils{
pinned ? R.string.sk_confirm_pin_post_title : R.string.sk_confirm_unpin_post_title,
pinned ? R.string.sk_confirm_pin_post : R.string.sk_confirm_unpin_post,
pinned ? R.string.sk_pin_post : R.string.sk_unpin_post,
pinned ? R.drawable.ic_fluent_pin_off_28_regular : R.drawable.ic_fluent_pin_28_regular,
()->{
new SetStatusPinned(status.id, pinned)
.setCallback(new Callback<>() {
@@ -480,6 +526,7 @@ public class UiUtils{
notification == null ? R.string.sk_clear_all_notifications : R.string.sk_delete_notification,
notification == null ? R.string.sk_clear_all_notifications_confirm : R.string.sk_delete_notification_confirm,
notification == null ? R.string.sk_clear_all_notifications_confirm_action : R.string.sk_delete_notification_confirm_action,
notification == null ? R.drawable.ic_fluent_mail_inbox_dismiss_28_regular : R.drawable.ic_fluent_delete_28_regular,
() -> new DismissNotification(notification != null ? notification.id : null).setCallback(new Callback<>() {
@Override
public void onSuccess(Object o) {
@@ -661,6 +708,42 @@ public class UiUtils{
return bitmap;
}
public static void insetPopupMenuIcon(Context context, MenuItem item) {
ColorStateList iconTint=ColorStateList.valueOf(UiUtils.getThemeColor(context, android.R.attr.textColorSecondary));
insetPopupMenuIcon(item, iconTint);
}
public static void insetPopupMenuIcon(MenuItem item, ColorStateList iconTint) {
Drawable icon=item.getIcon().mutate();
if(Build.VERSION.SDK_INT>=26) item.setIconTintList(iconTint);
else icon.setTintList(iconTint);
icon=new InsetDrawable(icon, V.dp(8), 0, V.dp(8), 0);
item.setIcon(icon);
SpannableStringBuilder ssb=new SpannableStringBuilder(item.getTitle());
item.setTitle(ssb);
}
public static void enableOptionsMenuIcons(Context context, Menu menu, @IdRes int... asAction) {
if(menu.getClass().getSimpleName().equals("MenuBuilder")){
try {
Method m = menu.getClass().getDeclaredMethod(
"setOptionalIconsVisible", Boolean.TYPE);
m.setAccessible(true);
m.invoke(menu, true);
enableMenuIcons(context, menu, asAction);
}
catch(Exception ignored){}
}
}
public static void enableMenuIcons(Context context, Menu m, @IdRes int... exclude) {
ColorStateList iconTint=ColorStateList.valueOf(UiUtils.getThemeColor(context, android.R.attr.textColorSecondary));
for(int i=0;i<m.size();i++){
MenuItem item=m.getItem(i);
if (item.getIcon() == null || Arrays.stream(exclude).anyMatch(id -> id == item.getItemId())) continue;
insetPopupMenuIcon(item, iconTint);
}
}
public static void enablePopupMenuIcons(Context context, PopupMenu menu){
Menu m=menu.getMenu();
if(Build.VERSION.SDK_INT>=29){
@@ -672,23 +755,7 @@ public class UiUtils{
setOptionalIconsVisible.invoke(m, true);
}catch(Exception ignore){}
}
ColorStateList iconTint=ColorStateList.valueOf(UiUtils.getThemeColor(context, android.R.attr.textColorSecondary));
for(int i=0;i<m.size();i++){
MenuItem item=m.getItem(i);
Drawable icon=item.getIcon().mutate();
if(Build.VERSION.SDK_INT>=26){
item.setIconTintList(iconTint);
}else{
icon.setTintList(iconTint);
}
icon=new InsetDrawable(icon, V.dp(8), 0, 0, 0);
item.setIcon(icon);
SpannableStringBuilder ssb=new SpannableStringBuilder(item.getTitle());
ssb.insert(0, " ");
ssb.setSpan(new SpacerSpan(V.dp(24), 1), 0, 1, 0);
ssb.append(" ", new SpacerSpan(V.dp(8), 1), 0);
item.setTitle(ssb);
}
enableMenuIcons(context, m);
}
public static void setUserPreferredTheme(Context context){
@@ -755,24 +822,87 @@ public class UiUtils{
return instance != null && !instance.title.isBlank() ? instance.title : session.domain;
}
public static void pickAccount(Context context, String exceptFor, @StringRes int titleRes, @DrawableRes int iconRes, Consumer<AccountSession> sessionConsumer, Consumer<AlertDialog.Builder> transformDialog) {
List<AccountSession> sessions=AccountSessionManager.getInstance().getLoggedInAccounts()
.stream().filter(s->!s.getID().equals(exceptFor)).collect(Collectors.toList());
AlertDialog.Builder builder = new M3AlertDialogBuilder(context)
.setItems(
sessions.stream().map(AccountSession::getFullUsername).toArray(String[]::new),
(dialog, which) -> sessionConsumer.accept(sessions.get(which))
)
.setTitle(titleRes == 0 ? R.string.choose_account : titleRes)
.setIcon(iconRes);
if (transformDialog != null) transformDialog.accept(builder);
builder.show();
}
@FunctionalInterface
public interface InteractionPerformer {
void interact(StatusInteractionController ic, Status status, Consumer<Status> resultConsumer);
}
public static void pickInteractAs(Context context, String accountID, Status sourceStatus, Predicate<Status> checkInteracted, InteractionPerformer interactionPerformer, @StringRes int interactAsRes, @StringRes int interactedAsAccountRes, @StringRes int alreadyInteractedRes, @DrawableRes int iconRes) {
pickAccount(context, accountID, interactAsRes, iconRes, session -> {
lookupStatus(context, sourceStatus, session.getID(), accountID, status -> {
if (checkInteracted.test(status)) {
Toast.makeText(context, alreadyInteractedRes, Toast.LENGTH_SHORT).show();
return;
}
StatusInteractionController ic = AccountSessionManager.getInstance().getAccount(session.getID()).getRemoteStatusInteractionController();
interactionPerformer.interact(ic, status, s -> {
if (checkInteracted.test(s)) {
Toast.makeText(context, context.getString(interactedAsAccountRes, session.getFullUsername()), Toast.LENGTH_SHORT).show();
}
});
});
}, null);
}
public static void lookupStatus(Context context, Status queryStatus, String targetAccountID, @Nullable String sourceAccountID, Consumer<Status> statusConsumer) {
if (sourceAccountID != null && targetAccountID.startsWith(sourceAccountID.substring(0, sourceAccountID.indexOf('_')))) {
statusConsumer.accept(queryStatus);
return;
}
new GetSearchResults(queryStatus.url, GetSearchResults.Type.STATUSES, true).setCallback(new Callback<>() {
@Override
public void onSuccess(SearchResults results) {
if (!results.statuses.isEmpty()) statusConsumer.accept(results.statuses.get(0));
else Toast.makeText(context, R.string.sk_resource_not_found, Toast.LENGTH_SHORT).show();
}
@Override
public void onError(ErrorResponse error) {
error.showToast(context);
}
})
.wrapProgress((Activity)context, R.string.loading, true,
d -> transformDialogForLookup(context, targetAccountID, null, d))
.exec(targetAccountID);
}
public static void openURL(Context context, String accountID, String url) {
openURL(context, accountID, url, true);
}
public static void openURL(Context context, String accountID, String url, boolean launchBrowser){
Consumer<ProgressDialog> transformDialogForLookup = dialog -> {
private static void transformDialogForLookup(Context context, String accountID, @Nullable String url, ProgressDialog dialog) {
if (accountID != null) {
dialog.setTitle(context.getString(R.string.sk_loading_resource_on_instance_title, getInstanceName(accountID)));
} else {
dialog.setTitle(R.string.sk_loading_fediverse_resource_title);
}
dialog.setButton(DialogInterface.BUTTON_NEGATIVE, context.getString(R.string.cancel), (d, which) -> d.cancel());
if (url != null) {
dialog.setButton(DialogInterface.BUTTON_POSITIVE, context.getString(R.string.open_in_browser), (d, which) -> {
d.cancel();
launchWebBrowser(context, url);
});
};
}
}
public static void openURL(Context context, String accountID, String url, boolean launchBrowser){
Uri uri=Uri.parse(url);
List<String> path=uri.getPathSegments();
if(accountID!=null && "https".equals(uri.getScheme())){
@@ -793,7 +923,8 @@ public class UiUtils{
if (launchBrowser) launchWebBrowser(context, url);
}
})
.wrapProgress((Activity)context, R.string.loading, true, transformDialogForLookup)
.wrapProgress((Activity)context, R.string.loading, true,
d -> transformDialogForLookup(context, accountID, url, d))
.exec(accountID);
return;
} else if (looksLikeMastodonUrl(url)) {
@@ -821,7 +952,8 @@ public class UiUtils{
if (launchBrowser) launchWebBrowser(context, url);
}
})
.wrapProgress((Activity)context, R.string.loading, true, transformDialogForLookup)
.wrapProgress((Activity)context, R.string.loading, true,
d -> transformDialogForLookup(context, accountID, url, d))
.exec(accountID);
return;
}
@@ -850,4 +982,26 @@ public class UiUtils{
public static boolean isMIUI(){
return !TextUtils.isEmpty(getSystemProperty("ro.miui.ui.version.code"));
}
public static boolean pickAccountForCompose(Activity activity, String accountID, String prefilledText){
Bundle args = new Bundle();
if (prefilledText != null) args.putString("prefilledText", prefilledText);
return pickAccountForCompose(activity, accountID, args);
}
public static boolean pickAccountForCompose(Activity activity, String accountID){
return pickAccountForCompose(activity, accountID, (String) null);
}
public static boolean pickAccountForCompose(Activity activity, String accountID, Bundle args){
if (AccountSessionManager.getInstance().getLoggedInAccounts().size() > 1) {
UiUtils.pickAccount(activity, accountID, 0, 0, session -> {
args.putString("account", session.getID());
Nav.go(activity, ComposeFragment.class, args);
}, null);
return true;
} else {
return false;
}
}
}

View File

@@ -4,6 +4,7 @@ import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.model.Filter;
import org.joinmastodon.android.model.Status;
import java.time.Instant;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@@ -21,6 +22,16 @@ public class StatusFilterPredicate implements Predicate<Status>{
@Override
public boolean test(Status status){
if(status.filtered!=null){
if (status.filtered.isEmpty()){
return true;
}
boolean matches=status.filtered.stream()
.map(filterResult->filterResult.filter)
.filter(filter->filter.expiresAt==null||filter.expiresAt.isAfter(Instant.now()))
.anyMatch(filter->filter.filterAction==Filter.FilterAction.HIDE);
return !matches;
}
for(Filter filter:filters){
if(filter.matches(status))
return false;

View File

@@ -3,7 +3,7 @@
android:color="?android:colorControlHighlight">
<item>
<shape>
<solid android:color="?colorSecondary"/>
<solid android:color="?attr/colorComposeButtonBackground"/>
<corners android:radius="16dp"/>
</shape>
</item>

View File

@@ -1,5 +0,0 @@
<vector android:height="34dp"
android:viewportHeight="24" android:viewportWidth="24"
android:width="34dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z"/>
</vector>

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
<path android:pathData="M12 4.5c-4.142 0-7.5 3.358-7.5 7.5 0 4.142 3.358 7.5 7.5 7.5 4.142 0 7.5-3.358 7.5-7.5 0-0.376-0.028-0.746-0.081-1.108C19.352 10.438 19.684 10 20.143 10c0.37 0 0.696 0.256 0.752 0.623C20.965 11.072 21 11.532 21 12c0 4.97-4.03 9-9 9s-9-4.03-9-9 4.03-9 9-9c2.305 0 4.408 0.867 6 2.292V4.25c0-0.414 0.336-0.75 0.75-0.75s0.75 0.336 0.75 0.75v3C19.5 7.664 19.164 8 18.75 8h-3C15.336 8 15 7.664 15 7.25s0.336-0.75 0.75-0.75h1.35c-1.34-1.241-3.13-2-5.1-2z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="28dp" android:height="28dp" android:viewportWidth="28" android:viewportHeight="28">
<path android:pathData="M19.832 6.5c-1.61-1.254-3.634-2-5.832-2-5.247 0-9.5 4.253-9.5 9.5s4.253 9.5 9.5 9.5 9.5-4.253 9.5-9.5c0-0.452-0.03-0.659-0.1-1.139l-0.001-0.002c-0.06-0.41 0.223-0.791 0.633-0.851 0.41-0.06 0.791 0.223 0.851 0.633l0.002 0.012C24.957 13.143 25 13.438 25 14c0 6.075-4.925 11-11 11S3 20.075 3 14 7.925 3 14 3c2.66 0 5.099 0.944 7 2.514V3.75C21 3.336 21.336 3 21.75 3s0.75 0.336 0.75 0.75v3.5C22.5 7.664 22.164 8 21.75 8h-3.5c-0.414 0-0.75-0.336-0.75-0.75s0.336-0.75 0.75-0.75h1.582z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:autoMirrored="true" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
<path android:pathData="M14.723 16.221c-0.293 0.293-0.293 0.768 0 1.06 0.293 0.294 0.768 0.293 1.061 0l4.997-5.003c0.292-0.293 0.292-0.768 0-1.06L15.783 6.22c-0.293-0.293-0.768-0.293-1.06 0-0.294 0.293-0.294 0.767-0.001 1.06l3.72 3.72H10.6c-1.595 0-2.81 0.242-3.889 0.764L6.466 11.89c-1.109 0.593-1.983 1.467-2.576 2.576C3.28 15.606 3 16.884 3 18.6c0 0.414 0.336 0.75 0.75 0.75S4.5 19.014 4.5 18.6c0-1.484 0.228-2.52 0.713-3.428 0.453-0.847 1.113-1.507 1.96-1.96 0.838-0.448 1.786-0.676 3.094-0.709L10.6 12.5h7.837l-3.715 3.721z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="28dp" android:height="28dp" android:viewportWidth="28" android:viewportHeight="28">
<path android:pathData="M10.03 5.47c0.293 0.293 0.293 0.767 0 1.06L5.56 11h8.69C20.187 11 25 15.813 25 21.75c0 0.414-0.336 0.75-0.75 0.75s-0.75-0.336-0.75-0.75c0-5.109-4.141-9.25-9.25-9.25H5.56l4.47 4.47c0.293 0.293 0.293 0.767 0 1.06-0.293 0.293-0.767 0.293-1.06 0l-5.75-5.75c-0.293-0.293-0.293-0.767 0-1.06l5.75-5.75c0.293-0.293 0.767-0.293 1.06 0z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="28dp" android:height="28dp" android:viewportWidth="28" android:viewportHeight="28">
<path android:pathData="M6 6.75C6 4.955 7.455 3.5 9.25 3.5h9.5C20.545 3.5 22 4.955 22 6.75v18c0 0.285-0.161 0.545-0.416 0.672-0.256 0.126-0.56 0.098-0.787-0.075L14 20.192l-6.797 5.157c-0.227 0.172-0.532 0.2-0.787 0.074C6.161 25.295 6 25.035 6 24.75v-18zM9.25 5C8.284 5 7.5 5.784 7.5 6.75v16.49l6.047-4.587c0.268-0.204 0.638-0.204 0.906 0L20.5 23.24V6.75C20.5 5.784 19.716 5 18.75 5h-9.5z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -0,0 +1,15 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M9.562,3C5.419,3 2.062,6.358 2.062,10.5C2.062,11.633 2.313,12.709 2.764,13.673C2.51,14.671 2.226,15.784 2.04,16.515C1.807,17.428 2.629,18.259 3.544,18.039C4.294,17.859 5.447,17.582 6.474,17.337C7.417,17.763 8.462,18 9.562,18C13.704,18 17.062,14.642 17.062,10.5C17.062,6.358 13.704,3 9.562,3ZM3.562,10.5C3.562,7.186 6.248,4.5 9.562,4.5C12.875,4.5 15.562,7.186 15.562,10.5C15.562,13.814 12.875,16.5 9.562,16.5C8.601,16.5 7.695,16.275 6.892,15.875L6.648,15.754L6.384,15.817C5.461,16.036 4.395,16.292 3.596,16.484C3.795,15.705 4.058,14.672 4.286,13.776L4.356,13.5L4.226,13.247C3.801,12.425 3.562,11.491 3.562,10.5ZM14.562,21C12.592,21 10.8,20.241 9.462,19C9.495,19 9.528,19 9.562,19C10.28,19 10.977,18.911 11.643,18.743C12.507,19.225 13.502,19.5 14.562,19.5C15.522,19.5 16.428,19.275 17.232,18.875L17.475,18.754L17.74,18.817C18.661,19.036 19.705,19.263 20.479,19.426C20.304,18.676 20.065,17.671 19.837,16.776L19.767,16.5L19.897,16.247C20.322,15.425 20.562,14.491 20.562,13.5C20.562,11.385 19.468,9.526 17.815,8.458C17.636,7.734 17.365,7.048 17.015,6.411C19.952,7.427 22.062,10.217 22.062,13.5C22.062,14.633 21.81,15.709 21.359,16.674C21.612,17.682 21.868,18.774 22.03,19.477C22.235,20.362 21.455,21.163 20.563,20.977C19.836,20.825 18.693,20.581 17.649,20.337C16.707,20.763 15.661,21 14.562,21Z"
android:fillColor="@color/fluent_default_icon_tint" />
<path
android:pathData="M7.297,8.312L11.85,8.312A0.75,0.75 0,0 1,12.6 9.062L12.6,9.062A0.75,0.75 0,0 1,11.85 9.812L7.297,9.812A0.75,0.75 0,0 1,6.547 9.062L6.547,9.062A0.75,0.75 0,0 1,7.297 8.312z"
android:fillColor="@color/fluent_default_icon_tint" />
<path
android:pathData="M7.297,11.312L10.323,11.312A0.75,0.75 0,0 1,11.073 12.062L11.073,12.062A0.75,0.75 0,0 1,10.323 12.812L7.297,12.812A0.75,0.75 0,0 1,6.547 12.062L6.547,12.062A0.75,0.75 0,0 1,7.297 11.312z"
android:fillColor="@color/fluent_default_icon_tint" />
</vector>

View File

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

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="20dp" android:height="20dp" android:viewportWidth="20" android:viewportHeight="20">
<path android:pathData="M10 2c4.418 0 8 3.582 8 8s-3.582 8-8 8-8-3.582-8-8 3.582-8 8-8zM9.5 5C9.224 5 9 5.224 9 5.5v5l0.008 0.09C9.05 10.823 9.255 11 9.5 11h3l0.09-0.008C12.823 10.95 13 10.745 13 10.5c0-0.276-0.224-0.5-0.5-0.5H10V5.5L9.992 5.41C9.95 5.177 9.745 5 9.5 5z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="20dp" android:height="20dp" android:viewportWidth="20" android:viewportHeight="20">
<path android:pathData="M10 2c4.418 0 8 3.582 8 8s-3.582 8-8 8-8-3.582-8-8 3.582-8 8-8zm0 1c-3.866 0-7 3.134-7 7s3.134 7 7 7 7-3.134 7-7-3.134-7-7-7zM9.5 5c0.245 0 0.45 0.177 0.492 0.41L10 5.5V10h2.5c0.276 0 0.5 0.224 0.5 0.5 0 0.245-0.177 0.45-0.41 0.492L12.5 11h-3c-0.245 0-0.45-0.177-0.492-0.41L9 10.5v-5C9 5.224 9.224 5 9.5 5z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
<path android:pathData="M15.25 13.5h-4c-0.414 0-0.75-0.336-0.75-0.75v-6C10.5 6.336 10.836 6 11.25 6S12 6.336 12 6.75V12h3.25c0.414 0 0.75 0.336 0.75 0.75s-0.336 0.75-0.75 0.75zM12 2C6.478 2 2 6.478 2 12s4.478 10 10 10 10-4.478 10-10S17.522 2 12 2z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
<path android:pathData="M12 2c5.523 0 10 4.478 10 10s-4.477 10-10 10S2 17.522 2 12 6.477 2 12 2zm0 1.667c-4.595 0-8.333 3.738-8.333 8.333 0 4.595 3.738 8.333 8.333 8.333 4.595 0 8.333-3.738 8.333-8.333 0-4.595-3.738-8.333-8.333-8.333zM11.25 6c0.38 0 0.694 0.282 0.743 0.648L12 6.75V12h3.25c0.414 0 0.75 0.336 0.75 0.75 0 0.38-0.282 0.694-0.648 0.743L15.25 13.5h-4c-0.38 0-0.694-0.282-0.743-0.648L10.5 12.75v-6C10.5 6.336 10.836 6 11.25 6z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="20dp" android:height="20dp" android:viewportWidth="20" android:viewportHeight="20">
<path android:pathData="M15.613 8.32l-3.936-3.936-8.038 8.039-0.117 0.128c-0.185 0.22-0.322 0.48-0.398 0.76l-1.106 4.055-0.015 0.08c-0.038 0.34 0.282 0.628 0.63 0.534l4.054-1.106 0.165-0.053c0.271-0.1 0.518-0.257 0.723-0.462l8.038-8.039zm1.568-5.503c-1.087-1.087-2.849-1.087-3.936 0l-0.861 0.86 3.936 3.936 0.86-0.86 0.131-0.14c0.955-1.093 0.911-2.754-0.13-3.796zM11.648 3H2.5C2.224 3 2 3.224 2 3.5S2.224 4 2.5 4h8.148l1-1zm-3 3H2.5C2.224 6 2 6.223 2 6.5 2 6.776 2.224 7 2.5 7h5.148l1-1zm-3 3l-1 1H2.5C2.224 10 2 9.776 2 9.5 2 9.223 2.224 9 2.5 9h3.148z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="20dp" android:height="20dp" android:viewportWidth="20" android:viewportHeight="20">
<path android:pathData="M13.245 2.817L3.64 12.423 3.522 12.55c-0.185 0.22-0.322 0.48-0.398 0.76l-1.106 4.055-0.015 0.08c-0.038 0.34 0.282 0.628 0.63 0.534l4.054-1.106 0.165-0.053c0.271-0.1 0.518-0.257 0.723-0.462l9.606-9.606 0.13-0.14c0.955-1.093 0.911-2.754-0.13-3.796-1.087-1.087-2.849-1.087-3.936 0zM4.346 13.13l8.039-8.038 2.521 2.52-8.038 8.04-0.098 0.085-0.107 0.072c-0.075 0.044-0.155 0.077-0.239 0.1l-3.212 0.876 0.877-3.211 0.042-0.123c0.05-0.12 0.123-0.229 0.215-0.321zm12.128-9.606l0.11 0.12c0.584 0.7 0.547 1.744-0.11 2.402l-0.861 0.86-2.521-2.521 0.86-0.86 0.12-0.11c0.7-0.585 1.744-0.548 2.402 0.11zM11.648 3H2.5C2.224 3 2 3.224 2 3.5S2.224 4 2.5 4h8.148l1-1zm-3 3H2.5C2.224 6 2 6.223 2 6.5 2 6.776 2.224 7 2.5 7h5.148l1-1zm-4 4l1-1H2.5C2.224 9 2 9.223 2 9.5 2 9.776 2.224 10 2.5 10h2.148z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
<path android:pathData="M13.94 5l5.061 5.06L9.063 20c-0.277 0.277-0.621 0.477-1 0.58l-5.115 1.395c-0.56 0.153-1.073-0.361-0.92-0.921l1.394-5.116c0.103-0.377 0.303-0.722 0.58-0.999L13.94 5zm-7.414 6l-1.5 1.5H2.75C2.337 12.5 2 12.165 2 11.75 2 11.336 2.337 11 2.75 11h3.775zm14.352-8.174l0.153 0.144 0.145 0.153c1.25 1.405 1.203 3.56-0.145 4.908L20.061 9 15 3.94l0.97-0.97c1.348-1.348 3.503-1.396 4.908-0.144zM10.526 7l-1.5 1.5H2.75C2.337 8.5 2 8.165 2 7.75 2 7.336 2.337 7 2.75 7h7.775zm4-4l-1.5 1.5H2.75C2.337 4.5 2 4.165 2 3.75 2 3.336 2.337 3 2.75 3h11.775z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
<path android:pathData="M20.878 2.826l0.153 0.144 0.145 0.153c1.25 1.405 1.203 3.56-0.145 4.908L9.063 19.999c-0.277 0.277-0.621 0.477-1 0.58l-5.115 1.395c-0.56 0.153-1.073-0.361-0.92-0.921l1.394-5.116c0.103-0.377 0.303-0.722 0.58-0.999L15.97 2.97c1.348-1.348 3.503-1.396 4.908-0.144zM15.001 6.06l-9.938 9.938c-0.092 0.092-0.16 0.207-0.193 0.333l-1.05 3.85 3.85-1.05c0.125-0.035 0.24-0.101 0.332-0.194L17.94 9l-2.939-2.94zM6.526 11l-1.5 1.5H2.75C2.337 12.5 2 12.165 2 11.75 2 11.336 2.337 11 2.75 11h3.775zm4-4l-1.5 1.5H2.75C2.337 8.5 2 8.165 2 7.75 2 7.336 2.337 7 2.75 7h7.775zm6.505-2.97L16.061 5 19 7.94l0.97-0.97c0.812-0.812 0.812-2.128 0-2.94-0.811-0.811-2.127-0.811-2.939 0zM14.526 3l-1.5 1.5H2.75C2.337 4.5 2 4.165 2 3.75 2 3.336 2.337 3 2.75 3h11.775z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="28dp" android:height="28dp" android:viewportWidth="28" android:viewportHeight="28">
<path android:pathData="M19.289 3.15c1.535-1.536 4.025-1.536 5.56 0 1.536 1.535 1.536 4.025 0 5.56l-1.54 1.54-5.56-5.56 1.54-1.54zm-2.6 2.6L4.502 17.937c-0.44 0.44-0.76 0.986-0.928 1.586l-1.547 5.525c-0.073 0.26 0 0.54 0.192 0.732 0.191 0.192 0.471 0.265 0.732 0.192l5.524-1.547c0.6-0.168 1.147-0.487 1.587-0.928L22.25 11.311l-5.56-5.56z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
<path android:pathData="M12 9.005c2.209 0 4 1.79 4 4 0 2.209-1.791 4-4 4-2.21 0-4-1.791-4-4 0-2.21 1.79-4 4-4zm0 1.5c-1.38 0-2.5 1.119-2.5 2.5 0 1.38 1.12 2.5 2.5 2.5s2.5-1.12 2.5-2.5c0-1.381-1.12-2.5-2.5-2.5zM12 5.5c4.613 0 8.596 3.15 9.701 7.564 0.1 0.402-0.144 0.81-0.545 0.91-0.402 0.1-0.81-0.143-0.91-0.545C19.307 9.678 15.92 7 12 7c-3.923 0-7.31 2.68-8.247 6.433-0.1 0.402-0.508 0.646-0.91 0.546-0.401-0.1-0.646-0.507-0.546-0.91C3.4 8.654 7.384 5.5 12 5.5z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="28dp" android:height="28dp" android:viewportWidth="28" android:viewportHeight="28">
<path android:pathData="M25.256 16h0.006-0.01 0.005zm-0.704-0.52c0.1 0.318 0.387 0.518 0.704 0.52 0.07 0 0.148-0.02 0.226-0.04 0.39-0.12 0.61-0.55 0.48-0.94C25.932 14.93 22.932 6 14 6S2.067 14.93 2.037 15.02c-0.13 0.39 0.09 0.81 0.48 0.94 0.4 0.13 0.82-0.09 0.95-0.48l0.003-0.005C3.603 15.085 6.207 7.5 14.01 7.5c7.842 0 10.432 7.65 10.542 7.98zM10.5 16c0-1.933 1.567-3.5 3.5-3.5s3.5 1.567 3.5 3.5-1.567 3.5-3.5 3.5-3.5-1.567-3.5-3.5zm3.5-5c-2.761 0-5 2.239-5 5s2.239 5 5 5 5-2.239 5-5-2.239-5-5-5z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
<path android:pathData="M2.22 2.22C1.953 2.486 1.93 2.903 2.147 3.196L2.22 3.28l4.034 4.035c-1.922 1.35-3.358 3.365-3.955 5.75-0.1 0.401 0.143 0.809 0.545 0.91 0.402 0.1 0.81-0.144 0.91-0.546 0.53-2.116 1.837-3.89 3.58-5.034l1.81 1.81C8.436 10.924 8 11.914 8 13.004c0 2.209 1.79 4 4 4 1.09 0 2.079-0.437 2.8-1.144l5.92 5.92c0.293 0.292 0.767 0.292 1.06 0 0.267-0.267 0.29-0.684 0.073-0.977L21.78 20.72l-6.113-6.114 0.001-0.002-1.2-1.198-2.87-2.87H11.6L8.719 7.658 8.72 7.656l-1.133-1.13L3.28 2.22c-0.293-0.293-0.767-0.293-1.06 0zm7.984 9.046L13.74 14.8c-0.45 0.435-1.063 0.704-1.739 0.704-1.38 0-2.5-1.12-2.5-2.5 0-0.676 0.268-1.29 0.704-1.74zM12 5.5c-1 0-1.97 0.148-2.889 0.425l1.237 1.236C10.884 7.055 11.436 7 12 7c3.923 0 7.31 2.68 8.247 6.433 0.1 0.402 0.508 0.646 0.91 0.546 0.401-0.1 0.646-0.507 0.545-0.91C20.6 8.654 16.615 5.5 12 5.5zm0.195 3.51l3.801 3.8c-0.102-2.057-1.749-3.702-3.801-3.8z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
<path android:pathData="M20 9.502V8.75c0-1.243-1.007-2.25-2.25-2.25h-5.725l-2.38-1.98C9.24 4.184 8.73 4 8.204 4H4.25C3.008 4 2 5.007 2 6.25l-0.004 11.5c0 1.242 1.007 2.25 2.25 2.25H18.47c0.803 0 1.503-0.546 1.698-1.325l1.75-6.998c0.276-1.105-0.56-2.175-1.698-2.175H20zM4.25 5.5h3.956c0.175 0 0.344 0.061 0.48 0.173l2.588 2.154C11.41 7.939 11.58 8 11.755 8h5.996c0.415 0 0.75 0.336 0.75 0.75v0.752H6.424c-1.032 0-1.932 0.703-2.183 1.704l-0.744 2.978L3.5 6.25c0-0.414 0.336-0.75 0.75-0.75zm1.447 6.07c0.083-0.334 0.383-0.568 0.727-0.568H20.22c0.162 0 0.282 0.153 0.242 0.31l-1.75 6.999c-0.027 0.111-0.128 0.189-0.242 0.189H4.285c-0.163 0-0.282-0.153-0.243-0.31l1.655-6.62z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
<path android:pathData="M12 1.998c5.524 0 10.002 4.478 10.002 10.002 0 5.523-4.478 10-10.002 10-5.524 0.001-10.002-4.477-10.002-10C1.998 6.476 6.476 1.998 12 1.998zm2.939 14.501H9.061c0.652 2.415 1.786 4.002 2.94 4.002 1.153 0 2.286-1.588 2.938-4.002zm-7.43 0H4.785c0.958 1.534 2.392 2.74 4.094 3.411-0.522-0.82-0.953-1.846-1.27-3.015L7.508 16.5zm11.705 0h-2.722c-0.324 1.335-0.792 2.5-1.373 3.411 1.597-0.63 2.957-1.729 3.91-3.127l0.185-0.283zM7.094 10H3.735L3.73 10.016C3.58 10.652 3.5 11.316 3.5 12c0 1.056 0.192 2.067 0.544 3h3.173c-0.142-0.95-0.218-1.958-0.218-3 0-0.684 0.033-1.354 0.095-2.001zm8.303 0H8.603c-0.068 0.64-0.104 1.31-0.104 2 0 1.06 0.086 2.07 0.24 3h6.523c0.153-0.93 0.24-1.94 0.24-3 0-0.69-0.037-1.36-0.105-2zm4.868 0h-3.358c0.062 0.647 0.095 1.317 0.095 2 0 1.043-0.076 2.051-0.218 3h3.173c0.352-0.932 0.545-1.943 0.545-3 0-0.689-0.082-1.359-0.237-2zM8.88 4.088L8.858 4.097C6.811 4.912 5.155 6.5 4.25 8.5h3.048c0.314-1.752 0.86-3.278 1.583-4.41zM12 3.497l-0.117 0.005C10.62 3.62 9.396 5.621 8.83 8.5h6.342c-0.566-2.87-1.783-4.869-3.045-4.995L12 3.498zm3.12 0.59l0.106 0.175c0.67 1.112 1.177 2.572 1.475 4.237h3.048c-0.865-1.912-2.416-3.447-4.339-4.29l-0.29-0.121z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="28dp" android:height="28dp" android:viewportWidth="28" android:viewportHeight="28">
<path android:pathData="M26 7.5c0 3.59-2.91 6.5-6.5 6.5S13 11.09 13 7.5 15.91 1 19.5 1 26 3.91 26 7.5zm-9.146-3.354c-0.196-0.195-0.512-0.195-0.708 0-0.195 0.196-0.195 0.512 0 0.708L18.793 7.5l-2.647 2.646c-0.195 0.196-0.195 0.512 0 0.708 0.196 0.195 0.512 0.195 0.708 0L19.5 8.207l2.646 2.647c0.196 0.195 0.512 0.195 0.708 0 0.195-0.196 0.195-0.512 0-0.708L20.207 7.5l2.647-2.646c0.195-0.196 0.195-0.512 0-0.708-0.196-0.195-0.512-0.195-0.708 0L19.5 6.793l-2.646-2.647zM25 22.75V12.6c-0.443 0.476-0.947 0.896-1.5 1.245V16h-6l-0.102 0.007c-0.366 0.05-0.648 0.363-0.648 0.743 0 1.519-1.231 2.75-2.75 2.75s-2.75-1.231-2.75-2.75l-0.007-0.102C11.193 16.282 10.88 16 10.5 16h-6V7.25c0-0.966 0.784-1.75 1.75-1.75h6.02c0.145-0.525 0.345-1.028 0.595-1.5H6.25C4.455 4 3 5.455 3 7.25v15.5C3 24.545 4.455 26 6.25 26h15.5c1.795 0 3.25-1.455 3.25-3.25zm-20.5 0V17.5h5.316l0.041 0.204C10.291 19.592 11.982 21 14 21l0.215-0.005c1.994-0.1 3.627-1.574 3.969-3.495H23.5v5.25c0 0.966-0.784 1.75-1.75 1.75H6.25c-0.966 0-1.75-0.784-1.75-1.75z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="28dp" android:height="28dp" android:viewportWidth="28" android:viewportHeight="28">
<path android:pathData="M21 16c1.657 0 3 1.343 3 3v0.715C24 23.292 19.79 26 14 26S4 23.433 4 19.715V19c0-1.657 1.343-3 3-3h14zm0 1.5H7c-0.78 0-1.42 0.595-1.493 1.355L5.5 19v0.715c0 2.674 3.389 4.785 8.5 4.785 4.926 0 8.355-2.105 8.495-4.624l0.005-0.161V19c0-0.78-0.595-1.42-1.355-1.493L21 17.5zM14 2c3.314 0 6 2.686 6 6s-2.686 6-6 6-6-2.686-6-6 2.686-6 6-6zm0 1.5c-2.485 0-4.5 2.015-4.5 4.5s2.015 4.5 4.5 4.5 4.5-2.015 4.5-4.5-2.015-4.5-4.5-4.5z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
<path android:pathData="M17.5 12c3.037 0 5.5 2.463 5.5 5.5 0 3.038-2.463 5.5-5.5 5.5-3.038 0-5.5-2.462-5.5-5.5 0-3.037 2.462-5.5 5.5-5.5zm-5.478 2c-0.297 0.463-0.536 0.966-0.709 1.5h-7.06c-0.414 0-0.75 0.336-0.75 0.75v0.907c0 0.656 0.286 1.28 0.784 1.707C5.545 19.945 7.44 20.5 10 20.5c0.599 0 1.161-0.03 1.688-0.09 0.249 0.5 0.563 0.963 0.929 1.38C11.815 21.93 10.942 22 10 22c-2.89 0-5.128-0.656-6.691-2-0.829-0.712-1.306-1.75-1.306-2.844V16.25c0-1.242 1.008-2.25 2.25-2.25h7.77zm3.07 0.966l-0.068 0.058-0.058 0.07c-0.118 0.17-0.118 0.398 0 0.568l0.058 0.07 1.77 1.769-1.768 1.767-0.058 0.069c-0.118 0.17-0.118 0.398 0 0.569l0.058 0.069 0.07 0.058c0.17 0.118 0.398 0.118 0.568 0l0.07-0.058 1.766-1.767 1.77 1.77 0.069 0.057c0.17 0.118 0.398 0.118 0.568 0l0.07-0.058 0.057-0.07c0.118-0.17 0.118-0.397 0-0.568l-0.058-0.069-1.769-1.77 1.772-1.768 0.058-0.07c0.118-0.17 0.118-0.398 0-0.568l-0.058-0.07-0.07-0.057c-0.17-0.119-0.397-0.119-0.568 0l-0.069 0.057-1.772 1.77-1.77-1.77-0.069-0.057c-0.146-0.102-0.334-0.116-0.492-0.044l-0.076 0.043zM10 2.005c2.761 0 5 2.239 5 5s-2.239 5-5 5-5-2.239-5-5 2.239-5 5-5zm0 1.5c-1.933 0-3.5 1.567-3.5 3.5s1.567 3.5 3.5 3.5 3.5-1.567 3.5-3.5-1.567-3.5-3.5-3.5z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
<path android:pathData="M17.5 12c3.038 0 5.5 2.463 5.5 5.5 0 3.038-2.462 5.5-5.5 5.5-3.037 0-5.5-2.462-5.5-5.5 0-3.037 2.463-5.5 5.5-5.5zm-5.477 2c-0.297 0.463-0.537 0.966-0.709 1.5h-7.06c-0.414 0-0.75 0.336-0.75 0.75v0.907c0 0.656 0.286 1.28 0.783 1.707C5.545 19.945 7.441 20.5 10 20.5c0.6 0 1.162-0.03 1.688-0.09 0.25 0.5 0.563 0.963 0.93 1.38C11.814 21.93 10.941 22 10 22c-2.89 0-5.128-0.656-6.69-2-0.83-0.712-1.306-1.75-1.306-2.844V16.25c0-1.242 1.007-2.25 2.25-2.25h7.769zm8.787 1.252l-5.557 5.557c0.64 0.436 1.414 0.691 2.247 0.691 2.21 0 4-1.79 4-4 0-0.833-0.255-1.607-0.69-2.248zM17.5 13.5c-2.209 0-4 1.791-4 4 0 0.834 0.255 1.607 0.691 2.248l5.557-5.557c-0.64-0.436-1.414-0.69-2.248-0.69zM10 2.005c2.762 0 5 2.239 5 5s-2.238 5-5 5c-2.761 0-5-2.239-5-5s2.239-5 5-5zm0 1.5c-1.933 0-3.5 1.567-3.5 3.5s1.567 3.5 3.5 3.5 3.5-1.567 3.5-3.5-1.567-3.5-3.5-3.5z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="28dp" android:height="28dp" android:viewportWidth="28" android:viewportHeight="28">
<path android:pathData="M15.114 25.719c-0.396-0.408-0.746-0.861-1.04-1.35C13.418 24.453 12.725 24.5 12 24.5c-5.111 0-8.5-2.111-8.5-4.785V19l0.007-0.145C3.58 18.095 4.22 17.5 5 17.5h8.624c0.234-0.535 0.529-1.038 0.875-1.5H5c-1.657 0-3 1.343-3 3v0.715C2 23.433 6.21 26 12 26c1.101 0 2.145-0.098 3.114-0.281zM18 8c0-3.314-2.686-6-6-6S6 4.686 6 8s2.686 6 6 6 6-2.686 6-6zM7.5 8c0-2.485 2.015-4.5 4.5-4.5s4.5 2.015 4.5 4.5-2.015 4.5-4.5 4.5S7.5 10.485 7.5 8zM27 20.5c0 3.59-2.91 6.5-6.5 6.5S14 24.09 14 20.5s2.91-6.5 6.5-6.5 6.5 2.91 6.5 6.5zm-1.5 0c0-1.11-0.362-2.136-0.974-2.965l-6.991 6.991c0.83 0.612 1.855 0.974 2.965 0.974 2.761 0 5-2.239 5-5zm-9.026 2.965l6.991-6.991C22.635 15.862 21.61 15.5 20.5 15.5c-2.761 0-5 2.239-5 5 0 1.11 0.362 2.136 0.974 2.965z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
<path android:pathData="M15.75 13.997c0.981 0 1.815 0.628 2.123 1.503h-2.056l-0.066-0.003H4.249c-0.414 0-0.749 0.335-0.749 0.75v0.577c0 0.535 0.191 1.054 0.539 1.46 1.253 1.469 3.22 2.214 5.957 2.214h0.168c0.086 0.183 0.203 0.352 0.35 0.498l0.934 0.933c-0.464 0.046-0.948 0.07-1.452 0.07-3.145 0-5.531-0.906-7.098-2.74C2.318 18.58 2 17.717 2 16.824v-0.578c0-1.242 1.007-2.249 2.249-2.249H15.75zM9.997 2.002c2.762 0 5 2.239 5 5s-2.238 5-5 5c-2.761 0-5-2.239-5-5s2.239-5 5-5zm0 1.5c-1.933 0-3.5 1.567-3.5 3.5s1.567 3.5 3.5 3.5c1.934 0 3.5-1.567 3.5-3.5s-1.566-3.5-3.5-3.5zm4.784 14.282c0.293-0.293 0.293-0.768 0-1.061-0.293-0.293-0.768-0.293-1.06 0l-2.5 2.501c-0.141 0.14-0.22 0.332-0.22 0.53 0 0.2 0.079 0.39 0.22 0.53l2.5 2.5c0.292 0.293 0.767 0.293 1.06 0 0.293-0.293 0.293-0.768 0-1.061l-1.22-1.22h6.88l-1.22 1.22c-0.293 0.293-0.293 0.768 0 1.06 0.292 0.293 0.767 0.294 1.06 0l2.5-2.498c0.14-0.14 0.22-0.33 0.22-0.53 0-0.199-0.08-0.39-0.22-0.53l-2.5-2.502c-0.293-0.293-0.767-0.293-1.06 0-0.293 0.293-0.293 0.768 0 1.06l1.218 1.22h-6.877l1.22-1.22z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
<path android:pathData="M16.242 2.932l4.826 4.826c1.327 1.327 0.964 3.564-0.715 4.404l-4.87 2.435c-0.176 0.088-0.31 0.24-0.374 0.426l-1.44 4.166c-0.3 0.873-1.412 1.13-2.065 0.476L8.5 16.561 4.06 21H3v-1.062L7.44 15.5l-3.105-3.104c-0.653-0.653-0.397-1.764 0.476-2.066l4.166-1.439c0.185-0.064 0.338-0.198 0.426-0.373l2.435-4.871c0.84-1.68 3.077-2.042 4.405-0.715zm3.766 5.886l-4.826-4.825c-0.604-0.604-1.62-0.439-2.002 0.324l-2.435 4.871c-0.264 0.526-0.722 0.929-1.278 1.12l-3.789 1.31 6.705 6.704 1.308-3.788c0.192-0.557 0.595-1.015 1.12-1.278l4.872-2.436c0.763-0.381 0.928-1.398 0.325-2.002z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="28dp" android:height="28dp" android:viewportWidth="28" android:viewportHeight="28">
<path android:pathData="M13.803 4.385c0.963-2.031 3.647-2.496 5.236-0.907l5.483 5.483c1.589 1.59 1.124 4.273-0.907 5.235l-5.39 2.553c-0.406 0.193-0.723 0.534-0.884 0.954l-1.589 4.13c-0.454 1.182-1.975 1.505-2.87 0.609L9.75 19.31 4.06 25H3v-1.06l5.69-5.69-3.132-3.132c-0.896-0.895-0.573-2.416 0.609-2.87l4.13-1.59c0.42-0.16 0.761-0.477 0.953-0.883l2.553-5.39zm4.175 0.154c-0.856-0.856-2.3-0.606-2.819 0.488l-2.553 5.39c-0.357 0.754-0.991 1.342-1.77 1.642l-4.13 1.588c-0.17 0.065-0.216 0.283-0.088 0.41l7.324 7.324c0.128 0.128 0.345 0.082 0.41-0.087l1.589-4.13c0.3-0.779 0.887-1.413 1.642-1.77l5.39-2.553c1.094-0.518 1.344-1.964 0.488-2.82L17.978 4.54z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
<path android:pathData="M3.28 2.22c-0.292-0.293-0.767-0.293-1.06 0-0.293 0.293-0.293 0.767 0 1.06l5.905 5.905L4.81 10.33c-0.873 0.302-1.129 1.413-0.476 2.065L7.439 15.5 3 19.94V21h1.06l4.44-4.44 3.105 3.105c0.652 0.653 1.764 0.397 2.065-0.476l1.145-3.313 5.905 5.904c0.293 0.293 0.767 0.293 1.06 0 0.293-0.293 0.293-0.767 0-1.06L3.28 2.22zm10.355 12.476l-1.252 3.626-6.705-6.705 3.626-1.252 4.331 4.331zm6.048-3.876l-3.787 1.894 1.118 1.118 3.34-1.67c1.679-0.84 2.042-3.077 0.714-4.405l-4.825-4.825c-1.328-1.327-3.565-0.964-4.405 0.715l-1.67 3.34 1.118 1.117 1.894-3.787c0.382-0.763 1.399-0.928 2.002-0.325l4.826 4.826c0.603 0.604 0.438 1.62-0.325 2.002z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="28dp" android:height="28dp" android:viewportWidth="28" android:viewportHeight="28">
<path android:pathData="M3.28 2.22c-0.293-0.293-0.767-0.293-1.06 0-0.293 0.293-0.293 0.767 0 1.06l7.572 7.573-3.625 1.394c-1.182 0.455-1.505 1.976-0.61 2.871L8.69 18.25 3 23.94V25h1.06l5.69-5.69 3.132 3.132c0.895 0.895 2.416 0.573 2.87-0.61l1.395-3.624 7.572 7.573c0.293 0.292 0.768 0.292 1.061 0 0.293-0.293 0.293-0.768 0-1.061L3.28 2.22zm12.708 14.829l-0.047 0.115-1.589 4.13c-0.065 0.17-0.282 0.215-0.41 0.087l-7.324-7.324c-0.128-0.127-0.081-0.345 0.087-0.41l4.13-1.588c0.04-0.015 0.078-0.03 0.116-0.047l5.037 5.037zm-4.19-8.432l1.127 1.126 2.234-4.716c0.518-1.094 1.963-1.344 2.82-0.488l5.482 5.483c0.856 0.855 0.606 2.3-0.488 2.819l-4.716 2.234 1.126 1.126 4.232-2.005c2.031-0.962 2.496-3.646 0.907-5.235l-5.483-5.483c-1.59-1.59-4.273-1.124-5.236 0.907L11.8 8.617z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="28dp" android:height="28dp" android:viewportWidth="28" android:viewportHeight="28">
<path android:pathData="M13.56 2.142c0.262-0.19 0.616-0.19 0.878 0 0.643 0.464 2.088 1.312 3.897 2.041 1.81 0.73 3.922 1.317 5.913 1.317 0.414 0 0.75 0.336 0.75 0.75v7.752c0 3.027-1.703 5.841-3.838 7.95-2.133 2.107-4.827 3.64-7.033 4.024L14 25.998l-0.129-0.022c-2.205-0.385-4.9-1.917-7.033-4.024C4.703 19.843 3 17.029 3 14.002V6.25C3 5.836 3.336 5.5 3.75 5.5c1.991 0 4.103-0.587 5.914-1.317 1.808-0.73 3.253-1.576 3.896-2.04zM4.5 6.977v7.025c0 2.47 1.408 4.923 3.391 6.882C9.83 22.8 12.193 24.118 14 24.474c1.806-0.356 4.17-1.675 6.107-3.59 1.983-1.959 3.392-4.411 3.392-6.882V6.977c-2.014-0.124-4.032-0.72-5.724-1.403-1.589-0.64-2.934-1.375-3.775-1.918-0.84 0.543-2.186 1.278-3.774 1.918-1.693 0.683-3.71 1.28-5.725 1.403z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
<path android:pathData="M3.75 5C3.336 5 3 5.336 3 5.75V11c0 5.001 2.958 8.676 8.725 10.948 0.177 0.07 0.373 0.07 0.55 0 0.144-0.057 0.286-0.114 0.426-0.173-0.659-0.475-1.225-1.071-1.667-1.756C6.64 17.962 4.5 14.975 4.5 11V6.478c2.577-0.152 5.08-1.09 7.5-2.8 2.42 1.71 4.923 2.648 7.5 2.8v4.254c0.54 0.282 1.037 0.638 1.475 1.054C20.992 11.528 21 11.266 21 11V5.75C21 5.336 20.664 5 20.25 5c-2.663 0-5.258-0.944-7.8-2.85-0.267-0.2-0.633-0.2-0.9 0C9.008 4.056 6.413 5 3.75 5zM16.5 22c3.038 0 5.5-2.462 5.5-5.5S19.538 11 16.5 11 11 13.462 11 16.5s2.462 5.5 5.5 5.5zm-3.309-3.252c-0.436-0.64-0.691-1.415-0.691-2.248 0-2.21 1.79-4 4-4 0.834 0 1.608 0.255 2.248 0.691l-5.557 5.557zm1.06 1.06l5.558-5.556c0.436 0.64 0.691 1.414 0.691 2.248 0 2.21-1.79 4-4 4-0.834 0-1.607-0.255-2.248-0.691z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

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

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="28dp" android:height="28dp" android:viewportWidth="28" android:viewportHeight="28">
<path android:pathData="M16.5 4.814c0-1.094-1.307-1.66-2.105-0.912l-4.937 4.63C9.134 8.836 8.706 9.005 8.261 9.005H5.25C3.455 9.005 2 10.46 2 12.255v3.492c0 1.795 1.455 3.25 3.25 3.25h3.012c0.444 0 0.872 0.17 1.196 0.473l4.937 4.626c0.799 0.748 2.105 0.182 2.105-0.912V4.814zm-6.016 4.812L15 5.39v17.216l-4.516-4.232c-0.602-0.564-1.397-0.878-2.222-0.878H5.25c-0.966 0-1.75-0.784-1.75-1.75v-3.492c0-0.966 0.784-1.75 1.75-1.75h3.011c0.826 0 1.62-0.314 2.223-0.88z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

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

View File

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

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="28dp" android:height="28dp" android:viewportWidth="28" android:viewportHeight="28">
<path android:pathData="M12.701 3.908c0.532-1.078 2.069-1.078 2.6 0l2.692 5.452 6.017 0.875c1.19 0.173 1.664 1.634 0.804 2.473l-4.355 4.244 1.028 5.993c0.204 1.185-1.04 2.088-2.103 1.529L14 21.644l-5.381 2.83c-1.064 0.559-2.307-0.344-2.104-1.529l1.028-5.993-4.354-4.244c-0.861-0.839-0.386-2.3 0.803-2.473L10.01 9.36l2.691-5.452zm1.3 0.755l-2.657 5.385c-0.211 0.428-0.62 0.724-1.092 0.793l-5.943 0.864 4.3 4.191c0.342 0.334 0.498 0.813 0.417 1.284l-1.015 5.919 5.316-2.795c0.422-0.222 0.927-0.222 1.35 0l5.315 2.795-1.015-5.92c-0.081-0.47 0.075-0.95 0.417-1.283l4.3-4.191-5.943-0.864c-0.472-0.069-0.88-0.365-1.092-0.793l-2.657-5.385z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
<path android:pathData="M13.209 3.102c-0.495-1.003-1.926-1.003-2.421 0L8.43 7.88 3.157 8.646c-1.107 0.16-1.55 1.522-0.748 2.303l3.815 3.719-0.9 5.25c-0.19 1.104 0.968 1.945 1.958 1.424l4.716-2.48 4.716 2.48c0.99 0.52 2.148-0.32 1.96-1.423l-0.902-5.251 3.816-3.72c0.8-0.78 0.359-2.141-0.748-2.302L15.567 7.88l-2.358-4.778zM9.74 8.614l2.258-4.576 2.259 4.576c0.196 0.399 0.577 0.675 1.016 0.739l5.05 0.734-3.654 3.562c-0.318 0.31-0.463 0.757-0.388 1.195l0.862 5.029-4.516-2.375c-0.394-0.207-0.864-0.207-1.257 0l-4.516 2.375 0.862-5.03c0.075-0.438-0.07-0.884-0.388-1.194l-3.654-3.562 5.05-0.734c0.44-0.064 0.82-0.34 1.016-0.739zM1.164 3.781C0.906 4.104 0.958 4.576 1.282 4.835l2.5 2c0.323 0.259 0.795 0.207 1.054-0.117 0.258-0.323 0.206-0.795-0.117-1.054l-2.5-2C1.895 3.405 1.423 3.458 1.164 3.78zm21.672 14.437c0.259-0.323 0.206-0.795-0.117-1.054l-2.5-2c-0.324-0.259-0.796-0.206-1.055 0.117-0.258 0.323-0.206 0.795 0.117 1.054l2.5 2c0.324 0.259 0.796 0.206 1.055-0.117zM1.282 17.164c-0.324 0.259-0.376 0.73-0.118 1.054 0.26 0.323 0.731 0.376 1.055 0.117l2.5-2c0.323-0.259 0.375-0.73 0.117-1.054-0.26-0.323-0.731-0.376-1.055-0.117l-2.5 2zM22.835 3.78c0.259 0.323 0.206 0.795-0.117 1.054l-2.5 2c-0.324 0.259-0.796 0.207-1.055-0.117-0.258-0.323-0.206-0.795 0.117-1.054l2.5-2c0.324-0.259 0.796-0.206 1.055 0.117z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
<path android:pathData="M6.78 4.78c0.293-0.293 0.293-0.767 0-1.06-0.293-0.293-0.767-0.293-1.06 0L3.75 5.69 3.28 5.22c-0.293-0.293-0.767-0.293-1.06 0-0.293 0.293-0.293 0.767 0 1.06l1 1c0.293 0.293 0.767 0.293 1.06 0l2.5-2.5zm14.47 13.227H9.75l-0.102 0.007C9.282 18.064 9 18.377 9 18.757c0 0.414 0.336 0.75 0.75 0.75h11.5l0.102-0.007C21.718 19.45 22 19.137 22 18.757c0-0.414-0.336-0.75-0.75-0.75zm0-6.507H9.75l-0.102 0.007C9.282 11.557 9 11.87 9 12.25 9 12.664 9.336 13 9.75 13h11.5l0.102-0.007C21.718 12.943 22 12.63 22 12.25c0-0.414-0.336-0.75-0.75-0.75zm0-6.5H9.75L9.648 5.007C9.282 5.057 9 5.37 9 5.75 9 6.164 9.336 6.5 9.75 6.5h11.5l0.102-0.007C21.718 6.443 22 6.13 22 5.75 22 5.336 21.664 5 21.25 5zM6.78 17.78c0.293-0.293 0.293-0.767 0-1.06-0.293-0.293-0.767-0.293-1.06 0l-1.97 1.97-0.47-0.47c-0.293-0.293-0.767-0.293-1.06 0-0.293 0.293-0.293 0.767 0 1.06l1 1c0.293 0.293 0.767 0.293 1.06 0l2.5-2.5zm0-7.56c0.293 0.293 0.293 0.767 0 1.06l-2.5 2.5c-0.293 0.293-0.767 0.293-1.06 0l-1-1c-0.293-0.293-0.293-0.767 0-1.06 0.293-0.293 0.767-0.293 1.06 0l0.47 0.47 1.97-1.97c0.293-0.293 0.767-0.293 1.06 0z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

@@ -0,0 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
<path android:pathData="M13 17c0-0.551-0.448-0.998-1-0.998-0.55 0-0.998 0.447-0.998 0.998 0 0.552 0.447 1 0.999 1 0.551 0 0.998-0.448 0.998-1zm-0.26-7.853C12.69 8.781 12.376 8.5 11.996 8.5c-0.414 0-0.75 0.337-0.749 0.751l0.004 4.502 0.007 0.101c0.05 0.367 0.364 0.648 0.743 0.648 0.414 0 0.75-0.336 0.75-0.75L12.747 9.25 12.74 9.148zm1.23-5.488c-0.857-1.547-3.082-1.547-3.938 0L2.286 17.661C1.456 19.16 2.541 21 4.256 21h15.49c1.714 0 2.799-1.84 1.969-3.34l-7.746-14zm-2.626 0.727c0.286-0.516 1.027-0.517 1.313 0l7.745 14c0.277 0.5-0.085 1.114-0.656 1.114H4.256c-0.572 0-0.934-0.613-0.657-1.113l7.745-14.002z" android:fillColor="@color/fluent_default_icon_tint"/>
</vector>

View File

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

View File

@@ -0,0 +1,61 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:clipToPadding="false"
android:paddingHorizontal="16dp"
android:paddingTop="4dp"
android:paddingBottom="8dp">
<ProgressBar
android:id="@+id/send_progress"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginEnd="16dp"
android:layout_gravity="center_vertical"
android:visibility="gone" />
<ImageView
android:id="@+id/send_error"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_gravity="center_vertical"
android:src="@drawable/ic_fluent_error_circle_24_regular"
android:tint="@color/error_600"
android:scaleType="center"
android:visibility="gone" />
<Button
android:id="@+id/language_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingHorizontal="8dp"
android:drawableStart="@drawable/ic_fluent_local_language_16_regular"
android:drawablePadding="8dp"
android:drawableTint="?android:textColorSecondary"
android:textColor="?android:textColorSecondary"
android:background="@drawable/bg_text_button" />
<Button
android:id="@+id/drafts_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:paddingHorizontal="8dp"
android:drawableStart="@drawable/ic_fluent_clock_20_regular"
android:drawableTint="?android:textColorSecondary"
android:textColor="?android:textColorSecondary"
android:background="@drawable/bg_text_button"
android:contentDescription="@string/sk_schedule_or_draft"
android:tooltipText="@string/sk_schedule_or_draft" />
<Button
android:id="@+id/publish_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:ellipsize="end" />
</LinearLayout>

View File

@@ -16,7 +16,7 @@
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:drawableStart="@drawable/ic_fluent_chat_multiple_24_regular"
android:drawableStart="@drawable/ic_fluent_chat_multiple_24_selector_text"
android:drawablePadding="8dp"
android:paddingHorizontal="8dp"
android:drawableTint="?android:textColorSecondary"

View File

@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/display_item_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="10dp"
@@ -56,7 +57,8 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:gravity="center_vertical">
android:gravity="center_vertical"
android:visibility="gone">
<FrameLayout
android:id="@+id/action_btn_wrap"
@@ -72,7 +74,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingHorizontal="8dp"
tools:text="@string/translate_post"/>
tools:text="@string/sk_translate_post"/>
<ProgressBar
android:id="@+id/translate_progress"
android:layout_width="wrap_content"

View File

@@ -6,6 +6,7 @@
android:layout_height="match_parent">
<ScrollView
android:id="@+id/scroll_view"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
@@ -16,6 +17,28 @@
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:id="@+id/original_post"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="gone"
android:background="?android:selectableItemBackground">
<include layout="@layout/display_item_header" />
<include layout="@layout/display_item_text" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?attr/colorPollVoted"/>
</LinearLayout>
<LinearLayout
android:id="@+id/reply_wrap"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:id="@+id/content_warning"
android:layout_width="match_parent"
@@ -56,7 +79,7 @@
android:paddingLeft="16dp">
<ImageView
android:id="@+id/avatar"
android:id="@+id/self_avatar"
android:layout_width="46dp"
android:layout_height="46dp"
android:layout_alignParentStart="true"
@@ -64,21 +87,21 @@
android:layout_marginEnd="12dp" />
<TextView
android:id="@+id/name"
android:id="@+id/self_name"
android:layout_width="match_parent"
android:layout_height="24dp"
android:layout_toEndOf="@id/avatar"
android:layout_toEndOf="@id/self_avatar"
android:ellipsize="end"
android:singleLine="true"
android:textAppearance="@style/m3_title_medium"
tools:text="Eugen" />
<TextView
android:id="@+id/username"
android:id="@+id/self_username"
android:layout_width="match_parent"
android:layout_height="20dp"
android:layout_below="@id/name"
android:layout_toEndOf="@id/avatar"
android:layout_below="@id/self_name"
android:layout_toEndOf="@id/self_avatar"
android:ellipsize="end"
android:singleLine="true"
android:textAppearance="@style/m3_title_small"
@@ -225,19 +248,73 @@
android:text="@string/sk_mark_media_as_sensitive" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</ScrollView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="?colorBackgroundLightest"
android:elevation="2dp"
android:outlineProvider="bounds"
android:paddingLeft="16dp"
android:paddingRight="12dp"
android:layoutDirection="locale">
<LinearLayout
android:id="@+id/schedule_draft_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:minHeight="48dp"
android:paddingTop="4dp">
<TextView
android:id="@+id/schedule_draft_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableStart="@drawable/ic_fluent_drafts_20_regular"
android:drawableTint="?android:textColorSecondary"
android:drawablePadding="16dp"
android:text="@string/sk_compose_draft" />
<Space
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
<Button
android:id="@+id/scheduled_time_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:gravity="center"
android:padding="8dp"
android:textSize="14sp"
android:minHeight="40dp"
android:textColor="?android:textColorSecondary"
android:background="@drawable/bg_text_button"
android:fontFamily="sans-serif"
android:drawableStart="@drawable/ic_fluent_clock_20_regular"
android:drawablePadding="8dp"
android:drawableTint="?android:textColorSecondary"
tools:text="Dec 12, 2021, 12:42 PM"/>
<ImageButton
android:id="@+id/schedule_draft_dismiss"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginStart="8dp"
android:src="@drawable/ic_fluent_dismiss_20_filled"
android:background="?android:selectableItemBackgroundBorderless"
android:contentDescription="@string/sk_compose_no_schedule"
android:padding="4dp"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="48dp"
android:orientation="horizontal"
android:gravity="center_vertical"
android:background="?colorBackgroundLightest"
android:elevation="2dp"
android:outlineProvider="bounds"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:layoutDirection="locale">
android:gravity="center_vertical">
<ImageButton
android:id="@+id/btn_media"
@@ -316,7 +393,7 @@
android:textAppearance="@style/m3_body_large"
android:textColor="?android:textColorSecondary"
tools:text="500"/>
</LinearLayout>
</LinearLayout>
</org.joinmastodon.android.ui.views.SizeListenerLinearLayout>

View File

@@ -26,6 +26,7 @@
android:layout_marginBottom="16dp"
android:minWidth="145dp"
style="@style/Widget.Mastodon.M3.Button.Filled"
android:visibility="gone"
android:text="@string/i_agree" />
</LinearLayout>

View File

@@ -318,19 +318,7 @@
</LinearLayout>
</org.joinmastodon.android.ui.views.NestedRecyclerScrollView>
<ImageButton
android:id="@+id/fab"
android:layout_width="64dp"
android:layout_height="64dp"
android:layout_gravity="end|bottom"
android:layout_marginEnd="16dp"
android:layout_marginBottom="24dp"
android:background="@drawable/bg_fab"
android:tint="@color/fab_icon"
android:scaleType="center"
android:stateListAnimator="@animator/fab_shadow"
android:src="@drawable/ic_edit_34"/>
<ImageButton android:id="@+id/fab" style="@style/Widget.Mastodon.Button.Compose"/>
</FrameLayout>
</me.grishka.appkit.views.RecursiveSwipeRefreshLayout>

View File

@@ -26,6 +26,49 @@
android:gravity="top|start"
android:minHeight="212dp"/>
<LinearLayout
android:id="@+id/forward_report"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="8dp"
android:paddingVertical="8dp"
android:gravity="center_vertical"
android:layoutDirection="locale"
android:background="?android:selectableItemBackground">
<ImageView
android:id="@+id/icon"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginStart="16dp"
android:layout_marginEnd="32dp"
android:importantForAccessibility="no"
android:tint="?android:textColorPrimary"
android:src="@drawable/ic_fluent_arrow_forward_24_regular"/>
<TextView
android:id="@+id/forward_report_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:paddingVertical="8dp"
android:textSize="16sp"
android:text="@string/sk_forward_report_to"
android:textColor="?android:textColorPrimary" />
<Switch
android:id="@+id/forward_report_switch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:duplicateParentState="true"
android:layout_marginStart="16dp"
android:layout_marginEnd="12dp"
android:focusable="false"
android:clickable="false"/>
</LinearLayout>
</LinearLayout>
</ScrollView>

View File

@@ -78,6 +78,20 @@
android:layout_width="match_parent"
android:layout_marginVertical="8dp"
android:background="?colorPollVoted" />
<TextView
android:id="@+id/reblog_as"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="36dp"
android:paddingVertical="12dp"
android:paddingHorizontal="24dp"
android:gravity="center_vertical"
android:textSize="16sp"
android:textColor="?android:textColorPrimary"
android:drawablePadding="16dp"
android:background="?android:selectableItemBackground"
android:text="@string/sk_reblog_as"
android:drawableStart="@drawable/ic_fluent_person_swap_24_regular" />
<TextView
android:id="@+id/quote"
android:layout_width="match_parent"

View File

@@ -15,7 +15,7 @@
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginEnd="16dp"
android:textColor="?colorM3Primary"
android:textColor="?android:colorAccent"
android:fontFamily="sans-serif-condensed"
android:textStyle="bold"
android:textSize="22dp"

View File

@@ -20,19 +20,6 @@
android:layout_height="match_parent"
android:id="@+id/empty"/>
<ImageButton
android:id="@+id/fab"
android:layout_width="64dp"
android:layout_height="64dp"
android:layout_gravity="end|bottom"
android:layout_marginEnd="16dp"
android:layout_marginBottom="24dp"
android:background="@drawable/bg_fab"
android:tint="@color/fab_icon"
android:scaleType="center"
android:stateListAnimator="@animator/fab_shadow"
android:contentDescription="@string/new_post"
android:src="@drawable/ic_edit_34"/>
<ImageButton android:id="@+id/fab" style="@style/Widget.Mastodon.Button.Compose"/>
</FrameLayout>
</me.grishka.appkit.views.RecursiveSwipeRefreshLayout>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/schedule" android:title="@string/sk_schedule_post" android:icon="@drawable/ic_fluent_clock_24_regular" />
<item android:id="@+id/unschedule" android:title="@string/sk_schedule_post" android:icon="@drawable/ic_fluent_clock_24_filled" android:contentDescription="@string/sk_compose_no_schedule" />
<item android:id="@+id/draft" android:title="@string/sk_mark_as_draft" android:icon="@drawable/ic_fluent_drafts_24_regular" />
<item android:id="@+id/undraft" android:title="@string/sk_mark_as_draft" android:icon="@drawable/ic_fluent_drafts_24_filled" android:contentDescription="@string/sk_compose_no_draft" />
<item android:id="@+id/drafts" android:title="@string/sk_unsent_posts" android:icon="@drawable/ic_fluent_folder_open_24_regular" />
</menu>

View File

@@ -3,7 +3,7 @@
<item
android:id="@+id/follow_requests"
android:icon="@drawable/ic_follow_requests_24_badged"
android:showAsAction="ifRoom"
android:showAsAction="always"
android:visible="false"
android:title="@string/sk_follow_requests" />
<item

View File

@@ -1,19 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/open_with_account" android:title="@string/sk_open_with_account">
<item android:id="@+id/open_with_account" android:title="@string/sk_open_with_account" android:icon="@drawable/ic_fluent_person_swap_24_regular">
<menu android:id="@+id/accounts" />
</item>
<item android:id="@+id/edit" android:title="@string/edit"/>
<item android:id="@+id/delete" android:title="@string/delete"/>
<item android:id="@+id/delete_and_redraft" android:title="@string/sk_delete_and_redraft"/>
<item android:id="@+id/pin" android:title="@string/sk_pin_post"/>
<item android:id="@+id/unpin" android:title="@string/sk_unpin_post"/>
<item android:id="@+id/mute" android:title="@string/mute_user"/>
<item android:id="@+id/block" android:title="@string/block_user"/>
<item android:id="@+id/block_domain" android:title="@string/block_domain"/>
<item android:id="@+id/follow" android:title="@string/follow_user"/>
<item android:id="@+id/report" android:title="@string/report_user"/>
<item android:id="@+id/bookmark" android:title="@string/add_bookmark"/>
<item android:id="@+id/copy_link" android:title="@string/sk_copy_link_to_post"/>
<item android:id="@+id/open_in_browser" android:title="@string/open_in_browser"/>
<item android:id="@+id/edit" android:title="@string/edit" android:icon="@drawable/ic_fluent_edit_24_regular"/>
<item android:id="@+id/delete" android:title="@string/delete" android:icon="@drawable/ic_fluent_delete_24_regular"/>
<item android:id="@+id/delete_and_redraft" android:title="@string/sk_delete_and_redraft" android:icon="@drawable/ic_fluent_arrow_clockwise_24_regular" />
<item android:id="@+id/pin" android:title="@string/sk_pin_post" android:icon="@drawable/ic_fluent_pin_24_regular"/>
<item android:id="@+id/unpin" android:title="@string/sk_unpin_post" android:icon="@drawable/ic_fluent_pin_off_24_regular"/>
<item android:id="@+id/mute" android:title="@string/mute_user" android:icon="@drawable/ic_fluent_speaker_off_24_regular"/>
<item android:id="@+id/block" android:title="@string/block_user" android:icon="@drawable/ic_fluent_person_prohibited_24_regular"/>
<item android:id="@+id/block_domain" android:title="@string/block_domain" android:icon="@drawable/ic_fluent_shield_prohibited_24_regular"/>
<item android:id="@+id/follow" android:title="@string/follow_user" android:icon="@drawable/ic_fluent_person_add_24_regular"/>
<item android:id="@+id/report" android:title="@string/report_user" android:icon="@drawable/ic_fluent_warning_24_regular"/>
<item android:id="@+id/bookmark" android:title="@string/add_bookmark" android:icon="@drawable/ic_fluent_bookmark_24_regular"/>
<item android:id="@+id/copy_link" android:title="@string/sk_copy_link_to_post" android:icon="@drawable/ic_fluent_link_24_regular"/>
<item android:id="@+id/open_in_browser" android:title="@string/open_in_browser" android:icon="@drawable/ic_fluent_globe_24_regular"/>
</menu>

View File

@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/share" android:title="@string/share_user"/>
<item android:id="@+id/mute" android:title="@string/mute_user"/>
<item android:id="@+id/block" android:title="@string/block_user"/>
<item android:id="@+id/report" android:title="@string/report_user"/>
<item android:id="@+id/block_domain" android:title="@string/block_domain"/>
<item android:id="@+id/hide_boosts" android:title="@string/hide_boosts_from_user"/>
<item android:id="@+id/manage_user_lists" android:title="@string/sk_lists_with_user"/>
<item android:id="@+id/open_in_browser" android:title="@string/open_in_browser"/>
<item android:id="@+id/share" android:title="@string/share_user" android:icon="@drawable/ic_fluent_share_24_regular"/>
<item android:id="@+id/mute" android:title="@string/mute_user" android:icon="@drawable/ic_fluent_speaker_off_24_regular"/>
<item android:id="@+id/block" android:title="@string/block_user" android:icon="@drawable/ic_fluent_person_prohibited_24_regular"/>
<item android:id="@+id/report" android:title="@string/report_user" android:icon="@drawable/ic_fluent_warning_24_regular"/>
<item android:id="@+id/block_domain" android:title="@string/block_domain" android:icon="@drawable/ic_fluent_shield_prohibited_24_regular"/>
<item android:id="@+id/hide_boosts" android:title="@string/hide_boosts_from_user" android:icon="@drawable/ic_fluent_arrow_repeat_all_off_24_regular"/>
<item android:id="@+id/manage_user_lists" android:title="@string/sk_lists_with_user" android:icon="@drawable/ic_fluent_people_community_24_regular"/>
<item android:id="@+id/open_in_browser" android:title="@string/open_in_browser" android:icon="@drawable/ic_fluent_globe_24_regular"/>
</menu>

View File

@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/followed_hashtags" android:title="@string/sk_hashtags_you_follow" android:icon="@drawable/ic_fluent_number_symbol_24_regular" android:showAsAction="always"/>
<item android:id="@+id/bookmarks" android:title="@string/bookmarks" android:icon="@drawable/ic_fluent_bookmark_multiple_24_regular" android:showAsAction="always"/>
<item android:id="@+id/favorites" android:title="@string/your_favorites" android:icon="@drawable/ic_fluent_star_24_regular"/>
<item android:id="@+id/followed_hashtags" android:title="@string/sk_hashtags_you_follow" android:icon="@drawable/ic_fluent_number_symbol_24_regular"/>
<item android:id="@+id/scheduled" android:title="@string/sk_unsent_posts" android:icon="@drawable/ic_fluent_folder_open_24_regular"/>
<item android:id="@+id/share" android:title="@string/share_user" android:icon="@drawable/ic_fluent_share_24_regular"/>
</menu>

View File

@@ -58,13 +58,13 @@
<string name="sk_clear_recent_languages">Zuletzt verwendete Sprachen leeren</string>
<string name="sk_welcome_title">Willkommen!</string>
<string name="sk_example_domain">beispiel.social</string>
<string name="sk_welcome_text">Der Hai sagt Hi! Um anzufangen, bitte gib den Domain-Namen deiner Heim-Instanz unten ein.</string>
<string name="sk_welcome_text">Der Hai sagt Hi! Um anzufangen, gib den Domain-Namen deiner Heimat-Instanz unten ein.</string>
<string name="sk_color_palette_material3">System</string>
<string name="sk_color_palette_red">Rot</string>
<string name="sk_settings_profile">Profil einrichten</string>
<string name="sk_settings_posting">Einstellungen für Beiträge</string>
<string name="sk_settings_filters">Filter konfigurieren</string>
<string name="sk_settings_auth">Sicherheits-Einstellungen</string>
<string name="sk_settings_auth">Sicherheits-Einstellungen</string>
<string name="sk_settings_rules">Regelwerk</string>
<string name="sk_settings_about">Über die App</string>
<string name="sk_settings_donate">Spenden</string>
@@ -73,15 +73,51 @@
<string name="sk_delete_notification_confirm">Benachrichtigung wirklich löschen\?</string>
<string name="sk_clear_all_notifications_confirm_action">Alle löschen</string>
<string name="sk_enable_delete_notifications">Löschen von Benachrichtigungen aktivieren</string>
<string name="sk_settings_publish_button_text">Veröffentlichen-Button-Text</string>
<string name="sk_settings_hide_translate_in_timeline">Übersetzen-Button in der Timeline ausblenden</string>
<string name="sk_settings_publish_button_text">Veröffentlichen-Button-Text</string>
<string name="sk_settings_translate_only_opened">Nur geöffnete Beiträge übersetzen</string>
<string name="sk_delete_notification">Benachrichtigung löschen</string>
<string name="sk_clear_all_notifications">Alle Benachrichtigungen löschen</string>
<string name="sk_settings_publish_button_text_title">Veröffentlichen-Button-Text anpassen</string>
<string name="sk_settings_publish_button_text_title">Veröffentlichen-Button-Text anpassen</string>
<string name="sk_clear_all_notifications_confirm">Wirklich alle Benachrichtigungen löschen\?</string>
<string name="sk_settings_translation_availability_note_available">%s unterstützt Übersetzung!</string>
<string name="sk_settings_translation_availability_note_unavailable">%s scheint keine Übersetzung zu unterstützen.</string>
<string name="sk_loading_fediverse_resource_title">Suche im Fediverse</string>
<string name="sk_loading_fediverse_resource_title">Suche im Fediverse</string>
<string name="sk_undo_reblog">Reblog rückgängig machen</string>
<string name="sk_reblog_with_visibility">Rebloggen mit Sichtbarkeit</string>
<string name="sk_quote_post">Drüberkommentieren</string>
<string name="sk_hashtags_you_follow">Hashtags, denen du folgst</string>
<string name="sk_copy_link_to_post">Link zum Beitrag kopieren</string>
<string name="sk_open_with_account">Mit anderem Konto öffnen</string>
<string name="sk_resource_not_found">Ressource nicht gefunden</string>
<string name="sk_loading_resource_on_instance_title">Suche auf %s</string>
<string name="sk_bookmark_as">Lesezeichen in anderem Konto</string>
<string name="sk_bookmarked_as">Lesezeichen gesetzt als %s</string>
<string name="sk_already_bookmarked">Bereits in den Lesezeichen</string>
<string name="sk_favorite_as">Favorit von anderem Konto</string>
<string name="sk_favorited_as">Favorisiert als %s</string>
<string name="sk_already_favorited">Bereits favorisiert</string>
<string name="sk_reblog_as">Von einem anderen Konto boosten</string>
<string name="sk_reblogged_as">Geboostet als %s</string>
<string name="sk_already_reblogged">Bereits geboostet</string>
<string name="sk_reply_as">Antworten mit anderem Konto</string>
<string name="sk_settings_uniform_icon_for_notifications">Einheitliches Icon für alle Benachrichtigungen</string>
<string name="sk_forward_report_to">Weiterleiten zu %s</string>
<string name="sk_unsent_posts">Nicht gesendete Beiträge</string>
<string name="sk_draft">Entwurf</string>
<string name="sk_schedule">Planen</string>
<string name="sk_confirm_delete_draft_title">Entwurf löschen</string>
<string name="sk_confirm_delete_draft">Möchtest du diesen entworfenen Beitrag wirklich löschen\?</string>
<string name="sk_confirm_delete_scheduled_post_title">Geplanten Beitrag löschen</string>
<string name="sk_confirm_delete_scheduled_post">Möchtest du diesen geplanten Beitrag wirklich löschen\?</string>
<string name="sk_draft_or_schedule">Entwurf oder Planen</string>
<string name="sk_compose_draft">Beitrag wird als Entwurf gespeichert.</string>
<string name="sk_compose_scheduled">Geplant für</string>
<string name="sk_draft_saved">Entwurf gespeichert</string>
<string name="sk_post_scheduled">Beitrag geplant</string>
<string name="sk_scheduled_too_soon_title">Geplante Zeit ist zu früh</string>
<string name="sk_scheduled_too_soon">Der Beitrag muss mindestens 10 Minuten in der Zukunft eingeplant werden.</string>
<string name="sk_confirm_save_changes">Änderungen speichern\?</string>
<string name="sk_schedule_post">Beitrag planen</string>
<string name="sk_confirm_save_draft">Entwurf speichern\?</string>
<string name="sk_mark_as_draft">Als Entwurf markieren</string>
</resources>

View File

@@ -77,12 +77,47 @@
<string name="sk_enable_delete_notifications">Activar la eliminación de notificaciones</string>
<string name="sk_settings_publish_button_text">Publicar el texto del botón</string>
<string name="sk_settings_publish_button_text_title">Personalizar el texto del botón Publicar</string>
<string name="sk_settings_hide_translate_in_timeline">Ocultar el botón de la traducción en la línea de tiempo</string>
<string name="sk_settings_translate_only_opened">Ocultar el botón de la traducción en la línea de tiempo</string>
<string name="sk_clear_all_notifications_confirm">¿Estás seguro de que quieres borrar todas las notificaciones\?</string>
<string name="sk_settings_translation_availability_note_available">¡%s admite traducción!</string>
<string name="sk_settings_translation_availability_note_unavailable">%s no parece soportar la traducción.</string>
<string name="sk_loading_fediverse_resource_title">Buscándolo en Fediverse…</string>
<string name="sk_loading_fediverse_resource_title">Buscándolo en el Fediverso</string>
<string name="sk_quote_post">Publicar sobre esto</string>
<string name="sk_undo_reblog">Deshacer reblogueo</string>
<string name="sk_reblog_with_visibility">Rebloguea con visibilidad</string>
<string name="sk_hashtags_you_follow">Hashtags que sigues</string>
<string name="sk_copy_link_to_post">Copiar enlace a la publicación</string>
<string name="sk_open_with_account">Abrir con otra cuenta</string>
<string name="sk_resource_not_found">El recurso no se pudo encontrar</string>
<string name="sk_loading_resource_on_instance_title">Buscándolo en %s</string>
<string name="sk_bookmarked_as">Añadido a favoritos como %s</string>
<string name="sk_already_bookmarked">Ya marcado</string>
<string name="sk_favorited_as">Favorito como %s</string>
<string name="sk_already_favorited">Ya es un favorito</string>
<string name="sk_reblog_as">Compartir desde otra cuenta</string>
<string name="sk_reblogged_as">Compartido como %s</string>
<string name="sk_already_reblogged">Ya se volvió a publicar</string>
<string name="sk_reply_as">Responder con otra cuenta</string>
<string name="sk_bookmark_as">Marcador de otra cuenta</string>
<string name="sk_favorite_as">Favoritos de otra cuenta</string>
<string name="sk_settings_uniform_icon_for_notifications">Mismo icono para todas las notificaciones</string>
<string name="sk_forward_report_to">Reenviar a %s</string>
<string name="sk_unsent_posts">Mensajes no enviados</string>
<string name="sk_draft">Borrador</string>
<string name="sk_schedule">Programar</string>
<string name="sk_confirm_delete_draft_title">Borrar el borrador</string>
<string name="sk_confirm_delete_draft">¿Seguro que quieres borrar este borrador\?</string>
<string name="sk_confirm_delete_scheduled_post_title">Borrar la entrada programada</string>
<string name="sk_confirm_delete_scheduled_post">¿Está seguro de que desea eliminar esta publicación programada\?</string>
<string name="sk_draft_or_schedule">A borrador o programar el envio</string>
<string name="sk_compose_draft">El mensaje se guardará como borrador.</string>
<string name="sk_compose_scheduled">Programado para</string>
<string name="sk_draft_saved">Borrador guardado</string>
<string name="sk_post_scheduled">Publicación programada</string>
<string name="sk_scheduled_too_soon_title">La hora programada es demasiado pronto</string>
<string name="sk_scheduled_too_soon">El mensaje debe programarse con al menos 10 minutos de antelación.</string>
<string name="sk_confirm_save_draft">¿Guardar el borrador\?</string>
<string name="sk_confirm_save_changes">¿Guardar los cambios\?</string>
<string name="sk_mark_as_draft">Marcar como borrador</string>
<string name="sk_schedule_post">Programar la publicación</string>
</resources>

View File

@@ -1,3 +1,104 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="sk_app_name">Megalodon</string>
<string name="sk_delete_and_redraft">Ezabatu eta editatu</string>
<string name="sk_confirm_delete_and_redraft_title">Argitalpena ezabatu eta editatu</string>
<string name="sk_pin_post">Profilean finkatu</string>
<string name="sk_confirm_pin_post_title">Argitalpena profilean finkatu</string>
<string name="sk_confirm_pin_post">Ziur al zaude argitalpen hau zure profilean finkatu nahi duzula\?</string>
<string name="sk_pinning">Argitalpena finkatzen…</string>
<string name="sk_unpin_post">Finkapena kendu profiletik</string>
<string name="sk_confirm_unpin_post">Ziur al zaude argitalpena profiletik kendu nahi duzula\?</string>
<string name="sk_unpinning">Argitalpen finkatua kentzen…</string>
<string name="sk_image_description">Irudiaren deskribapena</string>
<string name="sk_visibility_unlisted">Zerrendatu gabea</string>
<string name="sk_settings_show_replies">Erakutsi erantzunak</string>
<string name="sk_settings_show_boosts">Erakutsi bultzadak</string>
<string name="sk_settings_show_interaction_counts">Erakutsi interakzio zenbaketak</string>
<string name="sk_settings_app_version">Megalodon v%1$s (%2$d)</string>
<string name="sk_mark_media_as_sensitive">Markatu multimedia sentikor gisa</string>
<string name="sk_user_post_notifications_on">Gaitu %s-en argitalpen jakinarazpenak</string>
<string name="sk_federated_timeline">Federatua</string>
<string name="sk_federated_timeline_info_banner">Hauek dira zure federazioko pertsonen argitalpen berrienak.</string>
<string name="sk_update_available">Megalodon %s deskargatzeko prest dago.</string>
<string name="sk_check_for_update">Eguneraketak egiaztatu</string>
<string name="sk_no_update_available">Ez dago eguneraketarik eskuragarri</string>
<string name="sk_list_timelines">Zerrendak</string>
<string name="sk_follow_requests">Jarraitzeko eskaerak</string>
<string name="sk_poll_allow_multiple">Onartu aukera anitzak</string>
<string name="sk_clear_recent_languages">Ezabatu berriki erabili diren hizkuntzak</string>
<string name="sk_welcome_title">Ongi etorri!</string>
<string name="sk_example_domain">adibidea.eus</string>
<string name="sk_lists_with_user">%s-ekin zerrendatu</string>
<string name="sk_settings_always_reveal_content_warnings">Erakutsi beti edukiaren abisua</string>
<string name="sk_disable_marquee">Desgaitu izenburu-barretako testuaren desplazamendua</string>
<string name="sk_settings_contribute">Lagundu Megalodon</string>
<string name="sk_translated_using">%s-ekin itzulia</string>
<string name="sk_post_language">Hizkuntza: %s</string>
<string name="sk_language_name">%s (%s)</string>
<string name="sk_bookmark_as">Beste kontu bateko laster-marka</string>
<string name="sk_bookmarked_as">%s bezala laster-markara eramana</string>
<string name="sk_already_bookmarked">Dagoeneko laster-marka da</string>
<string name="sk_favorite_as">Beste kontu bateko gogokoa</string>
<string name="sk_favorited_as">%s bezela dago gogokoetan</string>
<string name="sk_reblog_as">Beste kontu batetik bultzatua</string>
<string name="sk_reblogged_as">%s bezela bultzatua</string>
<string name="sk_already_reblogged">Dagoeneko bultzatua izan da</string>
<string name="sk_reply_as">Erantzun beste kontu batekin</string>
<string name="sk_translate_post">Itzuli</string>
<string name="sk_tabs_disable_swipe">Desgaitu fitxen arteko joan-etorria</string>
<string name="sk_settings_profile">Konfiguratu profila</string>
<string name="sk_settings_posting">Argitalpenen ezarpenak</string>
<string name="sk_settings_filters">Filtroak konfiguratu</string>
<string name="sk_settings_auth">Segurtasun ezarpenak</string>
<string name="sk_settings_rules">Arauak</string>
<string name="sk_settings_about">Aplikazioari buruz</string>
<string name="sk_settings_donate">Lagundu</string>
<string name="sk_settings_color_palette">Kolore paleta</string>
<string name="sk_color_palette_material3">Sistema</string>
<string name="sk_color_palette_pink">Arrosa</string>
<string name="sk_color_palette_purple">Morea</string>
<string name="sk_color_palette_green">Berdea</string>
<string name="sk_color_palette_blue">Urdina</string>
<string name="sk_color_palette_brown">Marroia</string>
<string name="sk_color_palette_red">Gorria</string>
<string name="sk_color_palette_yellow">Horia</string>
<string name="sk_delete_notification">Ezabatu jakinarazpena</string>
<string name="sk_delete_notification_confirm_action">Ezabatu jakinarazpena</string>
<string name="sk_delete_notification_confirm">Ziur al zaude jakinarazpen hau ezabatu nahi duzula\?</string>
<string name="sk_enable_delete_notifications">Gaitu jakinarazpenen ezabatzea</string>
<string name="sk_settings_publish_button_text">Argitaratze botoiaren testua</string>
<string name="sk_settings_publish_button_text_title">Pertsonalizatu argitaratze botoiaren testua</string>
<string name="sk_settings_translation_availability_note_available">%s-k itzulpena onartzen du!</string>
<string name="sk_settings_translation_availability_note_unavailable">%s ez da itzulpena onartzen duten hizkuntzen artean ageri.</string>
<string name="sk_clear_all_notifications">Ezabatu jakinarazpen guztiak</string>
<string name="sk_clear_all_notifications_confirm_action">Ezabatu dena</string>
<string name="sk_clear_all_notifications_confirm">Ziur al zaude jakinarazpen guztiak ezabatu nahi dituzula\?</string>
<string name="sk_loading_fediverse_resource_title">Fedibertsoan bilatzen</string>
<string name="sk_undo_reblog">Bultzada desegin</string>
<string name="sk_quote_post">Honen inguruan argitaratu</string>
<string name="sk_reblog_with_visibility">Bultzada ikusgarria</string>
<string name="sk_hashtags_you_follow">Jarraitzen dituzun Hashtag-ak</string>
<string name="sk_copy_link_to_post">Kopiatu link-a argitaratzeko</string>
<string name="sk_loading_resource_on_instance_title">%s-n bilatzen</string>
<string name="sk_open_with_account">Ireki beste kontu batekin</string>
<string name="sk_resource_not_found">Ezin da errekurtsoa bilatu</string>
<string name="sk_pinned_posts">Finkatua</string>
<string name="sk_confirm_delete_and_redraft">Ziur al zaude argitalpena ezabatu eta editatu nahi duzula\?</string>
<string name="sk_confirm_unpin_post_title">Argitalpen finkatua kendu profiletik</string>
<string name="sk_settings_load_new_posts">Kargatu automatikoki argitalpen berriak</string>
<string name="sk_user_post_notifications_off">Desgaitu %s-en argitalpen jakinarazpenak</string>
<string name="sk_update_ready">Megalodon %s deskargatuta eta instalatzeko prest dago.</string>
<string name="sk_accept_follow_request">Onartu jarraitzeko eskaerak</string>
<string name="sk_reject_follow_request">Alboratu jarraitzeko eskaerak</string>
<string name="sk_settings_show_federated_timeline">Erakutsi federatutako denbora-lerroa</string>
<string name="sk_notification_type_status">Argitalpenak</string>
<string name="sk_notify_posts">Argitalpenen jakinarazpenak</string>
<string name="sk_translate_show_original">Erakutsi jatorrizkoa</string>
<string name="sk_available_languages">Eskuragarri dauden hizkuntzak</string>
<string name="sk_confirm_clear_recent_languages">Ziur al zaude berriki erabilitako hizkuntzak ezabatu nahi dituzula\?</string>
<string name="sk_welcome_text">Marrazoak ongi etorria ematen dizu! Hasteko, sartu jjarraian jatorrizko instantziaren domeinu-izena.</string>
<string name="sk_settings_translate_only_opened">Irekitako argitalpenak soilik itzuli</string>
<string name="sk_already_favorited">Dagoeneko gogokoen artean dago</string>
<string name="sk_settings_uniform_icon_for_notifications">Ikono berdina jakinarazpen guztietarako</string>
</resources>

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