Compare commits
272 Commits
1.1.4+fork
...
v1.1.4+for
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c9f4df3d4e | ||
|
|
9078667d51 | ||
|
|
7569e1aef6 | ||
|
|
723983dadf | ||
|
|
f87e020abd | ||
|
|
fb5729d5cc | ||
|
|
2ff6c53d6d | ||
|
|
cfc6895711 | ||
|
|
1c27fc68ee | ||
|
|
df0d578573 | ||
|
|
2fa3c69af1 | ||
|
|
095bf92fed | ||
|
|
debe017f12 | ||
|
|
f956e12167 | ||
|
|
2c50c38d82 | ||
|
|
b4980101ad | ||
|
|
8395fca60f | ||
|
|
b22e7d277f | ||
|
|
c0e67593ee | ||
|
|
5dc4235724 | ||
|
|
f77caeefae | ||
|
|
c1ef23bbe8 | ||
|
|
e7e80bcf7d | ||
|
|
c27f5aaf30 | ||
|
|
d52728f22e | ||
|
|
3c7c962320 | ||
|
|
abf570d177 | ||
|
|
46422cd62d | ||
|
|
f1ffa2629e | ||
|
|
2074f3c33b | ||
|
|
7c51803674 | ||
|
|
6d80c62f30 | ||
|
|
64907a7e1c | ||
|
|
17922ca1d5 | ||
|
|
01ac219854 | ||
|
|
9bbf8c4618 | ||
|
|
978beaec77 | ||
|
|
0950e2eb7f | ||
|
|
116328adb9 | ||
|
|
32a2c66c34 | ||
|
|
231ea46f9f | ||
|
|
661f545e35 | ||
|
|
600be455a3 | ||
|
|
a4df06726f | ||
|
|
e45e2c31d1 | ||
|
|
d1e0cd3c20 | ||
|
|
db16dde073 | ||
|
|
b3fe44bc08 | ||
|
|
e5fab4a555 | ||
|
|
abe28179ec | ||
|
|
60d4e4d396 | ||
|
|
435e73d718 | ||
|
|
17dc0850d5 | ||
|
|
9667a32e44 | ||
|
|
4e6ba84bb3 | ||
|
|
8714b24388 | ||
|
|
495db142d7 | ||
|
|
ac5d11159f | ||
|
|
d1479f142b | ||
|
|
ee6ec631e8 | ||
|
|
dee21222a7 | ||
|
|
cd8123ca34 | ||
|
|
ceb08ea78d | ||
|
|
b79ba71228 | ||
|
|
2903874dbc | ||
|
|
cac5b554e2 | ||
|
|
c4adbc8e45 | ||
|
|
bb01077c3b | ||
|
|
b370fcda6d | ||
|
|
d364ebbb2f | ||
|
|
5b28468efd | ||
|
|
6fd58c9682 | ||
|
|
b580743619 | ||
|
|
4a9cb9f2dc | ||
|
|
202a5f9581 | ||
|
|
145f55817f | ||
|
|
79025c2f36 | ||
|
|
2515a8d381 | ||
|
|
ede7ece25a | ||
|
|
2db39f8c66 | ||
|
|
5f0382456f | ||
|
|
63b1b58c4e | ||
|
|
06f2f67f0c | ||
|
|
05c33be3f4 | ||
|
|
e274b7e6d5 | ||
|
|
0806d0c5ea | ||
|
|
5c67dd0188 | ||
|
|
b4358f51cb | ||
|
|
622c6d503d | ||
|
|
b190480d77 | ||
|
|
9a085beea8 | ||
|
|
1a42a77e24 | ||
|
|
e35794ef7d | ||
|
|
1f9611fc3e | ||
|
|
563afd487c | ||
|
|
e10faeefc4 | ||
|
|
65dbbb3d61 | ||
|
|
fa69868ca1 | ||
|
|
9c18de7b90 | ||
|
|
61bd19f6ff | ||
|
|
ba0689aef7 | ||
|
|
ad54e6bb4b | ||
|
|
f15fcb43da | ||
|
|
66208f5694 | ||
|
|
68863f28eb | ||
|
|
7feaf093e2 | ||
|
|
4ab9e25fec | ||
|
|
e14dfda2fd | ||
|
|
c9aae828e2 | ||
|
|
f346c0af26 | ||
|
|
f2557b7815 | ||
|
|
a2726f5b61 | ||
|
|
834ec1575d | ||
|
|
a30f5bdee8 | ||
|
|
4cef005286 | ||
|
|
58a05681fe | ||
|
|
2589faf499 | ||
|
|
a5bdf34289 | ||
|
|
09fdd7f492 | ||
|
|
519d8b887d | ||
|
|
a2f2263bf7 | ||
|
|
5b73b10b34 | ||
|
|
b7a4364a28 | ||
|
|
3f075aff7b | ||
|
|
f4c33a5970 | ||
|
|
809af0ec18 | ||
|
|
4ee640e072 | ||
|
|
1cbf310555 | ||
|
|
f1fdc8aa43 | ||
|
|
d696daece3 | ||
|
|
967bb09282 | ||
|
|
136d910b3b | ||
|
|
51eb48a455 | ||
|
|
6ee8afcf96 | ||
|
|
a59f2d4609 | ||
|
|
b75d871837 | ||
|
|
c72f93b990 | ||
|
|
586d337ead | ||
|
|
d84e10a22e | ||
|
|
351ec89207 | ||
|
|
7db7bf0220 | ||
|
|
a9764c4f46 | ||
|
|
a430b6a280 | ||
|
|
6a01124d13 | ||
|
|
2843e445e2 | ||
|
|
5c947d14b2 | ||
|
|
590adba3e3 | ||
|
|
efee249173 | ||
|
|
6d2ed27364 | ||
|
|
55716d742f | ||
|
|
e4555da735 | ||
|
|
8b4b99bec7 | ||
|
|
5de4b19969 | ||
|
|
a9460f401e | ||
|
|
012cca550e | ||
|
|
0c743db412 | ||
|
|
b819ee7d6d | ||
|
|
e7e3a249b5 | ||
|
|
980c580b55 | ||
|
|
e23c530e74 | ||
|
|
a64caccca2 | ||
|
|
829bcafcf2 | ||
|
|
e2a935c647 | ||
|
|
2e7afdb49e | ||
|
|
cdc965e026 | ||
|
|
dd4faa005e | ||
|
|
726ec7159c | ||
|
|
e74256ef6f | ||
|
|
a18718ca81 | ||
|
|
5a9bc0e269 | ||
|
|
2d39c62ff0 | ||
|
|
0da4f79413 | ||
|
|
2bdef776a2 | ||
|
|
1819d6f042 | ||
|
|
2f6a707847 | ||
|
|
4aaf017824 | ||
|
|
fb05ed48d0 | ||
|
|
49203ae539 | ||
|
|
d17660d516 | ||
|
|
513ce34671 | ||
|
|
44ce48009b | ||
|
|
a57ad67308 | ||
|
|
e63d04cea9 | ||
|
|
cf48cb6f75 | ||
|
|
542e53cf6a | ||
|
|
bab1d40038 | ||
|
|
2f4a8247e8 | ||
|
|
f0b9006c55 | ||
|
|
4bc14ef797 | ||
|
|
47d2cee3f1 | ||
|
|
088f53f5a9 | ||
|
|
fee660bf6c | ||
|
|
e97ecb89a9 | ||
|
|
1ab6a4532b | ||
|
|
b43ddd0d8b | ||
|
|
d0c4c2d594 | ||
|
|
e0ae079ea0 | ||
|
|
dfddbd15a9 | ||
|
|
29242c45a1 | ||
|
|
7ff0e59f4d | ||
|
|
2d0fe57a47 | ||
|
|
ba2b87749b | ||
|
|
0806af1261 | ||
|
|
09e92f3a18 | ||
|
|
eb5d0bb795 | ||
|
|
a8afba4067 | ||
|
|
7d66141c37 | ||
|
|
b6f3ea2eec | ||
|
|
2570445133 | ||
|
|
acbd22cf22 | ||
|
|
ef251b040a | ||
|
|
6c5bb69ba9 | ||
|
|
18f605e5c5 | ||
|
|
7599406449 | ||
|
|
670e4c8538 | ||
|
|
30458b115c | ||
|
|
da8933ec58 | ||
|
|
dc8ac51c83 | ||
|
|
c4747fdc72 | ||
|
|
58ba748ade | ||
|
|
c0d51ad58a | ||
|
|
a0da73f76f | ||
|
|
34b8888c8f | ||
|
|
74ad40f67c | ||
|
|
a7a29db8d5 | ||
|
|
86a938d31d | ||
|
|
d8d0830631 | ||
|
|
bba3b7476a | ||
|
|
4880c642fe | ||
|
|
76ad896461 | ||
|
|
b49159f9e0 | ||
|
|
cd8a80a6a1 | ||
|
|
3ce8aa7894 | ||
|
|
5f3645f716 | ||
|
|
5af96597d5 | ||
|
|
c2baf4e05f | ||
|
|
b356794da9 | ||
|
|
afe8f6cf6a | ||
|
|
ed0df82fe9 | ||
|
|
d3bc7a9790 | ||
|
|
9e7923bc50 | ||
|
|
851bf94c90 | ||
|
|
ae80b7d098 | ||
|
|
633c0f870d | ||
|
|
f9fe7819f9 | ||
|
|
f3d13545e7 | ||
|
|
f6b77777b5 | ||
|
|
340990fbd9 | ||
|
|
a7687f8e35 | ||
|
|
52aa4a5289 | ||
|
|
268accea14 | ||
|
|
101cde4d84 | ||
|
|
8863446f6a | ||
|
|
28a0824f6b | ||
|
|
b1f9d0516d | ||
|
|
5b21747d5d | ||
|
|
9fda48cff0 | ||
|
|
0e6f3df212 | ||
|
|
a8c3f1555e | ||
|
|
cd797a637b | ||
|
|
53b2eb59d3 | ||
|
|
09e2224596 | ||
|
|
5999aad21b | ||
|
|
874ce07c3e | ||
|
|
1787d08718 | ||
|
|
9a12be88da | ||
|
|
8f6bb74e61 | ||
|
|
e67bd2972a | ||
|
|
de5929d8d2 | ||
|
|
d7699ef079 | ||
|
|
3ab04ebca8 | ||
|
|
78d2aa96d7 |
32
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
32
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Screenshots and screen recordings**
|
||||
If applicable, add screenshots (and screen recordings, if possible) to help explain your problem.
|
||||
|
||||
**Version**
|
||||
Megalodon version: [e.g. v1.1.4+fork.#]
|
||||
|
||||
**Additional context**
|
||||
- Does this issue also occur with the respective upstream release? (Please test using the respective `upstream-xxxxxx.apk` provided in [Releases](https://github.com/sk22/megalodon/releases)) No / Yes (`mastodon#…`)
|
||||
|
||||
> In this case, please consider filing an [upstream bug report](https://github.com/mastodon/mastodon-android/issues) instead. If this bug is seriously impacting your usage or you think I might want to try to fix it for Megalodon, feel free to still create this issue!
|
||||
|
||||
**Crash log**
|
||||
If you know your way around Android development tools, please consider attaching a crash log, if possible.
|
||||
20
.github/ISSUE_TEMPLATE/feature-ui-request.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/feature-ui-request.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: Feature/UI request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: feature
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
If applicable: a clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
10
.github/ISSUE_TEMPLATE/something-else.md
vendored
Normal file
10
.github/ISSUE_TEMPLATE/something-else.md
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
name: It's something else…
|
||||
about: Issues that can't be categorized as feature requests or bug reports
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
|
||||
69
README.md
69
README.md
@@ -2,11 +2,16 @@
|
||||
|
||||
# Megalodon
|
||||
|
||||
> 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 won’t ever be implemented, such as the federated timeline, unlisted posting, bookmarks and an image description viewer.
|
||||
[](https://translate.codeberg.org/engage/megalodon/)
|
||||
|
||||
[](https://github.com/sk22/megalodon/releases/latest/download/megalodon.apk)
|
||||
|
||||
**Warning! [The last version's integrated updater was broken](https://github.com/sk22/megalodon/issues/106) – I already published a fixed version! If you're not updating through Izzy's F-Droid repository (more sources to come, hopefully!), you'll have to download the current release manually. Sorry about that!**
|
||||
<a href="https://play.google.com/store/apps/details?id=org.joinmastodon.android.sk"><img height="50" alt="Get it on Google Play" src="img/google-play-badge.png"></a>
|
||||
|
||||
<a href="https://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 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 won’t ever be implemented, such as the federated timeline, unlisted posting and an image description viewer.
|
||||
|
||||
[](https://github.com/sk22/megalodon/releases/latest/download/megalodon.apk)
|
||||
|
||||
---
|
||||
|
||||
@@ -41,24 +46,32 @@ This is important to **ensure the content you’re sharing is as accessible as p
|
||||
|
||||
On the Fediverse, it’s quite common for people to pin posts they want others to read before following them. You can pin/unpin posts yourself by clicking the `⋯` button in the top right corner of your posts.
|
||||
|
||||
### **Bookmarks**
|
||||
|
||||
**They allow for quickly saving posts and viewing them through the Bookmarks button on the top right of your profile.**
|
||||
|
||||
To bookmark a post, press the button between the Favorite and Share buttons on the bottom of the post. Bookmarks are saved privately, so the post authors won’t know you saved their post – the list of bookmarked posts is only visible to you.
|
||||
|
||||
## Installation
|
||||
|
||||
**Press the download button above to download the APK. Open the downloaded file on your Android device to install it. Megalodon will automatically notify you about new updates inside the app.**
|
||||
### From app stores
|
||||
|
||||
To install this app on your Android device, download the [latest release from GitHub](https://github.com/sk22/megalodon/releases/latest/download/megalodon.apk) and open it. You might have to accept installing APK files from your browser when trying to install it. You can also take a look at all releases on the [Releases](https://github.com/sk22/megalodon/releases) page.
|
||||
* **[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)
|
||||
|
||||
Note that you'll need to add Izzy's F-Droid repository to your F-Droid app first:
|
||||
|
||||
`https://apt.izzysoft.de/fdroid/repo`
|
||||
|
||||
* **[Google Play Store](https://play.google.com/store/apps/details?id=org.joinmastodon.android.sk)**: [play.google.com/store/apps/details?id=org.joinmastodon.android.sk](https://play.google.com/store/apps/details?id=org.joinmastodon.android.sk)
|
||||
|
||||
* **[F-Droid.org](https://f-droid.org)?** Not yet, sorry!
|
||||
|
||||
If you want, you can help me figure out if something's missing in the [Issue #47: F-Droid.org](https://github.com/sk22/megalodon/issues/47)
|
||||
|
||||
### Directly from GitHub
|
||||
|
||||
Press the download button to download the APK. Open the downloaded file on your Android device to install it. Megalodon will automatically notify you about new updates inside the app.
|
||||
|
||||
[](https://github.com/sk22/megalodon/releases/latest/download/megalodon.apk)
|
||||
|
||||
You might have to accept installing APK files from your browser when trying to install it. You can also take a look at all releases on the [Releases](https://github.com/sk22/megalodon/releases) page.
|
||||
|
||||
Megalodon makes use of [Mastodon for Android](https://github.com/mastodon/mastodon-android)’s automatic update checker. Megalodon will check for new updates available on GitHub and offer to download and install them. You can also manually press “Check for updates” at the bottom of the settings page!
|
||||
|
||||
### Other sources
|
||||
|
||||
* **[Izzy's F-Droid repository](https://apt.izzysoft.de/fdroid/repo)**: https://apt.izzysoft.de/fdroid/index/apk/org.joinmastodon.android.sk
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -78,6 +91,19 @@ This is an **unmodified version** of the official [Mastodon for Android](https:/
|
||||
|
||||
Variant without the integrated updater. This is the variant to be published to F-Droid.org where an integrated updater is not necessary. -->
|
||||
|
||||
---
|
||||
|
||||
## Contribution
|
||||
|
||||
### Translation
|
||||
|
||||
As with the source code, the translation is sourced from the official project, which you can contribute to on the official “**Mastodon for Android**” Crowdin project: https://crowdin.com/project/mastodon-for-android
|
||||
|
||||
There's also a handful of custom strings exclusive to this projects that would need to be translated. You can help translate **Megalodon** on Weblate: https://translate.codeberg.org/projects/megalodon/
|
||||
|
||||
[](https://translate.codeberg.org/engage/megalodon/)
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -105,6 +131,10 @@ Variant without the integrated updater. This is the variant to be published to F
|
||||
* [Show visibility of original post when replying](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/display-reply-visibility)
|
||||
* [Clickable reply/boost line above posts](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:clickable-boost-reply-line)
|
||||
* [Clickable reply line while replying to open original post](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/clickable-reply-line-compose)
|
||||
* [Add push notification setting for post notifications](https://github.com/sk22/megalodon/commit/b190480d7739be47f23543d9e7644660f9b4b4ee)
|
||||
* [Add option to allow voting for multiple options on polls](https://github.com/sk22/megalodon/commit/5b28468efd49387b4f8b83f142f3adf3104ca60c)
|
||||
* [Add translate function](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/translate-button)
|
||||
* [Add language selector](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/language-selector)
|
||||
|
||||
|
||||
### Behavior
|
||||
@@ -116,6 +146,12 @@ Variant without the integrated updater. This is the variant to be published to F
|
||||
* [Option to hide interaction numbers](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:settings/hide-interaction-numbers)
|
||||
* [Option to always reveal content warnings](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/cw-above-text)
|
||||
* [Option to disable scrolling title bars](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:settings/disable-marquee)
|
||||
* [No ellipsis for long poll answers](https://github.com/mastodon/mastodon-android/commit/c9aae828e2518adccdc092e41f8d1f0489636271)
|
||||
* [Show poll vote button for multiple and single answer polls](https://github.com/mastodon/mastodon-android/commit/e14dfda2fdf32f0fa3043504ac5831683a87559a)
|
||||
* [Show own vote after voting](https://github.com/mastodon/mastodon-android/commit/4ab9e25fec4fd9c10b7a8ddd1be522b3cc12cf28) ([Closes issue](https://github.com/mastodon/mastodon-android/commit/4ab9e25fec4fd9c10b7a8ddd1be522b3cc12cf28))
|
||||
* [Make inline emoji search case-insensitive and don't only search from start of emoji names](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:better-inline-emoji-search) ([Pull request](https://github.com/mastodon/mastodon-android/pull/445))
|
||||
* [Include subject line when sharing e.g. a website to Megalodon](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:external-share-include-subject)
|
||||
* [Improve semantics for voting on polls (radio buttons and checkboxes)](https://github.com/sk22/megalodon/commit/6fd58c96827cb1d2da329cebdc170a1425dd18d7)
|
||||
|
||||
|
||||
### Visual
|
||||
@@ -123,6 +159,9 @@ Variant without the integrated updater. This is the variant to be published to F
|
||||
* [Custom extended footer redesign](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:compact-extended-footer)
|
||||
* [Improvements to the true black mode](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:true-black-improvements)
|
||||
* [Profile header tweaks](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:ui/profile-header-tweaks)
|
||||
* [Custom color themes](https://github.com/sk22/megalodon/pull/124) by [@LucasGGamerM](https://github.com/LucasGGamerM)
|
||||
* [Custom "megalodon" text logo](https://github.com/sk22/megalodon/commit/563afd487ca5c608cfbb00fa3909d3c27384acc0) by [@LucasGGamerM](https://github.com/LucasGGamerM)
|
||||
* [Custom login screen](https://github.com/sk22/megalodon/commit/9bbf8c4618dbe13accaeb3b5482bf3fe88cac4c0)
|
||||
|
||||
|
||||
## Building
|
||||
|
||||
BIN
img/f-droid-badge.png
Normal file
BIN
img/f-droid-badge.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
@@ -9,8 +9,8 @@ android {
|
||||
applicationId "org.joinmastodon.android.sk"
|
||||
minSdk 23
|
||||
targetSdk 33
|
||||
versionCode 51
|
||||
versionName "1.1.4+fork.51"
|
||||
versionCode 59
|
||||
versionName "1.1.4+fork.59"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
resConfigs "en", "ar-rSA", "bs-rBA", "ca-rES", "cs-rCZ", "de-rDE", "el-rGR", "es-rES",
|
||||
"eu-rES", "fi-rFI", "fr-rFR", "gl-rES", "hr-rHR", "hy-rAM", "it-rIT", "iw-rIL",
|
||||
@@ -32,9 +32,11 @@ android {
|
||||
githubRelease{
|
||||
initWith release
|
||||
}
|
||||
noFederatedRelease{
|
||||
playRelease{
|
||||
initWith release
|
||||
versionNameSuffix '-nofederated'
|
||||
minifyEnabled true
|
||||
shrinkResources true
|
||||
versionNameSuffix '-play'
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<application
|
||||
android:name=".MastodonApp"
|
||||
android:allowBackup="true"
|
||||
android:label="@string/app_name"
|
||||
android:label="@string/sk_app_name"
|
||||
android:supportsRtl="true"
|
||||
android:localeConfig="@xml/locales_config"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
|
||||
@@ -51,7 +51,10 @@ public class ExternalShareActivity extends FragmentStackActivity{
|
||||
getWindow().setBackgroundDrawable(null);
|
||||
|
||||
Intent intent=getIntent();
|
||||
String text=intent.getStringExtra(Intent.EXTRA_TEXT);
|
||||
StringBuilder builder=new StringBuilder();
|
||||
if (intent.hasExtra(Intent.EXTRA_SUBJECT)) builder.append(intent.getStringExtra(Intent.EXTRA_SUBJECT)).append("\n");
|
||||
if (intent.hasExtra(Intent.EXTRA_TEXT)) builder.append(intent.getStringExtra(Intent.EXTRA_TEXT)).append("\n");
|
||||
String text=builder.toString();
|
||||
List<Uri> mediaUris;
|
||||
if(Intent.ACTION_SEND.equals(intent.getAction())){
|
||||
Uri singleUri=intent.getParcelableExtra(Intent.EXTRA_STREAM);
|
||||
|
||||
@@ -1,8 +1,18 @@
|
||||
package org.joinmastodon.android;
|
||||
|
||||
import static org.joinmastodon.android.api.MastodonAPIController.gson;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class GlobalUserPreferences{
|
||||
public static boolean playGifs;
|
||||
public static boolean useCustomTabs;
|
||||
@@ -10,15 +20,26 @@ public class GlobalUserPreferences{
|
||||
public static boolean showReplies;
|
||||
public static boolean showBoosts;
|
||||
public static boolean loadNewPosts;
|
||||
public static boolean showFederatedTimeline;
|
||||
public static boolean showInteractionCounts;
|
||||
public static boolean alwaysExpandContentWarnings;
|
||||
public static boolean disableMarquee;
|
||||
public static boolean voteButtonForSingleChoice;
|
||||
public static ThemePreference theme;
|
||||
public static ColorPreference color;
|
||||
|
||||
private static SharedPreferences getPrefs(){
|
||||
private final static Type recentLanguagesType = new TypeToken<Map<String, List<String>>>() {}.getType();
|
||||
public static Map<String, List<String>> recentLanguages;
|
||||
|
||||
private static SharedPreferences getPrefs(){
|
||||
return MastodonApp.context.getSharedPreferences("global", Context.MODE_PRIVATE);
|
||||
}
|
||||
|
||||
private static <T> T fromJson(String json, Type type, T orElse) {
|
||||
try { return gson.fromJson(json, type); }
|
||||
catch (JsonSyntaxException ignored) { return orElse; }
|
||||
}
|
||||
|
||||
public static void load(){
|
||||
SharedPreferences prefs=getPrefs();
|
||||
playGifs=prefs.getBoolean("playGifs", true);
|
||||
@@ -27,10 +48,14 @@ public class GlobalUserPreferences{
|
||||
showReplies=prefs.getBoolean("showReplies", true);
|
||||
showBoosts=prefs.getBoolean("showBoosts", true);
|
||||
loadNewPosts=prefs.getBoolean("loadNewPosts", true);
|
||||
showFederatedTimeline=prefs.getBoolean("showFederatedTimeline", !BuildConfig.BUILD_TYPE.equals("playRelease"));
|
||||
showInteractionCounts=prefs.getBoolean("showInteractionCounts", false);
|
||||
alwaysExpandContentWarnings=prefs.getBoolean("alwaysExpandContentWarnings", false);
|
||||
disableMarquee=prefs.getBoolean("disableMarquee", false);
|
||||
voteButtonForSingleChoice=prefs.getBoolean("voteButtonForSingleChoice", true);
|
||||
theme=ThemePreference.values()[prefs.getInt("theme", 0)];
|
||||
color=ColorPreference.values()[prefs.getInt("color", 0)];
|
||||
recentLanguages=fromJson(prefs.getString("recentLanguages", "{}"), recentLanguagesType, new HashMap<>());
|
||||
}
|
||||
|
||||
public static void save(){
|
||||
@@ -40,17 +65,30 @@ public class GlobalUserPreferences{
|
||||
.putBoolean("showReplies", showReplies)
|
||||
.putBoolean("showBoosts", showBoosts)
|
||||
.putBoolean("loadNewPosts", loadNewPosts)
|
||||
.putBoolean("showFederatedTimeline", showFederatedTimeline)
|
||||
.putBoolean("trueBlackTheme", trueBlackTheme)
|
||||
.putBoolean("showInteractionCounts", showInteractionCounts)
|
||||
.putBoolean("alwaysExpandContentWarnings", alwaysExpandContentWarnings)
|
||||
.putBoolean("disableMarquee", disableMarquee)
|
||||
.putInt("theme", theme.ordinal())
|
||||
.putInt("color", color.ordinal())
|
||||
.putString("recentLanguages", gson.toJson(recentLanguages))
|
||||
.apply();
|
||||
}
|
||||
|
||||
public enum ColorPreference{
|
||||
PINK,
|
||||
PURPLE,
|
||||
GREEN,
|
||||
BLUE,
|
||||
BROWN,
|
||||
YELLOW
|
||||
}
|
||||
|
||||
public enum ThemePreference{
|
||||
AUTO,
|
||||
LIGHT,
|
||||
DARK
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,9 +14,9 @@ import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||
import org.joinmastodon.android.fragments.ComposeFragment;
|
||||
import org.joinmastodon.android.fragments.HomeFragment;
|
||||
import org.joinmastodon.android.fragments.ProfileFragment;
|
||||
import org.joinmastodon.android.fragments.SplashFragment;
|
||||
import org.joinmastodon.android.fragments.ThreadFragment;
|
||||
import org.joinmastodon.android.fragments.onboarding.AccountActivationFragment;
|
||||
import org.joinmastodon.android.fragments.onboarding.MegalodonLoginFragment;
|
||||
import org.joinmastodon.android.model.Notification;
|
||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||
import org.joinmastodon.android.updater.GithubSelfUpdater;
|
||||
@@ -33,7 +33,7 @@ public class MainActivity extends FragmentStackActivity{
|
||||
|
||||
if(savedInstanceState==null){
|
||||
if(AccountSessionManager.getInstance().getLoggedInAccounts().isEmpty()){
|
||||
showFragmentClearingBackStack(new SplashFragment());
|
||||
showFragmentClearingBackStack(new MegalodonLoginFragment());
|
||||
}else{
|
||||
AccountSessionManager.getInstance().maybeUpdateLocalInfo();
|
||||
AccountSession session;
|
||||
|
||||
@@ -370,7 +370,7 @@ public class PushSubscriptionManager{
|
||||
for(AccountSession session:AccountSessionManager.getInstance().getLoggedInAccounts()){
|
||||
if(session.pushSubscription==null || forceReRegister)
|
||||
session.getPushSubscriptionManager().registerAccountForPush(session.pushSubscription);
|
||||
else if(session.needUpdatePushSettings)
|
||||
else
|
||||
session.getPushSubscriptionManager().updatePushSettings(session.pushSubscription);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,4 +7,15 @@ public class GetInstance extends MastodonAPIRequest<Instance>{
|
||||
public GetInstance(){
|
||||
super(HttpMethod.GET, "/instance", Instance.class);
|
||||
}
|
||||
|
||||
public static class V2 extends MastodonAPIRequest<Instance.V2>{
|
||||
public V2(){
|
||||
super(HttpMethod.GET, "/instance", Instance.V2.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getPathPrefix() {
|
||||
return "/api/v2";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package org.joinmastodon.android.api.requests.statuses;
|
||||
|
||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||
import org.joinmastodon.android.model.TranslatedStatus;
|
||||
|
||||
public class TranslateStatus extends MastodonAPIRequest<TranslatedStatus> {
|
||||
public TranslateStatus(String id) {
|
||||
super(HttpMethod.POST, "/statuses/"+id+"/translate", TranslatedStatus.class);
|
||||
setRequestBody(new Object());
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import org.joinmastodon.android.api.StatusInteractionController;
|
||||
import org.joinmastodon.android.model.Account;
|
||||
import org.joinmastodon.android.model.Application;
|
||||
import org.joinmastodon.android.model.Filter;
|
||||
import org.joinmastodon.android.model.Preferences;
|
||||
import org.joinmastodon.android.model.PushSubscription;
|
||||
import org.joinmastodon.android.model.Token;
|
||||
|
||||
@@ -28,6 +29,7 @@ public class AccountSession{
|
||||
public long filtersLastUpdated;
|
||||
public List<Filter> wordFilters=new ArrayList<>();
|
||||
public String pushAccountID;
|
||||
public Preferences preferences;
|
||||
private transient MastodonAPIController apiController;
|
||||
private transient StatusInteractionController statusInteractionController;
|
||||
private transient CacheController cacheController;
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.joinmastodon.android.MastodonApp;
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.api.MastodonAPIController;
|
||||
import org.joinmastodon.android.api.PushSubscriptionManager;
|
||||
import org.joinmastodon.android.api.requests.accounts.GetPreferences;
|
||||
import org.joinmastodon.android.api.requests.accounts.GetWordFilters;
|
||||
import org.joinmastodon.android.api.requests.instance.GetCustomEmojis;
|
||||
import org.joinmastodon.android.api.requests.accounts.GetOwnAccount;
|
||||
@@ -34,6 +35,7 @@ import org.joinmastodon.android.model.Emoji;
|
||||
import org.joinmastodon.android.model.EmojiCategory;
|
||||
import org.joinmastodon.android.model.Filter;
|
||||
import org.joinmastodon.android.model.Instance;
|
||||
import org.joinmastodon.android.model.Preferences;
|
||||
import org.joinmastodon.android.model.Token;
|
||||
|
||||
import java.io.File;
|
||||
@@ -248,12 +250,13 @@ public class AccountSessionManager{
|
||||
HashSet<String> domains=new HashSet<>();
|
||||
for(AccountSession session:sessions.values()){
|
||||
domains.add(session.domain.toLowerCase());
|
||||
if(now-session.infoLastUpdated>24L*3600_000L){
|
||||
updateSessionLocalInfo(session);
|
||||
}
|
||||
if(now-session.filtersLastUpdated>3600_000L){
|
||||
updateSessionWordFilters(session);
|
||||
}
|
||||
// if(now-session.infoLastUpdated>24L*3600_000L){
|
||||
updateSessionPreferences(session);
|
||||
updateSessionLocalInfo(session);
|
||||
// }
|
||||
// if(now-session.filtersLastUpdated>3600_000L){
|
||||
updateSessionWordFilters(session);
|
||||
// }
|
||||
}
|
||||
if(loadedInstances){
|
||||
maybeUpdateCustomEmojis(domains);
|
||||
@@ -263,10 +266,10 @@ public class AccountSessionManager{
|
||||
private void maybeUpdateCustomEmojis(Set<String> domains){
|
||||
long now=System.currentTimeMillis();
|
||||
for(String domain:domains){
|
||||
Long lastUpdated=instancesLastUpdated.get(domain);
|
||||
if(lastUpdated==null || now-lastUpdated>24L*3600_000L){
|
||||
updateInstanceInfo(domain);
|
||||
}
|
||||
// Long lastUpdated=instancesLastUpdated.get(domain);
|
||||
// if(lastUpdated==null || now-lastUpdated>24L*3600_000L){
|
||||
updateInstanceInfo(domain);
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,6 +291,18 @@ public class AccountSessionManager{
|
||||
.exec(session.getID());
|
||||
}
|
||||
|
||||
private void updateSessionPreferences(AccountSession session){
|
||||
new GetPreferences().setCallback(new Callback<>() {
|
||||
@Override
|
||||
public void onSuccess(Preferences preferences) {
|
||||
session.preferences=preferences;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(ErrorResponse error) {}
|
||||
}).exec(session.getID());
|
||||
}
|
||||
|
||||
private void updateSessionWordFilters(AccountSession session){
|
||||
new GetWordFilters()
|
||||
.setCallback(new Callback<>(){
|
||||
@@ -313,6 +328,11 @@ public class AccountSessionManager{
|
||||
public void onSuccess(Instance instance){
|
||||
instances.put(domain, instance);
|
||||
updateInstanceEmojis(instance, domain);
|
||||
try {
|
||||
if (Integer.parseInt(instance.version.split("\\.")[0]) >= 4) {
|
||||
updateInstanceInfoV2(domain);
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -323,6 +343,19 @@ public class AccountSessionManager{
|
||||
.execNoAuth(domain);
|
||||
}
|
||||
|
||||
public void updateInstanceInfoV2(String domain) {
|
||||
new GetInstance.V2().setCallback(new Callback<>() {
|
||||
@Override
|
||||
public void onSuccess(Instance.V2 v2) {
|
||||
Instance instanceInfo = instances.get(domain);
|
||||
if (instanceInfo != null) instanceInfo.v2 = v2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(ErrorResponse errorResponse) {}
|
||||
}).execNoAuth(domain);
|
||||
}
|
||||
|
||||
private void updateInstanceEmojis(Instance instance, String domain){
|
||||
new GetCustomEmojis()
|
||||
.setCallback(new Callback<>(){
|
||||
@@ -398,6 +431,10 @@ public class AccountSessionManager{
|
||||
return instances.get(domain);
|
||||
}
|
||||
|
||||
public Instance getInstanceInfoForAccount(String account) {
|
||||
return AccountSessionManager.getInstance().getInstanceInfo(instance.getAccount(account).domain);
|
||||
}
|
||||
|
||||
public void updateAccountInfo(String id, Account account){
|
||||
AccountSession session=getAccount(id);
|
||||
session.self=account;
|
||||
|
||||
@@ -400,10 +400,12 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
||||
public void onPollOptionClick(PollOptionStatusDisplayItem.Holder holder){
|
||||
Poll poll=holder.getItem().poll;
|
||||
Poll.Option option=holder.getItem().option;
|
||||
if(poll.multiple){
|
||||
if(poll.multiple || GlobalUserPreferences.voteButtonForSingleChoice){
|
||||
if(poll.selectedOptions==null)
|
||||
poll.selectedOptions=new ArrayList<>();
|
||||
if(poll.selectedOptions.contains(option)){
|
||||
boolean optionContained=poll.selectedOptions.contains(option);
|
||||
if(!poll.multiple) poll.selectedOptions.clear();
|
||||
if(optionContained){
|
||||
poll.selectedOptions.remove(option);
|
||||
holder.itemView.setSelected(false);
|
||||
}else{
|
||||
@@ -412,6 +414,9 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
||||
}
|
||||
for(int i=0;i<list.getChildCount();i++){
|
||||
RecyclerView.ViewHolder vh=list.getChildViewHolder(list.getChildAt(i));
|
||||
if(!poll.multiple && vh instanceof PollOptionStatusDisplayItem.Holder item){
|
||||
if (item != holder) item.itemView.setSelected(false);
|
||||
}
|
||||
if(vh instanceof PollFooterStatusDisplayItem.Holder footer){
|
||||
if(footer.getItemID().equals(holder.getItemID())){
|
||||
footer.rebind();
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
package org.joinmastodon.android.fragments;
|
||||
|
||||
import static org.joinmastodon.android.GlobalUserPreferences.recentLanguages;
|
||||
import static org.joinmastodon.android.utils.MastodonLanguage.allLanguages;
|
||||
import static org.joinmastodon.android.utils.MastodonLanguage.defaultRecentLanguages;
|
||||
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
@@ -29,11 +33,13 @@ import android.text.Spanned;
|
||||
import android.text.TextUtils;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.Log;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.SubMenu;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewOutlineProvider;
|
||||
@@ -41,6 +47,7 @@ import android.view.WindowManager;
|
||||
import android.view.animation.LinearInterpolator;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.Button;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.EditText;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageButton;
|
||||
@@ -54,6 +61,7 @@ import android.widget.Toast;
|
||||
import com.twitter.twittertext.TwitterTextEmojiRegex;
|
||||
|
||||
import org.joinmastodon.android.E;
|
||||
import org.joinmastodon.android.GlobalUserPreferences;
|
||||
import org.joinmastodon.android.MastodonApp;
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.api.MastodonAPIController;
|
||||
@@ -94,6 +102,7 @@ import org.joinmastodon.android.ui.views.ComposeEditText;
|
||||
import org.joinmastodon.android.ui.views.ComposeMediaLayout;
|
||||
import org.joinmastodon.android.ui.views.ReorderableLinearLayout;
|
||||
import org.joinmastodon.android.ui.views.SizeListenerLinearLayout;
|
||||
import org.joinmastodon.android.utils.MastodonLanguage;
|
||||
import org.parceler.Parcel;
|
||||
import org.parceler.Parcels;
|
||||
|
||||
@@ -104,6 +113,7 @@ import java.time.temporal.ChronoUnit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
@@ -144,7 +154,8 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
private String accountID;
|
||||
private int charCount, charLimit, trimmedCharCount;
|
||||
|
||||
private Button publishButton;
|
||||
private Button publishButton, languageButton;
|
||||
private PopupMenu languagePopup;
|
||||
private ImageButton mediaBtn, pollBtn, emojiBtn, spoilerBtn, visibilityBtn;
|
||||
private ImageView sensitiveIcon;
|
||||
private ComposeMediaLayout attachmentsView;
|
||||
@@ -153,6 +164,8 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
private View pollWrap;
|
||||
private View addPollOptionBtn;
|
||||
private View sensitiveItem;
|
||||
private View pollAllowMultipleItem;
|
||||
private CheckBox pollAllowMultipleCheckbox;
|
||||
private TextView pollDurationView;
|
||||
|
||||
private ArrayList<DraftPollOption> pollOptions=new ArrayList<>();
|
||||
@@ -187,6 +200,9 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
private boolean ignoreSelectionChanges=false;
|
||||
private Runnable updateUploadEtaRunnable;
|
||||
|
||||
private String language;
|
||||
private MastodonLanguage.LanguageResolver languageResolver;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState){
|
||||
super.onCreate(savedInstanceState);
|
||||
@@ -198,6 +214,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
instanceDomain=session.domain;
|
||||
customEmojis=AccountSessionManager.getInstance().getCustomEmojis(instanceDomain);
|
||||
instance=AccountSessionManager.getInstance().getInstanceInfo(instanceDomain);
|
||||
languageResolver=new MastodonLanguage.LanguageResolver(instance);
|
||||
if(getArguments().containsKey("editStatus")){
|
||||
editingStatus=Parcels.unwrap(getArguments().getParcelable("editStatus"));
|
||||
redraftStatus=getArguments().getBoolean("redraftStatus");
|
||||
@@ -216,8 +233,6 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
charLimit=instance.configuration.statuses.maxCharacters;
|
||||
else
|
||||
charLimit=500;
|
||||
|
||||
loadDefaultStatusVisibility(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -297,6 +312,9 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
pollOptionsView=view.findViewById(R.id.poll_options);
|
||||
pollWrap=view.findViewById(R.id.poll_wrap);
|
||||
addPollOptionBtn=view.findViewById(R.id.add_poll_option);
|
||||
pollAllowMultipleItem=view.findViewById(R.id.poll_allow_multiple);
|
||||
pollAllowMultipleCheckbox=view.findViewById(R.id.poll_allow_multiple_checkbox);
|
||||
pollAllowMultipleItem.setOnClickListener(v->this.togglePollAllowMultiple());
|
||||
|
||||
addPollOptionBtn.setOnClickListener(v->{
|
||||
createDraftPollOption().edit.requestFocus();
|
||||
@@ -311,6 +329,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
pollBtn.setSelected(true);
|
||||
mediaBtn.setEnabled(false);
|
||||
pollWrap.setVisibility(View.VISIBLE);
|
||||
updatePollAllowMultiple(savedInstanceState.getBoolean("pollAllowMultiple", false));
|
||||
for(String oldText:savedInstanceState.getStringArrayList("pollOptions")){
|
||||
DraftPollOption opt=createDraftPollOption();
|
||||
opt.edit.setText(oldText);
|
||||
@@ -321,6 +340,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
pollBtn.setSelected(true);
|
||||
mediaBtn.setEnabled(false);
|
||||
pollWrap.setVisibility(View.VISIBLE);
|
||||
updatePollAllowMultiple(editingStatus.poll.multiple);
|
||||
for(Poll.Option eopt:editingStatus.poll.options){
|
||||
DraftPollOption opt=createDraftPollOption();
|
||||
opt.edit.setText(eopt.title);
|
||||
@@ -367,6 +387,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
statusVisibility=editingStatus.visibility;
|
||||
}
|
||||
|
||||
loadDefaultStatusVisibility(savedInstanceState);
|
||||
updateVisibilityIcon();
|
||||
|
||||
autocompleteViewController=new ComposeAutocompleteViewController(getActivity(), accountID);
|
||||
@@ -391,9 +412,11 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
outState.putStringArrayList("pollOptions", opts);
|
||||
outState.putInt("pollDuration", pollDuration);
|
||||
outState.putString("pollDurationStr", pollDurationStr);
|
||||
outState.putBoolean("pollAllowMultiple", pollAllowMultipleItem.isSelected());
|
||||
}
|
||||
outState.putBoolean("sensitive", sensitive);
|
||||
outState.putBoolean("hasSpoiler", hasSpoiler);
|
||||
outState.putString("language", language);
|
||||
if(!attachments.isEmpty()){
|
||||
ArrayList<Parcelable> serializedAttachments=new ArrayList<>(attachments.size());
|
||||
for(DraftMediaAttachment att:attachments){
|
||||
@@ -485,14 +508,14 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
spoilerEdit.addTextChangedListener(new SimpleTextWatcher(e->updateCharCounter()));
|
||||
if(replyTo!=null){
|
||||
replyText.setText(getString(R.string.in_reply_to, replyTo.account.displayName));
|
||||
int visibilityNameRes = switch (statusVisibility) {
|
||||
int visibilityNameRes = switch (replyTo.visibility) {
|
||||
case PUBLIC -> R.string.visibility_public;
|
||||
case UNLISTED -> R.string.visibility_unlisted;
|
||||
case UNLISTED -> R.string.sk_visibility_unlisted;
|
||||
case PRIVATE -> R.string.visibility_followers_only;
|
||||
case DIRECT -> R.string.visibility_private;
|
||||
};
|
||||
replyText.setContentDescription(getString(R.string.in_reply_to, replyTo.account.displayName) + ". " + getString(R.string.post_visibility) + ": " + getString(visibilityNameRes));
|
||||
Drawable visibilityIcon = getActivity().getDrawable(switch(statusVisibility){
|
||||
Drawable visibilityIcon = getActivity().getDrawable(switch(replyTo.visibility){
|
||||
case PUBLIC -> R.drawable.ic_fluent_earth_20_regular;
|
||||
case UNLISTED -> R.drawable.ic_fluent_people_community_20_regular;
|
||||
case PRIVATE -> R.drawable.ic_fluent_people_checkmark_20_regular;
|
||||
@@ -533,6 +556,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
spoilerEdit.setText(replyTo.spoilerText);
|
||||
spoilerBtn.setSelected(true);
|
||||
}
|
||||
if (replyTo.language != null && !replyTo.language.isEmpty()) updateLanguage(replyTo.language);
|
||||
}
|
||||
}else if (editingStatus==null || editingStatus.inReplyToId==null){
|
||||
// TODO: remove workaround after https://github.com/mastodon/mastodon-android/issues/341 gets fixed
|
||||
@@ -545,6 +569,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
ignoreSelectionChanges=true;
|
||||
mainEditText.setSelection(mainEditText.length());
|
||||
ignoreSelectionChanges=false;
|
||||
updateLanguage(editingStatus.language);
|
||||
if(!editingStatus.mediaAttachments.isEmpty()){
|
||||
attachmentsView.setVisibility(View.VISIBLE);
|
||||
for(Attachment att:editingStatus.mediaAttachments){
|
||||
@@ -607,6 +632,10 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
sendError.setVisibility(View.GONE);
|
||||
sendProgress.setVisibility(View.GONE);
|
||||
|
||||
LinearLayout.LayoutParams langParams=new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
langParams.setMarginEnd(V.dp(8));
|
||||
wrap.addView(buildLanguageSelector(), langParams);
|
||||
|
||||
wrap.addView(publishButton, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||
wrap.setPadding(V.dp(16), V.dp(4), V.dp(16), V.dp(8));
|
||||
wrap.setClipToPadding(false);
|
||||
@@ -616,6 +645,56 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
updatePublishButtonState();
|
||||
}
|
||||
|
||||
private void updateLanguage(String lang) {
|
||||
updateLanguage(languageResolver.from(lang));
|
||||
}
|
||||
|
||||
private void updateLanguage(MastodonLanguage loc) {
|
||||
language = loc.getLanguage();
|
||||
languageButton.setText(loc.getLanguageName());
|
||||
languageButton.setContentDescription(getActivity().getString(R.string.sk_post_language, loc.getDefaultName()));
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
private Button buildLanguageSelector() {
|
||||
TypedValue typedValue = new TypedValue();
|
||||
getActivity().getTheme().resolveAttribute(android.R.attr.textColorSecondary, typedValue, true);
|
||||
|
||||
languageButton=new Button(getActivity());
|
||||
languageButton.setTextColor(typedValue.data);
|
||||
languageButton.setBackground(getActivity().getDrawable(R.drawable.bg_text_button));
|
||||
languageButton.setPadding(V.dp(8), 0, V.dp(8), 0);
|
||||
languageButton.setCompoundDrawablesRelativeWithIntrinsicBounds(getActivity().getDrawable(R.drawable.ic_fluent_local_language_16_regular), null, null, null);
|
||||
languageButton.setCompoundDrawableTintList(languageButton.getTextColors());
|
||||
languageButton.setCompoundDrawablePadding(V.dp(6));
|
||||
|
||||
languagePopup=new PopupMenu(getActivity(), languageButton);
|
||||
languageButton.setOnTouchListener(languagePopup.getDragToOpenListener());
|
||||
languageButton.setOnClickListener(v->languagePopup.show());
|
||||
|
||||
updateLanguage(languageResolver.getDefault());
|
||||
|
||||
Menu languageMenu = languagePopup.getMenu();
|
||||
for (String recentLanguage : Optional.ofNullable(recentLanguages.get(accountID)).orElse(defaultRecentLanguages)) {
|
||||
MastodonLanguage l = languageResolver.from(recentLanguage);
|
||||
languageMenu.add(0, allLanguages.indexOf(l), Menu.NONE, getActivity().getString(R.string.sk_language_name, l.getDefaultName(), l.getLanguageName()));
|
||||
}
|
||||
|
||||
SubMenu allLanguagesMenu = languageMenu.addSubMenu(R.string.sk_available_languages);
|
||||
for (int i = 0; i < allLanguages.size(); i++) {
|
||||
MastodonLanguage l = allLanguages.get(i);
|
||||
allLanguagesMenu.add(0, i, Menu.NONE, getActivity().getString(R.string.sk_language_name, l.getDefaultName(), l.getLanguageName()));
|
||||
}
|
||||
|
||||
languagePopup.setOnMenuItemClickListener(i->{
|
||||
if (i.hasSubMenu()) return false;
|
||||
updateLanguage(allLanguages.get(i.getItemId()));
|
||||
return true;
|
||||
});
|
||||
|
||||
return languageButton;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item){
|
||||
return true;
|
||||
@@ -689,6 +768,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
req.status=text;
|
||||
req.visibility=statusVisibility;
|
||||
req.sensitive=sensitive;
|
||||
req.language=language;
|
||||
if(!attachments.isEmpty()){
|
||||
req.mediaIds=attachments.stream().map(a->a.serverAttachment.id).collect(Collectors.toList());
|
||||
}
|
||||
@@ -698,6 +778,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
if(!pollOptions.isEmpty()){
|
||||
req.poll=new CreateStatus.Request.Poll();
|
||||
req.poll.expiresIn=pollDuration;
|
||||
req.poll.multiple=pollAllowMultipleItem.isSelected();
|
||||
for(DraftPollOption opt:pollOptions)
|
||||
req.poll.options.add(opt.edit.getText().toString());
|
||||
}
|
||||
@@ -765,6 +846,14 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
.setCallback(resCallback)
|
||||
.exec(accountID);
|
||||
}
|
||||
|
||||
if (replyTo == null) {
|
||||
List<String> newRecentLanguages = new ArrayList<>(Optional.ofNullable(recentLanguages.get(accountID)).orElse(defaultRecentLanguages));
|
||||
newRecentLanguages.remove(language);
|
||||
newRecentLanguages.add(0, language);
|
||||
recentLanguages.put(accountID, newRecentLanguages.stream().limit(4).collect(Collectors.toList()));
|
||||
GlobalUserPreferences.save();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasDraft(){
|
||||
@@ -1208,6 +1297,11 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
option.view=LayoutInflater.from(getActivity()).inflate(R.layout.compose_poll_option, pollOptionsView, false);
|
||||
option.edit=option.view.findViewById(R.id.edit);
|
||||
option.dragger=option.view.findViewById(R.id.dragger_thingy);
|
||||
ImageView icon = option.view.findViewById(R.id.icon);
|
||||
icon.setImageDrawable(getContext().getDrawable(pollAllowMultipleItem.isSelected() ?
|
||||
R.drawable.ic_poll_checkbox_regular_selector :
|
||||
R.drawable.ic_poll_option_button
|
||||
));
|
||||
|
||||
option.dragger.setOnLongClickListener(v->{
|
||||
pollOptionsView.startDragging(option.view);
|
||||
@@ -1343,34 +1437,23 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
statusVisibility = (StatusPrivacy) savedInstanceState.getSerializable("visibility");
|
||||
}
|
||||
|
||||
new GetPreferences()
|
||||
.setCallback(new Callback<>(){
|
||||
@Override
|
||||
public void onSuccess(Preferences result){
|
||||
// Only override the reply visibility if our preference is more private
|
||||
if (result.postingDefaultVisibility.isLessVisibleThan(statusVisibility)) {
|
||||
statusVisibility = switch (result.postingDefaultVisibility) {
|
||||
case PUBLIC -> StatusPrivacy.PUBLIC;
|
||||
case UNLISTED -> StatusPrivacy.UNLISTED;
|
||||
case PRIVATE -> StatusPrivacy.PRIVATE;
|
||||
case DIRECT -> StatusPrivacy.DIRECT;
|
||||
};
|
||||
}
|
||||
Preferences prefs = AccountSessionManager.getInstance().getAccount(accountID).preferences;
|
||||
if (prefs != null) {
|
||||
// Only override the reply visibility if our preference is more private
|
||||
if (prefs.postingDefaultVisibility.isLessVisibleThan(statusVisibility)) {
|
||||
statusVisibility = switch (prefs.postingDefaultVisibility) {
|
||||
case PUBLIC -> StatusPrivacy.PUBLIC;
|
||||
case UNLISTED -> StatusPrivacy.UNLISTED;
|
||||
case PRIVATE -> StatusPrivacy.PRIVATE;
|
||||
case DIRECT -> StatusPrivacy.DIRECT;
|
||||
};
|
||||
}
|
||||
|
||||
// A saved privacy setting from a previous compose session wins over all
|
||||
if(savedInstanceState !=null){
|
||||
statusVisibility = (StatusPrivacy) savedInstanceState.getSerializable("visibility");
|
||||
}
|
||||
|
||||
updateVisibilityIcon ();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(ErrorResponse error){
|
||||
Log.w(TAG, "Unable to get user preferences to set default post privacy");
|
||||
}
|
||||
})
|
||||
.exec(accountID);
|
||||
// A saved privacy setting from a previous compose session wins over all
|
||||
if(savedInstanceState !=null){
|
||||
statusVisibility = (StatusPrivacy) savedInstanceState.getSerializable("visibility");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateVisibilityIcon(){
|
||||
@@ -1385,6 +1468,27 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
||||
});
|
||||
}
|
||||
|
||||
private void togglePollAllowMultiple() {
|
||||
updatePollAllowMultiple(!pollAllowMultipleItem.isSelected());
|
||||
}
|
||||
|
||||
private void updatePollAllowMultiple(boolean multiple){
|
||||
pollAllowMultipleItem.setSelected(multiple);
|
||||
pollAllowMultipleCheckbox.setChecked(multiple);
|
||||
ImageView btn = addPollOptionBtn.findViewById(R.id.add_poll_option_icon);
|
||||
btn.setImageDrawable(getContext().getDrawable(multiple ?
|
||||
R.drawable.ic_fluent_add_square_24_regular :
|
||||
R.drawable.ic_fluent_add_circle_24_regular
|
||||
));
|
||||
for (DraftPollOption opt:pollOptions) {
|
||||
ImageView icon = opt.view.findViewById(R.id.icon);
|
||||
icon.setImageDrawable(getContext().getDrawable(multiple ?
|
||||
R.drawable.ic_poll_checkbox_regular_selector :
|
||||
R.drawable.ic_poll_option_button
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSelectionChanged(int start, int end){
|
||||
if(ignoreSelectionChanges)
|
||||
|
||||
@@ -67,7 +67,7 @@ public class FollowRequestsListFragment extends BaseRecyclerFragment<FollowReque
|
||||
@Override
|
||||
public void onAttach(Activity activity) {
|
||||
super.onAttach(activity);
|
||||
setTitle(R.string.follow_requests);
|
||||
setTitle(R.string.sk_follow_requests);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -57,7 +57,7 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
|
||||
public void onCreate(Bundle savedInstanceState){
|
||||
super.onCreate(savedInstanceState);
|
||||
accountID=getArguments().getString("account");
|
||||
setTitle(R.string.app_name);
|
||||
setTitle(R.string.sk_app_name);
|
||||
|
||||
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.N)
|
||||
setRetainInstance(true);
|
||||
|
||||
@@ -5,6 +5,7 @@ import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.app.Activity;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
@@ -17,6 +18,7 @@ import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toolbar;
|
||||
|
||||
@@ -56,7 +58,7 @@ import me.grishka.appkit.utils.V;
|
||||
|
||||
public class HomeTimelineFragment extends StatusListFragment{
|
||||
private ImageButton fab;
|
||||
private TextView toolbarLogo;
|
||||
private ImageView toolbarLogo;
|
||||
private Button toolbarShowNewPostsBtn;
|
||||
private boolean newPostsBtnShown;
|
||||
private AnimatorSet currentNewPostsAnim;
|
||||
@@ -315,9 +317,10 @@ public class HomeTimelineFragment extends StatusListFragment{
|
||||
}
|
||||
|
||||
private void updateToolbarLogo(){
|
||||
toolbarLogo =new TextView(getActivity());
|
||||
toolbarLogo.setText(getString(R.string.app_name).toLowerCase(Locale.getDefault()));
|
||||
toolbarLogo.setTextAppearance(R.style.app_title);
|
||||
toolbarLogo=new ImageView(getActivity());
|
||||
toolbarLogo.setScaleType(ImageView.ScaleType.CENTER);
|
||||
toolbarLogo.setImageResource(R.drawable.logo);
|
||||
toolbarLogo.setImageTintList(ColorStateList.valueOf(UiUtils.getThemeColor(getActivity(), android.R.attr.textColorPrimary)));
|
||||
|
||||
toolbarShowNewPostsBtn=new Button(getActivity());
|
||||
toolbarShowNewPostsBtn.setTextAppearance(R.style.m3_title_medium);
|
||||
@@ -345,7 +348,9 @@ public class HomeTimelineFragment extends StatusListFragment{
|
||||
}
|
||||
|
||||
FrameLayout logoWrap=new FrameLayout(getActivity());
|
||||
logoWrap.addView(toolbarLogo, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER));
|
||||
FrameLayout.LayoutParams logoParams=new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER);
|
||||
logoParams.setMargins(0, V.dp(2), 0, 0);
|
||||
logoWrap.addView(toolbarLogo, logoParams);
|
||||
logoWrap.addView(toolbarShowNewPostsBtn, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, V.dp(32), Gravity.CENTER));
|
||||
|
||||
Toolbar toolbar=getToolbar();
|
||||
|
||||
@@ -57,7 +57,7 @@ public class ListTimelinesFragment extends BaseRecyclerFragment<ListTimeline> im
|
||||
if(args.containsKey("profileAccount")){
|
||||
profileAccountId=args.getString("profileAccount");
|
||||
profileDisplayUsername=args.getString("profileDisplayUsername");
|
||||
setTitle(getString(R.string.lists_with_user, profileDisplayUsername));
|
||||
setTitle(getString(R.string.sk_lists_with_user, profileDisplayUsername));
|
||||
// setHasOptionsMenu(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,7 +249,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
tab.setText(switch(position){
|
||||
case 0 -> R.string.posts;
|
||||
case 1 -> R.string.posts_and_replies;
|
||||
case 2 -> R.string.pinned_posts;
|
||||
case 2 -> R.string.sk_pinned_posts;
|
||||
case 3 -> R.string.media;
|
||||
case 4 -> R.string.profile_about;
|
||||
default -> throw new IllegalStateException();
|
||||
@@ -555,11 +555,14 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
menu.findItem(R.id.mute).setTitle(getString(relationship.muting ? R.string.unmute_user : R.string.mute_user, account.getDisplayUsername()));
|
||||
menu.findItem(R.id.block).setTitle(getString(relationship.blocking ? R.string.unblock_user : R.string.block_user, account.getDisplayUsername()));
|
||||
menu.findItem(R.id.report).setTitle(getString(R.string.report_user, account.getDisplayUsername()));
|
||||
MenuItem manageUserLists=menu.findItem(R.id.manage_user_lists);
|
||||
if(relationship.following) {
|
||||
menu.findItem(R.id.hide_boosts).setTitle(getString(relationship.showingReblogs ? R.string.hide_boosts_from_user : R.string.show_boosts_from_user, account.getDisplayUsername()));
|
||||
manageUserLists.setTitle(getString(R.string.sk_lists_with_user, account.getDisplayUsername()));
|
||||
manageUserLists.setVisible(true);
|
||||
}else {
|
||||
menu.findItem(R.id.hide_boosts).setVisible(false);
|
||||
menu.findItem(R.id.manage_user_lists).setVisible(false);
|
||||
manageUserLists.setVisible(false);
|
||||
}
|
||||
if(!account.isLocal())
|
||||
menu.findItem(R.id.block_domain).setTitle(getString(relationship.domainBlocking ? R.string.unblock_domain : R.string.block_domain, account.getDomain()));
|
||||
@@ -658,7 +661,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
||||
notifyProgress.setIndeterminateTintList(notifyButton.getTextColors());
|
||||
followsYouView.setVisibility(relationship.followedBy ? View.VISIBLE : View.GONE);
|
||||
notifyButton.setSelected(relationship.notifying);
|
||||
if (getActivity() != null) notifyButton.setContentDescription(getString(relationship.notifying ? R.string.user_post_notifications_on : R.string.user_post_notifications_off, '@'+account.username));
|
||||
if (getActivity() != null) notifyButton.setContentDescription(getString(relationship.notifying ? R.string.sk_user_post_notifications_on : R.string.sk_user_post_notifications_off, '@'+account.username));
|
||||
}
|
||||
|
||||
private void onScrollChanged(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY){
|
||||
|
||||
@@ -15,7 +15,6 @@ import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowInsets;
|
||||
import android.view.WindowManager;
|
||||
import android.view.animation.AlphaAnimation;
|
||||
import android.view.animation.LinearInterpolator;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageButton;
|
||||
@@ -49,6 +48,7 @@ import org.joinmastodon.android.ui.utils.UiUtils;
|
||||
import org.joinmastodon.android.updater.GithubSelfUpdater;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import androidx.annotation.DrawableRes;
|
||||
@@ -70,6 +70,7 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
||||
private NotificationPolicyItem notificationPolicyItem;
|
||||
private String accountID;
|
||||
private boolean needUpdateNotificationSettings;
|
||||
private boolean needAppRestart;
|
||||
private PushSubscription pushSubscription;
|
||||
|
||||
private ImageView themeTransitionWindowView;
|
||||
@@ -95,10 +96,11 @@ 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.disable_marquee, R.drawable.ic_fluent_text_more_24_regular, GlobalUserPreferences.disableMarquee, i->{
|
||||
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 ColorPicker());
|
||||
|
||||
items.add(new HeaderItem(R.string.settings_behavior));
|
||||
items.add(new SwitchItem(R.string.settings_gif, R.drawable.ic_fluent_gif_24_regular, GlobalUserPreferences.playGifs, i->{
|
||||
@@ -109,28 +111,33 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
||||
GlobalUserPreferences.useCustomTabs=i.checked;
|
||||
GlobalUserPreferences.save();
|
||||
}));
|
||||
items.add(new SwitchItem(R.string.settings_show_interaction_counts, R.drawable.ic_fluent_number_row_24_regular, GlobalUserPreferences.showInteractionCounts, i->{
|
||||
items.add(new SwitchItem(R.string.sk_settings_show_interaction_counts, R.drawable.ic_fluent_number_row_24_regular, GlobalUserPreferences.showInteractionCounts, i->{
|
||||
GlobalUserPreferences.showInteractionCounts=i.checked;
|
||||
GlobalUserPreferences.save();
|
||||
}));
|
||||
items.add(new SwitchItem(R.string.settings_always_reveal_content_warnings, R.drawable.ic_fluent_chat_warning_24_regular, GlobalUserPreferences.alwaysExpandContentWarnings, i->{
|
||||
items.add(new SwitchItem(R.string.sk_settings_always_reveal_content_warnings, R.drawable.ic_fluent_chat_warning_24_regular, GlobalUserPreferences.alwaysExpandContentWarnings, i->{
|
||||
GlobalUserPreferences.alwaysExpandContentWarnings=i.checked;
|
||||
GlobalUserPreferences.save();
|
||||
}));
|
||||
|
||||
items.add(new HeaderItem(R.string.home_timeline));
|
||||
items.add(new SwitchItem(R.string.settings_show_replies, R.drawable.ic_fluent_chat_multiple_24_regular, GlobalUserPreferences.showReplies, i->{
|
||||
items.add(new SwitchItem(R.string.sk_settings_show_replies, R.drawable.ic_fluent_chat_multiple_24_regular, GlobalUserPreferences.showReplies, i->{
|
||||
GlobalUserPreferences.showReplies=i.checked;
|
||||
GlobalUserPreferences.save();
|
||||
}));
|
||||
items.add(new SwitchItem(R.string.settings_show_boosts, R.drawable.ic_fluent_arrow_repeat_all_24_regular, GlobalUserPreferences.showBoosts, i->{
|
||||
items.add(new SwitchItem(R.string.sk_settings_show_boosts, R.drawable.ic_fluent_arrow_repeat_all_24_regular, GlobalUserPreferences.showBoosts, i->{
|
||||
GlobalUserPreferences.showBoosts=i.checked;
|
||||
GlobalUserPreferences.save();
|
||||
}));
|
||||
items.add(new SwitchItem(R.string.settings_load_new_posts, R.drawable.ic_fluent_arrow_up_24_regular, GlobalUserPreferences.loadNewPosts, i->{
|
||||
items.add(new SwitchItem(R.string.sk_settings_load_new_posts, R.drawable.ic_fluent_arrow_up_24_regular, GlobalUserPreferences.loadNewPosts, i->{
|
||||
GlobalUserPreferences.loadNewPosts=i.checked;
|
||||
GlobalUserPreferences.save();
|
||||
}));
|
||||
items.add(new SwitchItem(R.string.sk_settings_show_federated_timeline, R.drawable.ic_fluent_earth_24_regular, GlobalUserPreferences.showFederatedTimeline, i->{
|
||||
GlobalUserPreferences.showFederatedTimeline=i.checked;
|
||||
GlobalUserPreferences.save();
|
||||
needAppRestart=true;
|
||||
}));
|
||||
|
||||
items.add(new HeaderItem(R.string.settings_notifications));
|
||||
items.add(notificationPolicyItem=new NotificationPolicyItem());
|
||||
@@ -139,6 +146,7 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
||||
items.add(new SwitchItem(R.string.notify_follow, R.drawable.ic_fluent_person_add_24_regular, pushSubscription.alerts.follow, i->onNotificationsChanged(PushNotification.Type.FOLLOW, i.checked)));
|
||||
items.add(new SwitchItem(R.string.notify_reblog, R.drawable.ic_fluent_arrow_repeat_all_24_regular, pushSubscription.alerts.reblog, i->onNotificationsChanged(PushNotification.Type.REBLOG, i.checked)));
|
||||
items.add(new SwitchItem(R.string.notify_mention, R.drawable.ic_at_symbol, pushSubscription.alerts.mention, i->onNotificationsChanged(PushNotification.Type.MENTION, i.checked)));
|
||||
items.add(new SwitchItem(R.string.sk_notify_posts, R.drawable.ic_fluent_alert_24_regular, pushSubscription.alerts.status, i->onNotificationsChanged(PushNotification.Type.STATUS, i.checked)));
|
||||
|
||||
items.add(new HeaderItem(R.string.settings_boring));
|
||||
items.add(new TextItem(R.string.settings_account, ()->UiUtils.launchWebBrowser(getActivity(), "https://"+session.domain+"/auth/edit")));
|
||||
@@ -147,14 +155,18 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
||||
|
||||
items.add(new RedHeaderItem(R.string.settings_spicy));
|
||||
if (GithubSelfUpdater.needSelfUpdating()) {
|
||||
checkForUpdateItem = new TextItem(R.string.check_for_update, GithubSelfUpdater.getInstance()::checkForUpdates);
|
||||
checkForUpdateItem = new TextItem(R.string.sk_check_for_update, GithubSelfUpdater.getInstance()::checkForUpdates);
|
||||
items.add(checkForUpdateItem);
|
||||
}
|
||||
items.add(new TextItem(R.string.settings_contribute_fork, ()->UiUtils.launchWebBrowser(getActivity(), "https://github.com/sk22/megalodon")));
|
||||
items.add(new TextItem(R.string.sk_settings_contribute, ()->UiUtils.launchWebBrowser(getActivity(), "https://github.com/sk22/megalodon")));
|
||||
items.add(new TextItem(R.string.settings_clear_cache, this::clearImageCache));
|
||||
items.add(new TextItem(R.string.sk_clear_recent_languages, ()->UiUtils.showConfirmationAlert(getActivity(), R.string.sk_clear_recent_languages, R.string.sk_confirm_clear_recent_languages, R.string.clear, ()->{
|
||||
GlobalUserPreferences.recentLanguages.remove(accountID);
|
||||
GlobalUserPreferences.save();
|
||||
})));
|
||||
items.add(new TextItem(R.string.log_out, this::confirmLogOut));
|
||||
|
||||
items.add(new FooterItem(getString(R.string.settings_app_version, BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE)));
|
||||
items.add(new FooterItem(getString(R.string.sk_settings_app_version, BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -202,6 +214,11 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
||||
if(needUpdateNotificationSettings && PushSubscriptionManager.arePushNotificationsAvailable()){
|
||||
AccountSessionManager.getInstance().getAccount(accountID).getPushSubscriptionManager().updatePushSettings(pushSubscription);
|
||||
}
|
||||
if(needAppRestart){
|
||||
Intent intent = Intent.makeRestartActivityTask(MastodonApp.context.getPackageManager().getLaunchIntentForPackage(MastodonApp.context.getPackageName()).getComponent());
|
||||
MastodonApp.context.startActivity(intent);
|
||||
Runtime.getRuntime().exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -224,6 +241,12 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
||||
restartActivityToApplyNewTheme();
|
||||
}
|
||||
|
||||
private void onColorPreferenceClick(GlobalUserPreferences.ColorPreference color){
|
||||
GlobalUserPreferences.color=color;
|
||||
GlobalUserPreferences.save();
|
||||
restartActivityToApplyNewTheme();
|
||||
}
|
||||
|
||||
private void onTrueBlackThemeChanged(SwitchItem item){
|
||||
GlobalUserPreferences.trueBlackTheme=item.checked;
|
||||
GlobalUserPreferences.save();
|
||||
@@ -283,6 +306,7 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
||||
case FOLLOW -> subscription.alerts.follow=enabled;
|
||||
case REBLOG -> subscription.alerts.reblog=enabled;
|
||||
case MENTION -> subscription.alerts.mention=subscription.alerts.poll=enabled;
|
||||
case STATUS -> subscription.alerts.status=enabled;
|
||||
}
|
||||
needUpdateNotificationSettings=true;
|
||||
}
|
||||
@@ -377,7 +401,7 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
||||
}
|
||||
|
||||
if (ev.state == GithubSelfUpdater.UpdateState.NO_UPDATE) {
|
||||
Toast.makeText(getActivity(), R.string.no_update_available, Toast.LENGTH_SHORT).show();
|
||||
Toast.makeText(getActivity(), R.string.sk_no_update_available, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -426,6 +450,13 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
||||
}
|
||||
}
|
||||
|
||||
public class ColorPicker extends Item{
|
||||
@Override
|
||||
public int getViewType(){
|
||||
return 8;
|
||||
}
|
||||
}
|
||||
|
||||
private static class ThemeItem extends Item{
|
||||
|
||||
@Override
|
||||
@@ -510,6 +541,7 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
||||
case 5 -> new HeaderViewHolder(true);
|
||||
case 6 -> new FooterViewHolder();
|
||||
case 7 -> new UpdateViewHolder();
|
||||
case 8 -> new ColorPickerViewHolder();
|
||||
default -> throw new IllegalStateException("Unexpected value: "+viewType);
|
||||
};
|
||||
}
|
||||
@@ -638,6 +670,68 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
||||
}
|
||||
}
|
||||
}
|
||||
private class ColorPickerViewHolder extends BindableViewHolder<ColorPicker>{
|
||||
private final Button button;
|
||||
private final PopupMenu popupMenu;
|
||||
private final ImageView icon;
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
public ColorPickerViewHolder(){
|
||||
super(getActivity(), R.layout.item_settings_color_picker, list);
|
||||
icon=findViewById(R.id.icon);
|
||||
button=findViewById(R.id.color_picker_button);
|
||||
popupMenu=new PopupMenu(getActivity(), button, Gravity.CENTER_HORIZONTAL);
|
||||
popupMenu.inflate(R.menu.color_picker);
|
||||
popupMenu.setOnMenuItemClickListener(item->{
|
||||
GlobalUserPreferences.ColorPreference pref;
|
||||
int id=item.getItemId();
|
||||
if(id==R.id.pink_color) {
|
||||
pref = GlobalUserPreferences.ColorPreference.PINK;
|
||||
onColorPreferenceClick(pref);
|
||||
}
|
||||
else if(id==R.id.purple_color) {
|
||||
pref = GlobalUserPreferences.ColorPreference.PURPLE;
|
||||
onColorPreferenceClick(pref);
|
||||
}
|
||||
else if(id==R.id.green_color) {
|
||||
pref = GlobalUserPreferences.ColorPreference.GREEN;
|
||||
onColorPreferenceClick(pref);
|
||||
}
|
||||
else if(id==R.id.blue_color) {
|
||||
pref = GlobalUserPreferences.ColorPreference.BLUE;
|
||||
onColorPreferenceClick(pref);
|
||||
}
|
||||
else if(id==R.id.brown_color) {
|
||||
pref = GlobalUserPreferences.ColorPreference.BROWN;
|
||||
onColorPreferenceClick(pref);
|
||||
}
|
||||
else if(id==R.id.yellow_color) {
|
||||
pref = GlobalUserPreferences.ColorPreference.YELLOW;
|
||||
onColorPreferenceClick(pref);
|
||||
}
|
||||
else
|
||||
return false;
|
||||
return true;
|
||||
});
|
||||
// UiUtils.enablePopupMenuIcons(getActivity(), popupMenu);
|
||||
button.setOnTouchListener(popupMenu.getDragToOpenListener());
|
||||
button.setOnClickListener(v->popupMenu.show());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBind(ColorPicker item){
|
||||
icon.setImageResource(R.drawable.ic_fluent_color_24_regular);
|
||||
button.setText(switch(GlobalUserPreferences.color){
|
||||
case PINK -> R.string.sk_color_theme_pink;
|
||||
case PURPLE -> R.string.sk_color_theme_purple;
|
||||
case GREEN -> R.string.sk_color_theme_green;
|
||||
case BLUE -> R.string.sk_color_theme_blue;
|
||||
case BROWN -> R.string.sk_color_theme_brown;
|
||||
case YELLOW -> R.string.sk_color_theme_yellow;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class NotificationPolicyViewHolder extends BindableViewHolder<NotificationPolicyItem>{
|
||||
private final Button button;
|
||||
@@ -753,10 +847,10 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
||||
if (state == GithubSelfUpdater.UpdateState.CHECKING) return;
|
||||
GithubSelfUpdater.UpdateInfo info=updater.getUpdateInfo();
|
||||
if(state!=GithubSelfUpdater.UpdateState.DOWNLOADED){
|
||||
text.setText(getString(R.string.update_available, info.version));
|
||||
text.setText(getString(R.string.sk_update_available, info.version));
|
||||
button.setText(getString(R.string.download_update, UiUtils.formatFileSize(getActivity(), info.size, false)));
|
||||
}else{
|
||||
text.setText(getString(R.string.update_ready, info.version));
|
||||
text.setText(getString(R.string.sk_update_ready, info.version));
|
||||
button.setText(R.string.install_update);
|
||||
}
|
||||
if(state==GithubSelfUpdater.UpdateState.DOWNLOADING){
|
||||
|
||||
@@ -1,20 +1,32 @@
|
||||
package org.joinmastodon.android.fragments;
|
||||
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.text.SpannableString;
|
||||
import android.text.style.ImageSpan;
|
||||
import android.text.style.ReplacementSpan;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.view.WindowInsets;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.joinmastodon.android.MastodonApp;
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.fragments.onboarding.InstanceCatalogFragment;
|
||||
import org.joinmastodon.android.fragments.onboarding.InstanceCatalogSignupFragment;
|
||||
import org.joinmastodon.android.fragments.onboarding.InstanceChooserLoginFragment;
|
||||
import org.joinmastodon.android.ui.InterpolatingMotionEffect;
|
||||
import org.joinmastodon.android.ui.views.SizeListenerFrameLayout;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.viewpager.widget.PagerAdapter;
|
||||
import androidx.viewpager2.widget.ViewPager2;
|
||||
import me.grishka.appkit.Nav;
|
||||
import me.grishka.appkit.fragments.AppKitFragment;
|
||||
import me.grishka.appkit.utils.V;
|
||||
@@ -23,12 +35,13 @@ public class SplashFragment extends AppKitFragment{
|
||||
|
||||
private SizeListenerFrameLayout contentView;
|
||||
private View artContainer, blueFill, greenFill;
|
||||
private InterpolatingMotionEffect motionEffect;
|
||||
private ViewPager2 pager;
|
||||
private ViewGroup pagerDots;
|
||||
private View artClouds, artPlaneElephant, artRightHill, artLeftHill, artCenterHill;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState){
|
||||
super.onCreate(savedInstanceState);
|
||||
motionEffect=new InterpolatingMotionEffect(MastodonApp.context);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -37,15 +50,44 @@ public class SplashFragment extends AppKitFragment{
|
||||
contentView=(SizeListenerFrameLayout) inflater.inflate(R.layout.fragment_splash, container, false);
|
||||
contentView.findViewById(R.id.btn_get_started).setOnClickListener(this::onButtonClick);
|
||||
contentView.findViewById(R.id.btn_log_in).setOnClickListener(this::onButtonClick);
|
||||
artClouds=contentView.findViewById(R.id.art_clouds);
|
||||
artPlaneElephant=contentView.findViewById(R.id.art_plane_elephant);
|
||||
artRightHill=contentView.findViewById(R.id.art_right_hill);
|
||||
artLeftHill=contentView.findViewById(R.id.art_left_hill);
|
||||
artCenterHill=contentView.findViewById(R.id.art_center_hill);
|
||||
pager=contentView.findViewById(R.id.pager);
|
||||
pagerDots=contentView.findViewById(R.id.pager_dots);
|
||||
pager.setAdapter(new PagerAdapter());
|
||||
pager.setOffscreenPageLimit(3);
|
||||
pager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback(){
|
||||
@Override
|
||||
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels){
|
||||
for(int i=0;i<pagerDots.getChildCount();i++){
|
||||
float alpha;
|
||||
if(i==position){
|
||||
alpha=0.3f+0.7f*(1f-positionOffset);
|
||||
}else if(i==position+1){
|
||||
alpha=0.3f+0.7f*positionOffset;
|
||||
}else{
|
||||
alpha=0.3f;
|
||||
}
|
||||
pagerDots.getChildAt(i).setAlpha(alpha);
|
||||
}
|
||||
|
||||
float parallaxProgress=(position+positionOffset)/2f;
|
||||
artClouds.setTranslationX(V.dp(-27)*(position>=1 ? 1f : positionOffset));
|
||||
artPlaneElephant.setTranslationX(V.dp(101.55f)*parallaxProgress);
|
||||
artLeftHill.setTranslationX(V.dp(-88)*parallaxProgress);
|
||||
artLeftHill.setTranslationY(V.dp(24)*parallaxProgress);
|
||||
artRightHill.setTranslationX(V.dp(-88)*parallaxProgress);
|
||||
artRightHill.setTranslationY(V.dp(-24)*parallaxProgress);
|
||||
artCenterHill.setTranslationX(V.dp(-40)*parallaxProgress);
|
||||
}
|
||||
});
|
||||
|
||||
artContainer=contentView.findViewById(R.id.art_container);
|
||||
blueFill=contentView.findViewById(R.id.blue_fill);
|
||||
greenFill=contentView.findViewById(R.id.green_fill);
|
||||
motionEffect.addViewEffect(new InterpolatingMotionEffect.ViewEffect(contentView.findViewById(R.id.art_clouds), V.dp(-5), V.dp(5), V.dp(-5), V.dp(5)));
|
||||
motionEffect.addViewEffect(new InterpolatingMotionEffect.ViewEffect(contentView.findViewById(R.id.art_right_hill), V.dp(-15), V.dp(25), V.dp(-10), V.dp(10)));
|
||||
motionEffect.addViewEffect(new InterpolatingMotionEffect.ViewEffect(contentView.findViewById(R.id.art_left_hill), V.dp(-25), V.dp(15), V.dp(-15), V.dp(15)));
|
||||
motionEffect.addViewEffect(new InterpolatingMotionEffect.ViewEffect(contentView.findViewById(R.id.art_center_hill), V.dp(-14), V.dp(14), V.dp(-5), V.dp(25)));
|
||||
motionEffect.addViewEffect(new InterpolatingMotionEffect.ViewEffect(contentView.findViewById(R.id.art_plane_elephant), V.dp(-20), V.dp(12), V.dp(-20), V.dp(12)));
|
||||
|
||||
contentView.setSizeListener(new SizeListenerFrameLayout.OnSizeChangedListener(){
|
||||
@Override
|
||||
@@ -66,15 +108,16 @@ public class SplashFragment extends AppKitFragment{
|
||||
|
||||
private void onButtonClick(View v){
|
||||
Bundle extras=new Bundle();
|
||||
extras.putBoolean("signup", v.getId()==R.id.btn_get_started);
|
||||
Nav.go(getActivity(), InstanceCatalogFragment.class, extras);
|
||||
boolean isSignup=v.getId()==R.id.btn_get_started;
|
||||
extras.putBoolean("signup", isSignup);
|
||||
Nav.go(getActivity(), isSignup ? InstanceCatalogSignupFragment.class : InstanceChooserLoginFragment.class, extras);
|
||||
}
|
||||
|
||||
private void updateArtSize(int w, int h){
|
||||
float scale=w/(float)V.dp(412);
|
||||
float scale=w/(float)V.dp(360);
|
||||
artContainer.setScaleX(scale);
|
||||
artContainer.setScaleY(scale);
|
||||
blueFill.setScaleY(h/2f);
|
||||
blueFill.setScaleY(artContainer.getBottom()-V.dp(90));
|
||||
greenFill.setScaleY(h-artContainer.getBottom()+V.dp(90));
|
||||
}
|
||||
|
||||
@@ -100,15 +143,91 @@ public class SplashFragment extends AppKitFragment{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onShown(){
|
||||
super.onShown();
|
||||
motionEffect.activate();
|
||||
private class PagerAdapter extends RecyclerView.Adapter<PagerViewHolder>{
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public PagerViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
|
||||
return new PagerViewHolder(viewType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull PagerViewHolder holder, int position){}
|
||||
|
||||
@Override
|
||||
public int getItemCount(){
|
||||
return 3;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position){
|
||||
return position;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onHidden(){
|
||||
super.onHidden();
|
||||
motionEffect.deactivate();
|
||||
private class PagerViewHolder extends RecyclerView.ViewHolder{
|
||||
public PagerViewHolder(int page){
|
||||
super(new LinearLayout(getActivity()));
|
||||
LinearLayout ll=(LinearLayout) itemView;
|
||||
ll.setOrientation(LinearLayout.VERTICAL);
|
||||
int pad=V.dp(16);
|
||||
ll.setPadding(pad, pad, pad, pad);
|
||||
ll.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
|
||||
TextView title=new TextView(getActivity());
|
||||
title.setTextAppearance(R.style.m3_headline_medium);
|
||||
title.setText(switch(page){
|
||||
case 0 -> {
|
||||
String src=getString(R.string.welcome_page1_title);
|
||||
SpannableString ss=new SpannableString(src);
|
||||
int start=src.indexOf("{logo}");
|
||||
if(start!=-1){
|
||||
LogoSpan span=new LogoSpan(getResources().getDrawable(R.drawable.splash_logo, getActivity().getTheme()));
|
||||
ss.setSpan(span, start, start+6, 0);
|
||||
}
|
||||
yield ss;
|
||||
}
|
||||
case 1 -> getString(R.string.welcome_page2_title);
|
||||
case 2 -> getString(R.string.welcome_page3_title);
|
||||
default -> throw new IllegalStateException("Unexpected value: "+page);
|
||||
});
|
||||
title.setTextColor(0xFF17063B);
|
||||
LinearLayout.LayoutParams lp=new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, V.dp(page==0 ? 46 : 36));
|
||||
lp.bottomMargin=V.dp(page==0 ? 4 : 14);
|
||||
ll.addView(title, lp);
|
||||
|
||||
TextView text=new TextView(getActivity());
|
||||
text.setTextAppearance(R.style.m3_body_medium);
|
||||
text.setText(switch(page){
|
||||
case 0 -> R.string.welcome_page1_text;
|
||||
case 1 -> R.string.welcome_page2_text;
|
||||
case 2 -> R.string.welcome_page3_text;
|
||||
default -> throw new IllegalStateException("Unexpected value: "+page);
|
||||
});
|
||||
text.setTextColor(0xFF17063B);
|
||||
ll.addView(text, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||
}
|
||||
}
|
||||
|
||||
private class LogoSpan extends ReplacementSpan{
|
||||
private final Drawable drawable;
|
||||
|
||||
private LogoSpan(Drawable drawable){
|
||||
this.drawable=drawable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSize(@NonNull Paint paint, CharSequence text, int start, int end, @Nullable Paint.FontMetricsInt fm){
|
||||
return drawable.getIntrinsicWidth();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(@NonNull Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, @NonNull Paint paint){
|
||||
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
|
||||
canvas.save();
|
||||
canvas.translate(x, y-V.dp(20));
|
||||
drawable.draw(canvas);
|
||||
canvas.restore();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -286,13 +286,16 @@ public abstract class BaseAccountListFragment extends BaseRecyclerFragment<BaseA
|
||||
menu.findItem(R.id.mute).setTitle(getString(relationship.muting ? R.string.unmute_user : R.string.mute_user, account.getDisplayUsername()));
|
||||
menu.findItem(R.id.block).setTitle(getString(relationship.blocking ? R.string.unblock_user : R.string.block_user, account.getDisplayUsername()));
|
||||
menu.findItem(R.id.report).setTitle(getString(R.string.report_user, account.getDisplayUsername()));
|
||||
menu.findItem(R.id.manage_user_lists).setTitle(getString(R.string.lists_with_user, account.getDisplayUsername()));
|
||||
MenuItem hideBoosts=menu.findItem(R.id.hide_boosts);
|
||||
MenuItem manageUserLists=menu.findItem(R.id.manage_user_lists);
|
||||
if(relationship.following){
|
||||
hideBoosts.setTitle(getString(relationship.showingReblogs ? R.string.hide_boosts_from_user : R.string.show_boosts_from_user, account.getDisplayUsername()));
|
||||
hideBoosts.setVisible(true);
|
||||
manageUserLists.setTitle(getString(R.string.sk_lists_with_user, account.getDisplayUsername()));
|
||||
manageUserLists.setVisible(true);
|
||||
}else{
|
||||
hideBoosts.setVisible(false);
|
||||
manageUserLists.setVisible(true);
|
||||
}
|
||||
MenuItem blockDomain=menu.findItem(R.id.block_domain);
|
||||
if(!account.isLocal()){
|
||||
|
||||
@@ -19,6 +19,7 @@ import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.joinmastodon.android.BuildConfig;
|
||||
import org.joinmastodon.android.GlobalUserPreferences;
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.fragments.ScrollableToTop;
|
||||
import org.joinmastodon.android.fragments.ListTimelinesFragment;
|
||||
@@ -61,7 +62,7 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
|
||||
private String accountID;
|
||||
private Runnable searchDebouncer=this::onSearchChangedDebounced;
|
||||
|
||||
private static final boolean noFederated = BuildConfig.BUILD_TYPE.equals("noFederatedRelease");
|
||||
private final boolean noFederated = !GlobalUserPreferences.showFederatedTimeline;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState){
|
||||
@@ -163,12 +164,12 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
|
||||
if (noFederated && position > 0) position++;
|
||||
tab.setText(switch(position){
|
||||
case 0 -> R.string.local_timeline;
|
||||
case 1 -> R.string.federated_timeline;
|
||||
case 1 -> R.string.sk_federated_timeline;
|
||||
case 2 -> R.string.hashtags;
|
||||
case 3 -> R.string.posts;
|
||||
case 4 -> R.string.news;
|
||||
case 5 -> R.string.for_you;
|
||||
case 6 -> R.string.list_timelines;
|
||||
case 6 -> R.string.sk_list_timelines;
|
||||
default -> throw new IllegalStateException("Unexpected value: "+position);
|
||||
});
|
||||
tab.view.textView.setAllCaps(true);
|
||||
|
||||
@@ -2,46 +2,30 @@ package org.joinmastodon.android.fragments.onboarding;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.LocaleList;
|
||||
import android.text.Editable;
|
||||
import android.text.TextUtils;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowInsets;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.RadioButton;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.api.MastodonAPIController;
|
||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||
import org.joinmastodon.android.api.MastodonErrorResponse;
|
||||
import org.joinmastodon.android.api.requests.instance.GetInstance;
|
||||
import org.joinmastodon.android.api.requests.catalog.GetCatalogCategories;
|
||||
import org.joinmastodon.android.api.requests.catalog.GetCatalogInstances;
|
||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||
import org.joinmastodon.android.model.Instance;
|
||||
import org.joinmastodon.android.model.catalog.CatalogCategory;
|
||||
import org.joinmastodon.android.model.catalog.CatalogInstance;
|
||||
import org.joinmastodon.android.ui.BetterItemAnimator;
|
||||
import org.joinmastodon.android.ui.DividerItemDecoration;
|
||||
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
|
||||
import org.joinmastodon.android.ui.tabs.TabLayout;
|
||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||
import org.parceler.Parcels;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
@@ -59,317 +43,53 @@ import java.util.stream.Collectors;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.DiffUtil;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import me.grishka.appkit.Nav;
|
||||
import me.grishka.appkit.api.Callback;
|
||||
import me.grishka.appkit.api.ErrorResponse;
|
||||
import me.grishka.appkit.fragments.BaseRecyclerFragment;
|
||||
import me.grishka.appkit.utils.BindableViewHolder;
|
||||
import me.grishka.appkit.utils.MergeRecyclerAdapter;
|
||||
import me.grishka.appkit.utils.SingleViewRecyclerAdapter;
|
||||
import me.grishka.appkit.utils.V;
|
||||
import me.grishka.appkit.views.UsableRecyclerView;
|
||||
import okhttp3.Call;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
|
||||
public class InstanceCatalogFragment extends BaseRecyclerFragment<CatalogInstance>{
|
||||
private InstancesAdapter adapter;
|
||||
private MergeRecyclerAdapter mergeAdapter;
|
||||
private View headerView;
|
||||
private CatalogInstance chosenInstance;
|
||||
private List<CatalogInstance> filteredData=new ArrayList<>();
|
||||
private Button nextButton;
|
||||
private MastodonAPIRequest<?> getCategoriesRequest;
|
||||
private EditText searchEdit;
|
||||
private TabLayout categoriesList;
|
||||
private Runnable searchDebouncer=this::onSearchChangedDebounced;
|
||||
private String currentSearchQuery;
|
||||
private String currentCategory="all";
|
||||
private List<CatalogCategory> categories=new ArrayList<>();
|
||||
private String loadingInstanceDomain;
|
||||
private GetInstance loadingInstanceRequest;
|
||||
private Call loadingInstanceRedirectRequest;
|
||||
private HashMap<String, Instance> instancesCache=new HashMap<>();
|
||||
private ProgressDialog instanceProgressDialog;
|
||||
private View buttonBar;
|
||||
private HashMap<String, String> redirects=new HashMap<>(), redirectsInverse=new HashMap<>();
|
||||
|
||||
private boolean isSignup;
|
||||
abstract class InstanceCatalogFragment extends BaseRecyclerFragment<CatalogInstance>{
|
||||
protected RecyclerView.Adapter adapter;
|
||||
protected MergeRecyclerAdapter mergeAdapter;
|
||||
protected CatalogInstance chosenInstance;
|
||||
protected Button nextButton;
|
||||
protected EditText searchEdit;
|
||||
protected Runnable searchDebouncer=this::onSearchChangedDebounced;
|
||||
protected String currentSearchQuery;
|
||||
protected String loadingInstanceDomain;
|
||||
protected HashMap<String, Instance> instancesCache=new HashMap<>();
|
||||
protected View buttonBar;
|
||||
protected List<CatalogInstance> filteredData=new ArrayList<>();
|
||||
protected GetInstance loadingInstanceRequest;
|
||||
protected Call loadingInstanceRedirectRequest;
|
||||
protected ProgressDialog instanceProgressDialog;
|
||||
protected HashMap<String, String> redirects=new HashMap<>();
|
||||
protected HashMap<String, String> redirectsInverse=new HashMap<>();
|
||||
protected boolean isSignup;
|
||||
protected CatalogInstance fakeInstance=new CatalogInstance();
|
||||
|
||||
private static final double DUNBAR=Math.log(800);
|
||||
|
||||
public InstanceCatalogFragment(){
|
||||
super(R.layout.fragment_onboarding_common, 10);
|
||||
public InstanceCatalogFragment(int layout, int perPage){
|
||||
super(layout, perPage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState){
|
||||
super.onCreate(savedInstanceState);
|
||||
isSignup=getArguments().getBoolean("signup");
|
||||
isSignup=getArguments() != null && getArguments().getBoolean("signup");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context){
|
||||
super.onAttach(context);
|
||||
setRefreshEnabled(false);
|
||||
loadData();
|
||||
}
|
||||
protected abstract void proceedWithAuthOrSignup(Instance instance);
|
||||
|
||||
@Override
|
||||
protected void doLoadData(int offset, int count){
|
||||
currentRequest=new GetCatalogInstances(null, null)
|
||||
.setCallback(new Callback<>(){
|
||||
@Override
|
||||
public void onSuccess(List<CatalogInstance> result){
|
||||
if(getActivity()==null)
|
||||
return;
|
||||
Map<String, List<CatalogInstance>> byLang=result.stream().collect(Collectors.groupingBy(ci->ci.language));
|
||||
for(List<CatalogInstance> group:byLang.values()){
|
||||
Collections.sort(group, (a, b)->{
|
||||
double aa=Math.abs(DUNBAR-Math.log(a.lastWeekUsers));
|
||||
double bb=Math.abs(DUNBAR-Math.log(b.lastWeekUsers));
|
||||
return Double.compare(aa, bb);
|
||||
});
|
||||
}
|
||||
// get the list of user-configured system languages
|
||||
List<String> userLangs;
|
||||
if(Build.VERSION.SDK_INT<24){
|
||||
userLangs=Collections.singletonList(getResources().getConfiguration().locale.getLanguage());
|
||||
}else{
|
||||
LocaleList ll=getResources().getConfiguration().getLocales();
|
||||
userLangs=new ArrayList<>(ll.size());
|
||||
for(int i=0;i<ll.size();i++){
|
||||
userLangs.add(ll.get(i).getLanguage());
|
||||
}
|
||||
}
|
||||
// add instances in preferred languages to the top of the list, in the order of preference
|
||||
ArrayList<CatalogInstance> sortedList=new ArrayList<>();
|
||||
for(String lang:userLangs){
|
||||
List<CatalogInstance> langInstances=byLang.remove(lang);
|
||||
if(langInstances!=null){
|
||||
sortedList.addAll(langInstances);
|
||||
}
|
||||
}
|
||||
// sort the remaining language groups by aggregate lastWeekUsers
|
||||
class InstanceGroup{
|
||||
public int activeUsers;
|
||||
public List<CatalogInstance> instances;
|
||||
}
|
||||
byLang.values().stream().map(il->{
|
||||
InstanceGroup group=new InstanceGroup();
|
||||
group.instances=il;
|
||||
for(CatalogInstance instance:il){
|
||||
group.activeUsers+=instance.lastWeekUsers;
|
||||
}
|
||||
return group;
|
||||
}).sorted(Comparator.comparingInt((InstanceGroup g)->g.activeUsers).reversed()).forEachOrdered(ig->sortedList.addAll(ig.instances));
|
||||
onDataLoaded(sortedList, false);
|
||||
updateFilteredList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(ErrorResponse error){
|
||||
error.showToast(getActivity());
|
||||
onDataLoaded(Collections.emptyList(), false);
|
||||
}
|
||||
})
|
||||
.execNoAuth("");
|
||||
getCategoriesRequest=new GetCatalogCategories(null)
|
||||
.setCallback(new Callback<>(){
|
||||
@Override
|
||||
public void onSuccess(List<CatalogCategory> result){
|
||||
getCategoriesRequest=null;
|
||||
CatalogCategory all=new CatalogCategory();
|
||||
all.category="all";
|
||||
categories.add(all);
|
||||
result.stream().sorted(Comparator.comparingInt((CatalogCategory cc)->cc.serversCount).reversed()).forEach(categories::add);
|
||||
updateCategories();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(ErrorResponse error){
|
||||
getCategoriesRequest=null;
|
||||
error.showToast(getActivity());
|
||||
CatalogCategory all=new CatalogCategory();
|
||||
all.category="all";
|
||||
categories.add(all);
|
||||
updateCategories();
|
||||
}
|
||||
})
|
||||
.execNoAuth("");
|
||||
}
|
||||
|
||||
private void updateCategories(){
|
||||
categoriesList.removeAllTabs();
|
||||
for(CatalogCategory cat:categories){
|
||||
int titleRes=getTitleForCategory(cat.category);
|
||||
TabLayout.Tab tab=categoriesList.newTab().setText(titleRes!=0 ? getString(titleRes) : cat.category).setCustomView(R.layout.item_instance_category);
|
||||
ImageView emoji=tab.getCustomView().findViewById(R.id.emoji);
|
||||
emoji.setImageResource(getEmojiForCategory(cat.category));
|
||||
categoriesList.addTab(tab);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy(){
|
||||
super.onDestroy();
|
||||
if(getCategoriesRequest!=null)
|
||||
getCategoriesRequest.cancel();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RecyclerView.Adapter getAdapter(){
|
||||
headerView=getActivity().getLayoutInflater().inflate(R.layout.header_onboarding_instance_catalog, list, false);
|
||||
searchEdit=headerView.findViewById(R.id.search_edit);
|
||||
categoriesList=headerView.findViewById(R.id.categories_list);
|
||||
categoriesList.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener(){
|
||||
@Override
|
||||
public void onTabSelected(TabLayout.Tab tab){
|
||||
CatalogCategory category=categories.get(tab.getPosition());
|
||||
currentCategory=category.category;
|
||||
updateFilteredList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabUnselected(TabLayout.Tab tab){
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabReselected(TabLayout.Tab tab){
|
||||
|
||||
}
|
||||
});
|
||||
searchEdit.setOnEditorActionListener(this::onSearchEnterPressed);
|
||||
searchEdit.addTextChangedListener(new TextWatcher(){
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after){
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count){
|
||||
searchEdit.removeCallbacks(searchDebouncer);
|
||||
searchEdit.postDelayed(searchDebouncer, 300);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s){
|
||||
}
|
||||
});
|
||||
|
||||
mergeAdapter=new MergeRecyclerAdapter();
|
||||
mergeAdapter.addAdapter(new SingleViewRecyclerAdapter(headerView));
|
||||
mergeAdapter.addAdapter(adapter=new InstancesAdapter());
|
||||
return mergeAdapter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState){
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
nextButton=view.findViewById(R.id.btn_next);
|
||||
nextButton.setOnClickListener(this::onNextClick);
|
||||
nextButton.setEnabled(chosenInstance!=null);
|
||||
view.findViewById(R.id.btn_back).setOnClickListener(v->Nav.finish(this));
|
||||
list.setItemAnimator(new BetterItemAnimator());
|
||||
list.addItemDecoration(new DividerItemDecoration(getActivity(), R.attr.colorPollVoted, 1, 16, 16, DividerItemDecoration.NOT_FIRST));
|
||||
view.setBackgroundColor(UiUtils.getThemeColor(getActivity(), R.attr.colorBackgroundLight));
|
||||
buttonBar=view.findViewById(R.id.button_bar);
|
||||
setStatusBarColor(UiUtils.getThemeColor(getActivity(), R.attr.colorBackgroundLight));
|
||||
}
|
||||
|
||||
private void onNextClick(View v){
|
||||
String domain=chosenInstance.domain;
|
||||
Instance instance=instancesCache.get(domain);
|
||||
if(instance!=null){
|
||||
proceedWithAuthOrSignup(instance);
|
||||
}else{
|
||||
showProgressDialog();
|
||||
if(!domain.equals(loadingInstanceDomain)){
|
||||
loadInstanceInfo(domain, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void proceedWithAuthOrSignup(Instance instance){
|
||||
getActivity().getSystemService(InputMethodManager.class).hideSoftInputFromWindow(contentView.getWindowToken(), 0);
|
||||
if(isSignup){
|
||||
if(!instance.registrations){
|
||||
new M3AlertDialogBuilder(getActivity())
|
||||
.setTitle(R.string.error)
|
||||
.setMessage(R.string.instance_signup_closed)
|
||||
.setPositiveButton(R.string.ok, null)
|
||||
.show();
|
||||
return;
|
||||
}
|
||||
Bundle args=new Bundle();
|
||||
args.putParcelable("instance", Parcels.wrap(instance));
|
||||
Nav.go(getActivity(), InstanceRulesFragment.class, args);
|
||||
}else{
|
||||
AccountSessionManager.getInstance().authenticate(getActivity(), instance);
|
||||
}
|
||||
}
|
||||
|
||||
// private String getEmojiForCategory(String category){
|
||||
// return switch(category){
|
||||
// case "all" -> "💬";
|
||||
// case "academia" -> "📚";
|
||||
// case "activism" -> "✊";
|
||||
// case "food" -> "🍕";
|
||||
// case "furry" -> "🦁";
|
||||
// case "games" -> "🕹";
|
||||
// case "general" -> "🐘";
|
||||
// case "journalism" -> "📰";
|
||||
// case "lgbt" -> "🏳️🌈";
|
||||
// case "regional" -> "📍";
|
||||
// case "art" -> "🎨";
|
||||
// case "music" -> "🎼";
|
||||
// case "tech" -> "📱";
|
||||
// default -> "❓";
|
||||
// };
|
||||
// }
|
||||
|
||||
private int getEmojiForCategory(String category){
|
||||
return switch(category){
|
||||
case "all" -> R.drawable.ic_category_all;
|
||||
case "academia" -> R.drawable.ic_category_academia;
|
||||
case "activism" -> R.drawable.ic_category_activism;
|
||||
case "food" -> R.drawable.ic_category_food;
|
||||
case "furry" -> R.drawable.ic_category_furry;
|
||||
case "games" -> R.drawable.ic_category_games;
|
||||
case "general" -> R.drawable.ic_category_general;
|
||||
case "journalism" -> R.drawable.ic_category_journalism;
|
||||
case "lgbt" -> R.drawable.ic_category_lgbt;
|
||||
case "regional" -> R.drawable.ic_category_regional;
|
||||
case "art" -> R.drawable.ic_category_art;
|
||||
case "music" -> R.drawable.ic_category_music;
|
||||
case "tech" -> R.drawable.ic_category_tech;
|
||||
default -> R.drawable.ic_category_unknown;
|
||||
};
|
||||
}
|
||||
|
||||
private int getTitleForCategory(String category){
|
||||
return switch(category){
|
||||
case "all" -> R.string.category_all;
|
||||
case "academia" -> R.string.category_academia;
|
||||
case "activism" -> R.string.category_activism;
|
||||
case "food" -> R.string.category_food;
|
||||
case "furry" -> R.string.category_furry;
|
||||
case "games" -> R.string.category_games;
|
||||
case "general" -> R.string.category_general;
|
||||
case "journalism" -> R.string.category_journalism;
|
||||
case "lgbt" -> R.string.category_lgbt;
|
||||
case "regional" -> R.string.category_regional;
|
||||
case "art" -> R.string.category_art;
|
||||
case "music" -> R.string.category_music;
|
||||
case "tech" -> R.string.category_tech;
|
||||
default -> 0;
|
||||
};
|
||||
}
|
||||
|
||||
private boolean onSearchEnterPressed(TextView v, int actionId, KeyEvent event){
|
||||
protected boolean onSearchEnterPressed(TextView v, int actionId, KeyEvent event){
|
||||
if(event!=null && event.getAction()!=KeyEvent.ACTION_DOWN)
|
||||
return true;
|
||||
currentSearchQuery=searchEdit.getText().toString().toLowerCase();
|
||||
@@ -385,60 +105,73 @@ public class InstanceCatalogFragment extends BaseRecyclerFragment<CatalogInstanc
|
||||
return true;
|
||||
}
|
||||
|
||||
private void onSearchChangedDebounced(){
|
||||
protected void onSearchChangedDebounced(){
|
||||
currentSearchQuery=searchEdit.getText().toString().toLowerCase();
|
||||
updateFilteredList();
|
||||
loadInstanceInfo(currentSearchQuery, false);
|
||||
}
|
||||
|
||||
private void updateFilteredList(){
|
||||
ArrayList<CatalogInstance> prevData=new ArrayList<>(filteredData);
|
||||
filteredData.clear();
|
||||
for(CatalogInstance instance:data){
|
||||
if(currentCategory.equals("all") || instance.categories.contains(currentCategory)){
|
||||
if(TextUtils.isEmpty(currentSearchQuery) || instance.domain.contains(currentSearchQuery)){
|
||||
if(instance.domain.equals(currentSearchQuery) || !isSignup || !instance.approvalRequired)
|
||||
filteredData.add(instance);
|
||||
}
|
||||
protected List<CatalogInstance> sortInstances(List<CatalogInstance> result){
|
||||
Map<String, List<CatalogInstance>> byLang=result.stream().collect(Collectors.groupingBy(ci->ci.language));
|
||||
for(List<CatalogInstance> group:byLang.values()){
|
||||
Collections.sort(group, (a, b)->{
|
||||
double aa=Math.abs(DUNBAR-Math.log(a.lastWeekUsers));
|
||||
double bb=Math.abs(DUNBAR-Math.log(b.lastWeekUsers));
|
||||
return Double.compare(aa, bb);
|
||||
});
|
||||
}
|
||||
// get the list of user-configured system languages
|
||||
List<String> userLangs;
|
||||
if(Build.VERSION.SDK_INT<24){
|
||||
userLangs=Collections.singletonList(getResources().getConfiguration().locale.getLanguage());
|
||||
}else{
|
||||
LocaleList ll=getResources().getConfiguration().getLocales();
|
||||
userLangs=new ArrayList<>(ll.size());
|
||||
for(int i=0;i<ll.size();i++){
|
||||
userLangs.add(ll.get(i).getLanguage());
|
||||
}
|
||||
}
|
||||
DiffUtil.calculateDiff(new DiffUtil.Callback(){
|
||||
@Override
|
||||
public int getOldListSize(){
|
||||
return prevData.size();
|
||||
// add instances in preferred languages to the top of the list, in the order of preference
|
||||
ArrayList<CatalogInstance> sortedList=new ArrayList<>();
|
||||
for(String lang:userLangs){
|
||||
List<CatalogInstance> langInstances=byLang.remove(lang);
|
||||
if(langInstances!=null){
|
||||
sortedList.addAll(langInstances);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNewListSize(){
|
||||
return filteredData.size();
|
||||
}
|
||||
// sort the remaining language groups by aggregate lastWeekUsers
|
||||
class InstanceGroup{
|
||||
public int activeUsers;
|
||||
public List<CatalogInstance> instances;
|
||||
}
|
||||
byLang.values().stream().map(il->{
|
||||
InstanceGroup group=new InstanceGroup();
|
||||
group.instances=il;
|
||||
for(CatalogInstance instance:il){
|
||||
group.activeUsers+=instance.lastWeekUsers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition){
|
||||
return prevData.get(oldItemPosition)==filteredData.get(newItemPosition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition){
|
||||
return prevData.get(oldItemPosition)==filteredData.get(newItemPosition);
|
||||
}
|
||||
}).dispatchUpdatesTo(adapter);
|
||||
return group;
|
||||
}).sorted(Comparator.comparingInt((InstanceGroup g)->g.activeUsers).reversed()).forEachOrdered(ig->sortedList.addAll(ig.instances));
|
||||
return sortedList;
|
||||
}
|
||||
|
||||
private void showProgressDialog(){
|
||||
protected abstract void updateFilteredList();
|
||||
|
||||
protected void showProgressDialog(){
|
||||
instanceProgressDialog=new ProgressDialog(getActivity());
|
||||
instanceProgressDialog.setMessage(getString(R.string.loading_instance));
|
||||
instanceProgressDialog.setOnCancelListener(dialog->cancelLoadingInstanceInfo());
|
||||
instanceProgressDialog.show();
|
||||
}
|
||||
|
||||
private String normalizeInstanceDomain(String _domain){
|
||||
protected String normalizeInstanceDomain(String _domain){
|
||||
if(TextUtils.isEmpty(_domain))
|
||||
return null;
|
||||
if(_domain.contains(":")){
|
||||
try{
|
||||
_domain=Uri.parse(_domain).getAuthority();
|
||||
}catch(Exception ignore){}
|
||||
}catch(Exception ignore){
|
||||
}
|
||||
if(TextUtils.isEmpty(_domain))
|
||||
return null;
|
||||
}
|
||||
@@ -453,12 +186,12 @@ public class InstanceCatalogFragment extends BaseRecyclerFragment<CatalogInstanc
|
||||
return domain;
|
||||
}
|
||||
|
||||
private void loadInstanceInfo(String _domain, boolean isFromRedirect){
|
||||
protected void loadInstanceInfo(String _domain, boolean isFromRedirect){
|
||||
String domain=normalizeInstanceDomain(_domain);
|
||||
Instance cachedInstance=instancesCache.get(domain);
|
||||
if(cachedInstance!=null){
|
||||
for(CatalogInstance ci:filteredData){
|
||||
if(ci.domain.equals(domain))
|
||||
for(CatalogInstance ci : filteredData){
|
||||
if(ci.domain.equals(domain) && ci!=fakeInstance)
|
||||
return;
|
||||
}
|
||||
CatalogInstance ci=cachedInstance.toCatalogInstance();
|
||||
@@ -476,44 +209,57 @@ public class InstanceCatalogFragment extends BaseRecyclerFragment<CatalogInstanc
|
||||
loadingInstanceDomain=domain;
|
||||
loadingInstanceRequest=new GetInstance();
|
||||
loadingInstanceRequest.setCallback(new Callback<>(){
|
||||
@Override
|
||||
public void onSuccess(Instance result){
|
||||
loadingInstanceRequest=null;
|
||||
loadingInstanceDomain=null;
|
||||
result.uri=domain; // needed for instances that use domain redirection
|
||||
instancesCache.put(domain, result);
|
||||
if(instanceProgressDialog!=null){
|
||||
instanceProgressDialog.dismiss();
|
||||
instanceProgressDialog=null;
|
||||
proceedWithAuthOrSignup(result);
|
||||
}
|
||||
if(Objects.equals(domain, currentSearchQuery) || Objects.equals(currentSearchQuery, redirects.get(domain)) || Objects.equals(currentSearchQuery, redirectsInverse.get(domain))){
|
||||
boolean found=false;
|
||||
for(CatalogInstance ci:filteredData){
|
||||
if(ci.domain.equals(domain)){
|
||||
found=true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!found){
|
||||
CatalogInstance ci=result.toCatalogInstance();
|
||||
filteredData.add(0, ci);
|
||||
adapter.notifyItemInserted(0);
|
||||
}
|
||||
@Override
|
||||
public void onSuccess(Instance result){
|
||||
loadingInstanceRequest=null;
|
||||
loadingInstanceDomain=null;
|
||||
result.uri=domain; // needed for instances that use domain redirection
|
||||
instancesCache.put(domain, result);
|
||||
if(instanceProgressDialog!=null){
|
||||
instanceProgressDialog.dismiss();
|
||||
instanceProgressDialog=null;
|
||||
proceedWithAuthOrSignup(result);
|
||||
}
|
||||
if(Objects.equals(domain, currentSearchQuery) || Objects.equals(currentSearchQuery, redirects.get(domain)) || Objects.equals(currentSearchQuery, redirectsInverse.get(domain))){
|
||||
boolean found=false;
|
||||
for(CatalogInstance ci : filteredData){
|
||||
if(ci.domain.equals(domain) && ci!=fakeInstance){
|
||||
found=true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!found){
|
||||
CatalogInstance ci=result.toCatalogInstance();
|
||||
if(filteredData.size()==1 && filteredData.get(0)==fakeInstance){
|
||||
filteredData.set(0, ci);
|
||||
adapter.notifyItemChanged(0);
|
||||
}else{
|
||||
filteredData.add(0, ci);
|
||||
adapter.notifyItemInserted(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(ErrorResponse error){
|
||||
loadingInstanceRequest=null;
|
||||
if(!isFromRedirect && error instanceof MastodonErrorResponse me && me.httpStatus==404){
|
||||
fetchDomainFromHostMetaAndMaybeRetry(domain, error);
|
||||
return;
|
||||
@Override
|
||||
public void onError(ErrorResponse error){
|
||||
loadingInstanceRequest=null;
|
||||
if(!isFromRedirect && error instanceof MastodonErrorResponse me && me.httpStatus==404){
|
||||
fetchDomainFromHostMetaAndMaybeRetry(domain, error);
|
||||
return;
|
||||
}
|
||||
loadingInstanceDomain=null;
|
||||
showInstanceInfoLoadError(domain, error);
|
||||
if(fakeInstance!=null){
|
||||
fakeInstance.description=getString(R.string.error);
|
||||
if(filteredData.size()>0 && filteredData.get(0)==fakeInstance){
|
||||
if(list.findViewHolderForAdapterPosition(1) instanceof BindableViewHolder<?> ivh){
|
||||
ivh.rebind();
|
||||
}
|
||||
loadingInstanceDomain=null;
|
||||
showInstanceInfoLoadError(domain, error);
|
||||
}
|
||||
}).execNoAuth(domain);
|
||||
}
|
||||
}
|
||||
}).execNoAuth(domain);
|
||||
}
|
||||
|
||||
private void cancelLoadingInstanceInfo(){
|
||||
@@ -584,7 +330,7 @@ public class InstanceCatalogFragment extends BaseRecyclerFragment<CatalogInstanc
|
||||
InputSource source=new InputSource(response.body().charStream());
|
||||
Document doc=DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(source);
|
||||
NodeList list=doc.getElementsByTagName("Link");
|
||||
for(int i=0;i<list.getLength();i++){
|
||||
for(int i=0; i<list.getLength(); i++){
|
||||
if(list.item(i) instanceof Element el){
|
||||
String template=el.getAttribute("template");
|
||||
if("lrdd".equals(el.getAttribute("rel")) && !TextUtils.isEmpty(template) && template.contains("{uri}")){
|
||||
@@ -616,78 +362,26 @@ public class InstanceCatalogFragment extends BaseRecyclerFragment<CatalogInstanc
|
||||
}
|
||||
}
|
||||
|
||||
private class InstancesAdapter extends UsableRecyclerView.Adapter<InstanceViewHolder>{
|
||||
public InstancesAdapter(){
|
||||
super(imgLoader);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public InstanceViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
|
||||
return new InstanceViewHolder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(InstanceViewHolder holder, int position){
|
||||
holder.bind(filteredData.get(position));
|
||||
super.onBindViewHolder(holder, position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount(){
|
||||
return filteredData.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position){
|
||||
return -1;
|
||||
}
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState){
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
nextButton=view.findViewById(R.id.btn_next);
|
||||
nextButton.setOnClickListener(this::onNextClick);
|
||||
nextButton.setEnabled(chosenInstance!=null);
|
||||
buttonBar=view.findViewById(R.id.button_bar);
|
||||
setRefreshEnabled(false);
|
||||
}
|
||||
|
||||
private class InstanceViewHolder extends BindableViewHolder<CatalogInstance> implements UsableRecyclerView.Clickable{
|
||||
private final TextView title, description, userCount, lang;
|
||||
private final RadioButton radioButton;
|
||||
|
||||
public InstanceViewHolder(){
|
||||
super(getActivity(), R.layout.item_instance_catalog, list);
|
||||
title=findViewById(R.id.title);
|
||||
description=findViewById(R.id.description);
|
||||
userCount=findViewById(R.id.user_count);
|
||||
lang=findViewById(R.id.lang);
|
||||
radioButton=findViewById(R.id.radiobtn);
|
||||
if(Build.VERSION.SDK_INT<Build.VERSION_CODES.N){
|
||||
UiUtils.fixCompoundDrawableTintOnAndroid6(userCount);
|
||||
UiUtils.fixCompoundDrawableTintOnAndroid6(lang);
|
||||
protected void onNextClick(View v){
|
||||
String domain=chosenInstance.domain;
|
||||
Instance instance=instancesCache.get(domain);
|
||||
if(instance!=null){
|
||||
proceedWithAuthOrSignup(instance);
|
||||
}else{
|
||||
showProgressDialog();
|
||||
if(!domain.equals(loadingInstanceDomain)){
|
||||
loadInstanceInfo(domain, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBind(CatalogInstance item){
|
||||
title.setText(item.normalizedDomain);
|
||||
description.setText(item.description);
|
||||
userCount.setText(UiUtils.abbreviateNumber(item.totalUsers));
|
||||
lang.setText(item.language.toUpperCase());
|
||||
radioButton.setChecked(chosenInstance==item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(){
|
||||
if(chosenInstance==item)
|
||||
return;
|
||||
if(chosenInstance!=null){
|
||||
int idx=filteredData.indexOf(chosenInstance);
|
||||
if(idx!=-1){
|
||||
RecyclerView.ViewHolder holder=list.findViewHolderForAdapterPosition(mergeAdapter.getPositionForAdapter(adapter)+idx);
|
||||
if(holder instanceof InstanceViewHolder ivh){
|
||||
ivh.radioButton.setChecked(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
radioButton.setChecked(true);
|
||||
if(chosenInstance==null)
|
||||
nextButton.setEnabled(true);
|
||||
chosenInstance=item;
|
||||
loadInstanceInfo(chosenInstance.domain, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,374 @@
|
||||
package org.joinmastodon.android.fragments.onboarding;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.LocaleList;
|
||||
import android.text.Editable;
|
||||
import android.text.TextUtils;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.RadioButton;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||
import org.joinmastodon.android.api.requests.catalog.GetCatalogCategories;
|
||||
import org.joinmastodon.android.api.requests.catalog.GetCatalogInstances;
|
||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||
import org.joinmastodon.android.model.Instance;
|
||||
import org.joinmastodon.android.model.catalog.CatalogCategory;
|
||||
import org.joinmastodon.android.model.catalog.CatalogInstance;
|
||||
import org.joinmastodon.android.ui.BetterItemAnimator;
|
||||
import org.joinmastodon.android.ui.DividerItemDecoration;
|
||||
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
|
||||
import org.joinmastodon.android.ui.tabs.TabLayout;
|
||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||
import org.parceler.Parcels;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.DiffUtil;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import me.grishka.appkit.Nav;
|
||||
import me.grishka.appkit.api.Callback;
|
||||
import me.grishka.appkit.api.ErrorResponse;
|
||||
import me.grishka.appkit.utils.BindableViewHolder;
|
||||
import me.grishka.appkit.utils.MergeRecyclerAdapter;
|
||||
import me.grishka.appkit.utils.SingleViewRecyclerAdapter;
|
||||
import me.grishka.appkit.views.UsableRecyclerView;
|
||||
|
||||
public class InstanceCatalogSignupFragment extends InstanceCatalogFragment{
|
||||
private View headerView;
|
||||
private MastodonAPIRequest<?> getCategoriesRequest;
|
||||
private TabLayout categoriesList;
|
||||
private String currentCategory="all";
|
||||
private List<CatalogCategory> categories=new ArrayList<>();
|
||||
|
||||
|
||||
public InstanceCatalogSignupFragment(){
|
||||
super(R.layout.fragment_onboarding_common, 10);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context){
|
||||
super.onAttach(context);
|
||||
setRefreshEnabled(false);
|
||||
loadData();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doLoadData(int offset, int count){
|
||||
currentRequest=new GetCatalogInstances(null, null)
|
||||
.setCallback(new Callback<>(){
|
||||
@Override
|
||||
public void onSuccess(List<CatalogInstance> result){
|
||||
if(getActivity()==null)
|
||||
return;
|
||||
onDataLoaded(sortInstances(result), false);
|
||||
updateFilteredList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(ErrorResponse error){
|
||||
error.showToast(getActivity());
|
||||
onDataLoaded(Collections.emptyList(), false);
|
||||
}
|
||||
})
|
||||
.execNoAuth("");
|
||||
getCategoriesRequest=new GetCatalogCategories(null)
|
||||
.setCallback(new Callback<>(){
|
||||
@Override
|
||||
public void onSuccess(List<CatalogCategory> result){
|
||||
getCategoriesRequest=null;
|
||||
CatalogCategory all=new CatalogCategory();
|
||||
all.category="all";
|
||||
categories.add(all);
|
||||
result.stream().sorted(Comparator.comparingInt((CatalogCategory cc)->cc.serversCount).reversed()).forEach(categories::add);
|
||||
updateCategories();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(ErrorResponse error){
|
||||
getCategoriesRequest=null;
|
||||
error.showToast(getActivity());
|
||||
CatalogCategory all=new CatalogCategory();
|
||||
all.category="all";
|
||||
categories.add(all);
|
||||
updateCategories();
|
||||
}
|
||||
})
|
||||
.execNoAuth("");
|
||||
}
|
||||
|
||||
private void updateCategories(){
|
||||
categoriesList.removeAllTabs();
|
||||
for(CatalogCategory cat:categories){
|
||||
int titleRes=getTitleForCategory(cat.category);
|
||||
TabLayout.Tab tab=categoriesList.newTab().setText(titleRes!=0 ? getString(titleRes) : cat.category).setCustomView(R.layout.item_instance_category);
|
||||
ImageView emoji=tab.getCustomView().findViewById(R.id.emoji);
|
||||
emoji.setImageResource(getEmojiForCategory(cat.category));
|
||||
categoriesList.addTab(tab);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy(){
|
||||
super.onDestroy();
|
||||
if(getCategoriesRequest!=null)
|
||||
getCategoriesRequest.cancel();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RecyclerView.Adapter getAdapter(){
|
||||
headerView=getActivity().getLayoutInflater().inflate(R.layout.header_onboarding_instance_catalog, list, false);
|
||||
searchEdit=headerView.findViewById(R.id.search_edit);
|
||||
categoriesList=headerView.findViewById(R.id.categories_list);
|
||||
categoriesList.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener(){
|
||||
@Override
|
||||
public void onTabSelected(TabLayout.Tab tab){
|
||||
CatalogCategory category=categories.get(tab.getPosition());
|
||||
currentCategory=category.category;
|
||||
updateFilteredList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabUnselected(TabLayout.Tab tab){
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabReselected(TabLayout.Tab tab){
|
||||
|
||||
}
|
||||
});
|
||||
searchEdit.setOnEditorActionListener(this::onSearchEnterPressed);
|
||||
searchEdit.addTextChangedListener(new TextWatcher(){
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after){
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count){
|
||||
searchEdit.removeCallbacks(searchDebouncer);
|
||||
searchEdit.postDelayed(searchDebouncer, 300);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s){
|
||||
}
|
||||
});
|
||||
|
||||
mergeAdapter=new MergeRecyclerAdapter();
|
||||
mergeAdapter.addAdapter(new SingleViewRecyclerAdapter(headerView));
|
||||
mergeAdapter.addAdapter(adapter=new InstancesAdapter());
|
||||
return mergeAdapter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState){
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
view.findViewById(R.id.btn_back).setOnClickListener(v->Nav.finish(this));
|
||||
list.setItemAnimator(new BetterItemAnimator());
|
||||
list.addItemDecoration(new DividerItemDecoration(getActivity(), R.attr.colorPollVoted, 1, 16, 16, DividerItemDecoration.NOT_FIRST));
|
||||
view.setBackgroundColor(UiUtils.getThemeColor(getActivity(), R.attr.colorBackgroundLight));
|
||||
setStatusBarColor(UiUtils.getThemeColor(getActivity(), R.attr.colorBackgroundLight));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void proceedWithAuthOrSignup(Instance instance){
|
||||
getActivity().getSystemService(InputMethodManager.class).hideSoftInputFromWindow(contentView.getWindowToken(), 0);
|
||||
if(isSignup){
|
||||
if(!instance.registrations){
|
||||
new M3AlertDialogBuilder(getActivity())
|
||||
.setTitle(R.string.error)
|
||||
.setMessage(R.string.instance_signup_closed)
|
||||
.setPositiveButton(R.string.ok, null)
|
||||
.show();
|
||||
return;
|
||||
}
|
||||
Bundle args=new Bundle();
|
||||
args.putParcelable("instance", Parcels.wrap(instance));
|
||||
Nav.go(getActivity(), InstanceRulesFragment.class, args);
|
||||
}else{
|
||||
}
|
||||
}
|
||||
|
||||
// private String getEmojiForCategory(String category){
|
||||
// return switch(category){
|
||||
// case "all" -> "💬";
|
||||
// case "academia" -> "📚";
|
||||
// case "activism" -> "✊";
|
||||
// case "food" -> "🍕";
|
||||
// case "furry" -> "🦁";
|
||||
// case "games" -> "🕹";
|
||||
// case "general" -> "🐘";
|
||||
// case "journalism" -> "📰";
|
||||
// case "lgbt" -> "🏳️🌈";
|
||||
// case "regional" -> "📍";
|
||||
// case "art" -> "🎨";
|
||||
// case "music" -> "🎼";
|
||||
// case "tech" -> "📱";
|
||||
// default -> "❓";
|
||||
// };
|
||||
// }
|
||||
|
||||
private int getEmojiForCategory(String category){
|
||||
return switch(category){
|
||||
case "all" -> R.drawable.ic_category_all;
|
||||
case "academia" -> R.drawable.ic_category_academia;
|
||||
case "activism" -> R.drawable.ic_category_activism;
|
||||
case "food" -> R.drawable.ic_category_food;
|
||||
case "furry" -> R.drawable.ic_category_furry;
|
||||
case "games" -> R.drawable.ic_category_games;
|
||||
case "general" -> R.drawable.ic_category_general;
|
||||
case "journalism" -> R.drawable.ic_category_journalism;
|
||||
case "lgbt" -> R.drawable.ic_category_lgbt;
|
||||
case "regional" -> R.drawable.ic_category_regional;
|
||||
case "art" -> R.drawable.ic_category_art;
|
||||
case "music" -> R.drawable.ic_category_music;
|
||||
case "tech" -> R.drawable.ic_category_tech;
|
||||
default -> R.drawable.ic_category_unknown;
|
||||
};
|
||||
}
|
||||
|
||||
private int getTitleForCategory(String category){
|
||||
return switch(category){
|
||||
case "all" -> R.string.category_all;
|
||||
case "academia" -> R.string.category_academia;
|
||||
case "activism" -> R.string.category_activism;
|
||||
case "food" -> R.string.category_food;
|
||||
case "furry" -> R.string.category_furry;
|
||||
case "games" -> R.string.category_games;
|
||||
case "general" -> R.string.category_general;
|
||||
case "journalism" -> R.string.category_journalism;
|
||||
case "lgbt" -> R.string.category_lgbt;
|
||||
case "regional" -> R.string.category_regional;
|
||||
case "art" -> R.string.category_art;
|
||||
case "music" -> R.string.category_music;
|
||||
case "tech" -> R.string.category_tech;
|
||||
default -> 0;
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateFilteredList(){
|
||||
ArrayList<CatalogInstance> prevData=new ArrayList<>(filteredData);
|
||||
filteredData.clear();
|
||||
for(CatalogInstance instance:data){
|
||||
if(currentCategory.equals("all") || instance.categories.contains(currentCategory)){
|
||||
if(TextUtils.isEmpty(currentSearchQuery) || instance.domain.contains(currentSearchQuery)){
|
||||
if(instance.domain.equals(currentSearchQuery) || !isSignup || !instance.approvalRequired)
|
||||
filteredData.add(instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
DiffUtil.calculateDiff(new DiffUtil.Callback(){
|
||||
@Override
|
||||
public int getOldListSize(){
|
||||
return prevData.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNewListSize(){
|
||||
return filteredData.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition){
|
||||
return prevData.get(oldItemPosition)==filteredData.get(newItemPosition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition){
|
||||
return prevData.get(oldItemPosition)==filteredData.get(newItemPosition);
|
||||
}
|
||||
}).dispatchUpdatesTo(adapter);
|
||||
}
|
||||
|
||||
|
||||
private class InstancesAdapter extends UsableRecyclerView.Adapter<InstanceCatalogSignupFragment.InstanceViewHolder>{
|
||||
public InstancesAdapter(){
|
||||
super(imgLoader);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public InstanceCatalogSignupFragment.InstanceViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
|
||||
return new InstanceCatalogSignupFragment.InstanceViewHolder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(InstanceCatalogSignupFragment.InstanceViewHolder holder, int position){
|
||||
holder.bind(filteredData.get(position));
|
||||
super.onBindViewHolder(holder, position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount(){
|
||||
return filteredData.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position){
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
private class InstanceViewHolder extends BindableViewHolder<CatalogInstance> implements UsableRecyclerView.Clickable{
|
||||
private final TextView title, description, userCount, lang;
|
||||
private final RadioButton radioButton;
|
||||
|
||||
public InstanceViewHolder(){
|
||||
super(getActivity(), R.layout.item_instance_catalog, list);
|
||||
title=findViewById(R.id.title);
|
||||
description=findViewById(R.id.description);
|
||||
userCount=findViewById(R.id.user_count);
|
||||
lang=findViewById(R.id.lang);
|
||||
radioButton=findViewById(R.id.radiobtn);
|
||||
if(Build.VERSION.SDK_INT<Build.VERSION_CODES.N){
|
||||
UiUtils.fixCompoundDrawableTintOnAndroid6(userCount);
|
||||
UiUtils.fixCompoundDrawableTintOnAndroid6(lang);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBind(CatalogInstance item){
|
||||
title.setText(item.normalizedDomain);
|
||||
description.setText(item.description);
|
||||
userCount.setText(UiUtils.abbreviateNumber(item.totalUsers));
|
||||
lang.setText(item.language.toUpperCase());
|
||||
radioButton.setChecked(chosenInstance==item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(){
|
||||
if(chosenInstance==item)
|
||||
return;
|
||||
if(chosenInstance!=null){
|
||||
int idx=filteredData.indexOf(chosenInstance);
|
||||
if(idx!=-1){
|
||||
RecyclerView.ViewHolder holder=list.findViewHolderForAdapterPosition(mergeAdapter.getPositionForAdapter(adapter)+idx);
|
||||
if(holder instanceof InstanceCatalogSignupFragment.InstanceViewHolder ivh){
|
||||
ivh.radioButton.setChecked(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
radioButton.setChecked(true);
|
||||
if(chosenInstance==null)
|
||||
nextButton.setEnabled(true);
|
||||
chosenInstance=item;
|
||||
loadInstanceInfo(chosenInstance.domain, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,263 @@
|
||||
package org.joinmastodon.android.fragments.onboarding;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Outline;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewOutlineProvider;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.RadioButton;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toolbar;
|
||||
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.api.requests.catalog.GetCatalogInstances;
|
||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||
import org.joinmastodon.android.model.Instance;
|
||||
import org.joinmastodon.android.model.catalog.CatalogInstance;
|
||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import me.grishka.appkit.api.Callback;
|
||||
import me.grishka.appkit.api.ErrorResponse;
|
||||
import me.grishka.appkit.utils.BindableViewHolder;
|
||||
import me.grishka.appkit.utils.MergeRecyclerAdapter;
|
||||
import me.grishka.appkit.utils.SingleViewRecyclerAdapter;
|
||||
import me.grishka.appkit.utils.V;
|
||||
import me.grishka.appkit.views.UsableRecyclerView;
|
||||
|
||||
public class InstanceChooserLoginFragment extends InstanceCatalogFragment{
|
||||
private View headerView;
|
||||
private boolean loadedAutocomplete;
|
||||
private ImageButton clearBtn;
|
||||
|
||||
public InstanceChooserLoginFragment(){
|
||||
super(R.layout.fragment_login, 10);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState){
|
||||
super.onCreate(savedInstanceState);
|
||||
dataLoaded();
|
||||
setTitle(R.string.login_title);
|
||||
if(!loadedAutocomplete){
|
||||
loadAutocompleteServers();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void proceedWithAuthOrSignup(Instance instance){
|
||||
AccountSessionManager.getInstance().authenticate(getActivity(), instance);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateFilteredList(){
|
||||
ArrayList<CatalogInstance> prevData=new ArrayList<>(filteredData);
|
||||
filteredData.clear();
|
||||
if(currentSearchQuery.length()>0){
|
||||
boolean foundExactMatch=false;
|
||||
for(CatalogInstance inst:data){
|
||||
if(inst.normalizedDomain.contains(currentSearchQuery)){
|
||||
filteredData.add(inst);
|
||||
if(inst.normalizedDomain.equals(currentSearchQuery))
|
||||
foundExactMatch=true;
|
||||
}
|
||||
}
|
||||
if(!foundExactMatch)
|
||||
filteredData.add(0, fakeInstance);
|
||||
}
|
||||
UiUtils.updateList(prevData, filteredData, list, adapter, Objects::equals);
|
||||
for(int i=0;i<list.getChildCount();i++){
|
||||
list.getChildAt(i).invalidateOutline();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doLoadData(int offset, int count){
|
||||
|
||||
}
|
||||
|
||||
private void loadAutocompleteServers(){
|
||||
loadedAutocomplete=true;
|
||||
new GetCatalogInstances(null, null)
|
||||
.setCallback(new Callback<>(){
|
||||
@Override
|
||||
public void onSuccess(List<CatalogInstance> result){
|
||||
data.clear();
|
||||
data.addAll(sortInstances(result));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(ErrorResponse error){
|
||||
|
||||
}
|
||||
})
|
||||
.execNoAuth("");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onUpdateToolbar(){
|
||||
super.onUpdateToolbar();
|
||||
Toolbar toolbar=getToolbar();
|
||||
toolbar.setElevation(0);
|
||||
toolbar.setBackground(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RecyclerView.Adapter getAdapter(){
|
||||
headerView=getActivity().getLayoutInflater().inflate(R.layout.header_onboarding_login, list, false);
|
||||
clearBtn=headerView.findViewById(R.id.search_clear);
|
||||
searchEdit=headerView.findViewById(R.id.search_edit);
|
||||
searchEdit.setOnEditorActionListener(this::onSearchEnterPressed);
|
||||
searchEdit.addTextChangedListener(new TextWatcher(){
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after){
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count){
|
||||
searchEdit.removeCallbacks(searchDebouncer);
|
||||
searchEdit.postDelayed(searchDebouncer, 300);
|
||||
|
||||
if(s.length()>0){
|
||||
fakeInstance.domain=fakeInstance.normalizedDomain=s.toString();
|
||||
fakeInstance.description=getString(R.string.loading_instance);
|
||||
if(filteredData.size()>0 && filteredData.get(0)==fakeInstance){
|
||||
if(list.findViewHolderForAdapterPosition(1) instanceof InstanceViewHolder ivh){
|
||||
ivh.rebind();
|
||||
}
|
||||
}
|
||||
if(filteredData.isEmpty()){
|
||||
filteredData.add(fakeInstance);
|
||||
adapter.notifyItemInserted(0);
|
||||
}
|
||||
clearBtn.setVisibility(View.VISIBLE);
|
||||
}else{
|
||||
clearBtn.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s){
|
||||
}
|
||||
});
|
||||
clearBtn.setOnClickListener(v->searchEdit.setText(""));
|
||||
|
||||
mergeAdapter=new MergeRecyclerAdapter();
|
||||
mergeAdapter.addAdapter(new SingleViewRecyclerAdapter(headerView));
|
||||
mergeAdapter.addAdapter(adapter=new InstancesAdapter());
|
||||
return mergeAdapter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState){
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
setStatusBarColor(UiUtils.getThemeColor(getActivity(), R.attr.colorM3Background));
|
||||
|
||||
list.addItemDecoration(new RecyclerView.ItemDecoration(){
|
||||
@Override
|
||||
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state){
|
||||
if(parent.getChildViewHolder(view) instanceof InstanceViewHolder){
|
||||
outRect.left=outRect.right=V.dp(16);
|
||||
}
|
||||
}
|
||||
});
|
||||
((UsableRecyclerView)list).setDrawSelectorOnTop(true);
|
||||
}
|
||||
|
||||
private class InstancesAdapter extends UsableRecyclerView.Adapter<InstanceViewHolder>{
|
||||
public InstancesAdapter(){
|
||||
super(imgLoader);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public InstanceViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
|
||||
return new InstanceViewHolder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(InstanceViewHolder holder, int position){
|
||||
holder.bind(filteredData.get(position));
|
||||
super.onBindViewHolder(holder, position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount(){
|
||||
return filteredData.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position){
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
private class InstanceViewHolder extends BindableViewHolder<CatalogInstance> implements UsableRecyclerView.Clickable{
|
||||
private final TextView title, description;
|
||||
private final RadioButton radioButton;
|
||||
|
||||
public InstanceViewHolder(){
|
||||
super(getActivity(), R.layout.item_instance_login, list);
|
||||
title=findViewById(R.id.title);
|
||||
description=findViewById(R.id.description);
|
||||
radioButton=findViewById(R.id.radiobtn);
|
||||
radioButton.setMinWidth(0);
|
||||
radioButton.setMinHeight(0);
|
||||
|
||||
itemView.setOutlineProvider(new ViewOutlineProvider(){
|
||||
@Override
|
||||
public void getOutline(View view, Outline outline){
|
||||
outline.setRoundRect(0, getAbsoluteAdapterPosition()==1 ? 0 : V.dp(-4), view.getWidth(), view.getHeight()+(getAbsoluteAdapterPosition()==filteredData.size() ? 0 : V.dp(4)), V.dp(4));
|
||||
}
|
||||
});
|
||||
itemView.setClipToOutline(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBind(CatalogInstance item){
|
||||
title.setText(item.normalizedDomain);
|
||||
description.setText(item.description);
|
||||
radioButton.setChecked(chosenInstance==item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(){
|
||||
if(chosenInstance==item)
|
||||
return;
|
||||
if(chosenInstance!=null){
|
||||
int idx=filteredData.indexOf(chosenInstance);
|
||||
if(idx!=-1){
|
||||
boolean found=false;
|
||||
for(int i=0;i<list.getChildCount();i++){
|
||||
RecyclerView.ViewHolder holder=list.getChildViewHolder(list.getChildAt(i));
|
||||
if(holder.getAbsoluteAdapterPosition()==mergeAdapter.getPositionForAdapter(adapter)+idx && holder instanceof InstanceViewHolder ivh){
|
||||
ivh.radioButton.setChecked(false);
|
||||
found=true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!found)
|
||||
adapter.notifyItemChanged(idx);
|
||||
}
|
||||
}
|
||||
radioButton.setChecked(true);
|
||||
if(chosenInstance==null)
|
||||
nextButton.setEnabled(true);
|
||||
chosenInstance=item;
|
||||
loadInstanceInfo(chosenInstance.domain, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,267 @@
|
||||
package org.joinmastodon.android.fragments.onboarding;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.TextUtils;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.RadioButton;
|
||||
import android.widget.Space;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toolbar;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.DiffUtil;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||
import org.joinmastodon.android.model.Instance;
|
||||
import org.joinmastodon.android.model.catalog.CatalogInstance;
|
||||
import org.joinmastodon.android.ui.BetterItemAnimator;
|
||||
import org.joinmastodon.android.ui.DividerItemDecoration;
|
||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import me.grishka.appkit.FragmentStackActivity;
|
||||
import me.grishka.appkit.Nav;
|
||||
import me.grishka.appkit.api.PaginatedList;
|
||||
import me.grishka.appkit.utils.BindableViewHolder;
|
||||
import me.grishka.appkit.utils.MergeRecyclerAdapter;
|
||||
import me.grishka.appkit.utils.SingleViewRecyclerAdapter;
|
||||
import me.grishka.appkit.utils.V;
|
||||
import me.grishka.appkit.views.UsableRecyclerView;
|
||||
|
||||
public class MegalodonLoginFragment extends InstanceCatalogFragment {
|
||||
private View headerView;
|
||||
|
||||
public MegalodonLoginFragment() {
|
||||
super(R.layout.fragment_megalodon_welcome, 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context){
|
||||
super.onAttach(context);
|
||||
setRefreshEnabled(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState){
|
||||
super.onCreate(savedInstanceState);
|
||||
dataLoaded();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onUpdateToolbar(){
|
||||
super.onUpdateToolbar();
|
||||
|
||||
if (!canGoBack()) {
|
||||
ImageView toolbarLogo=new ImageView(getActivity());
|
||||
toolbarLogo.setScaleType(ImageView.ScaleType.CENTER);
|
||||
toolbarLogo.setImageResource(R.drawable.logo);
|
||||
toolbarLogo.setImageTintList(ColorStateList.valueOf(UiUtils.getThemeColor(getActivity(), android.R.attr.textColorPrimary)));
|
||||
|
||||
FrameLayout logoWrap=new FrameLayout(getActivity());
|
||||
FrameLayout.LayoutParams logoParams=new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER);
|
||||
logoParams.setMargins(0, V.dp(2), 0, 0);
|
||||
logoWrap.addView(toolbarLogo, logoParams);
|
||||
|
||||
getToolbar().addView(logoWrap, new Toolbar.LayoutParams(Gravity.CENTER));
|
||||
} else {
|
||||
setTitle(R.string.add_account);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void proceedWithAuthOrSignup(Instance instance) {
|
||||
AccountSessionManager.getInstance().authenticate(getActivity(), instance);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateFilteredList(){
|
||||
boolean addFakeInstance = currentSearchQuery.length()>0 && currentSearchQuery.matches("^\\S+\\.[^\\.]+$");
|
||||
if(addFakeInstance){
|
||||
fakeInstance.domain=fakeInstance.normalizedDomain=currentSearchQuery;
|
||||
fakeInstance.description=getString(R.string.loading_instance);
|
||||
if(filteredData.size()>0 && filteredData.get(0)==fakeInstance){
|
||||
if(list.findViewHolderForAdapterPosition(1) instanceof InstanceViewHolder ivh){
|
||||
ivh.rebind();
|
||||
}
|
||||
}
|
||||
if(filteredData.isEmpty()){
|
||||
filteredData.add(fakeInstance);
|
||||
adapter.notifyItemInserted(0);
|
||||
}
|
||||
}
|
||||
ArrayList<CatalogInstance> prevData=new ArrayList<>(filteredData);
|
||||
filteredData.clear();
|
||||
if(currentSearchQuery.length()>0){
|
||||
boolean foundExactMatch=false;
|
||||
for(CatalogInstance inst:data){
|
||||
if(inst.normalizedDomain.contains(currentSearchQuery)){
|
||||
filteredData.add(inst);
|
||||
if(inst.normalizedDomain.equals(currentSearchQuery))
|
||||
foundExactMatch=true;
|
||||
}
|
||||
}
|
||||
if(!foundExactMatch && addFakeInstance) {
|
||||
filteredData.add(0, fakeInstance);
|
||||
adapter.notifyItemChanged(0);
|
||||
}
|
||||
}
|
||||
UiUtils.updateList(prevData, filteredData, list, adapter, Objects::equals);
|
||||
for(int i=0;i<list.getChildCount();i++){
|
||||
list.getChildAt(i).invalidateOutline();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
view.setBackgroundColor(UiUtils.getThemeColor(getActivity(), R.attr.colorWindowBackground));
|
||||
list.setItemAnimator(new BetterItemAnimator());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doLoadData(int offset, int count) {}
|
||||
|
||||
@Override
|
||||
protected RecyclerView.Adapter getAdapter(){
|
||||
headerView=getActivity().getLayoutInflater().inflate(R.layout.header_megalodon_welcome, list, false);
|
||||
searchEdit=headerView.findViewById(R.id.search_edit);
|
||||
searchEdit.setOnEditorActionListener(this::onSearchEnterPressed);
|
||||
|
||||
headerView.findViewById(R.id.more).setVisibility(View.GONE);
|
||||
headerView.findViewById(R.id.visibility).setVisibility(View.GONE);
|
||||
((TextView) headerView.findViewById(R.id.username)).setText("@megalodon");
|
||||
((TextView) headerView.findViewById(R.id.name)).setText(R.string.sk_app_name);
|
||||
((TextView) headerView.findViewById(R.id.timestamp)).setText(R.string.time_now);
|
||||
((ImageView) headerView.findViewById(R.id.avatar)).setImageDrawable(getActivity().getDrawable(R.mipmap.ic_launcher));
|
||||
((FragmentStackActivity) getActivity()).invalidateSystemBarColors(this);
|
||||
|
||||
searchEdit.addTextChangedListener(new TextWatcher(){
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after){}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count){
|
||||
nextButton.setEnabled(false);
|
||||
chosenInstance = null;
|
||||
searchEdit.removeCallbacks(searchDebouncer);
|
||||
searchEdit.postDelayed(searchDebouncer, 300);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s){}
|
||||
});
|
||||
|
||||
mergeAdapter=new MergeRecyclerAdapter();
|
||||
mergeAdapter.addAdapter(new SingleViewRecyclerAdapter(headerView));
|
||||
mergeAdapter.addAdapter(adapter=new InstancesAdapter());
|
||||
View spacer = new Space(getActivity());
|
||||
spacer.setMinimumHeight(V.dp(8));
|
||||
mergeAdapter.addAdapter(new SingleViewRecyclerAdapter(spacer));
|
||||
return mergeAdapter;
|
||||
}
|
||||
|
||||
private class InstancesAdapter extends UsableRecyclerView.Adapter<InstanceViewHolder>{
|
||||
public InstancesAdapter(){
|
||||
super(imgLoader);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public InstanceViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
|
||||
return new InstanceViewHolder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(InstanceViewHolder holder, int position){
|
||||
holder.bind(filteredData.get(position));
|
||||
chosenInstance = filteredData.get(position);
|
||||
if (chosenInstance != fakeInstance) nextButton.setEnabled(true);
|
||||
super.onBindViewHolder(holder, position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount(){
|
||||
return filteredData.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position){
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
private class InstanceViewHolder extends BindableViewHolder<CatalogInstance> implements UsableRecyclerView.Clickable{
|
||||
private final TextView title, description, userCount, lang;
|
||||
private final RadioButton radioButton;
|
||||
|
||||
public InstanceViewHolder(){
|
||||
super(getActivity(), R.layout.item_megalodon_instance, list);
|
||||
|
||||
// itemView.setPadding(V.dp(16), V.dp(16), V.dp(16), V.dp(16));
|
||||
// TypedValue value = new TypedValue();
|
||||
// getActivity().getTheme().resolveAttribute(android.R.attr.selectableItemBackground, value, true);
|
||||
// itemView.setBackground(getActivity().getTheme().getDrawable(R.drawable.bg_search_field));
|
||||
title=findViewById(R.id.title);
|
||||
description=findViewById(R.id.description);
|
||||
userCount=findViewById(R.id.user_count);
|
||||
lang=findViewById(R.id.lang);
|
||||
radioButton=findViewById(R.id.radiobtn);
|
||||
if(Build.VERSION.SDK_INT<Build.VERSION_CODES.N){
|
||||
UiUtils.fixCompoundDrawableTintOnAndroid6(userCount);
|
||||
UiUtils.fixCompoundDrawableTintOnAndroid6(lang);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBind(CatalogInstance item){
|
||||
title.setText(item.normalizedDomain);
|
||||
description.setText(item.description);
|
||||
if (item == fakeInstance) {
|
||||
userCount.setVisibility(View.GONE);
|
||||
lang.setVisibility(View.GONE);
|
||||
} else {
|
||||
userCount.setVisibility(View.VISIBLE);
|
||||
lang.setVisibility(View.VISIBLE);
|
||||
userCount.setText(UiUtils.abbreviateNumber(item.totalUsers));
|
||||
lang.setText(item.language.toUpperCase());
|
||||
}
|
||||
radioButton.setChecked(chosenInstance==item);
|
||||
radioButton.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(){
|
||||
if(chosenInstance!=null){
|
||||
int idx=filteredData.indexOf(chosenInstance);
|
||||
if(idx!=-1){
|
||||
RecyclerView.ViewHolder holder=list.findViewHolderForAdapterPosition(mergeAdapter.getPositionForAdapter(adapter)+idx);
|
||||
if(holder instanceof InstanceViewHolder ivh){
|
||||
ivh.radioButton.setChecked(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
radioButton.setChecked(true);
|
||||
if(chosenInstance==null)
|
||||
nextButton.setEnabled(true);
|
||||
chosenInstance=item;
|
||||
loadInstanceInfo(chosenInstance.domain, false);
|
||||
onNextClick(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -82,6 +82,8 @@ public class Instance extends BaseModel{
|
||||
// non-standard field in some Mastodon forks
|
||||
public int maxTootChars;
|
||||
|
||||
public V2 v2;
|
||||
|
||||
@Override
|
||||
public void postprocess() throws ObjectValidationException{
|
||||
super.postprocess();
|
||||
@@ -176,4 +178,19 @@ public class Instance extends BaseModel{
|
||||
public int minExpiration;
|
||||
public int maxExpiration;
|
||||
}
|
||||
|
||||
@Parcel
|
||||
public static class V2 extends BaseModel {
|
||||
public V2.Configuration configuration;
|
||||
|
||||
@Parcel
|
||||
public static class Configuration {
|
||||
public TranslationConfiguration translation;
|
||||
}
|
||||
|
||||
@Parcel
|
||||
public static class TranslationConfiguration{
|
||||
public boolean enabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,9 @@ public class PushNotification extends BaseModel{
|
||||
@SerializedName("follow")
|
||||
FOLLOW(R.string.notification_type_follow),
|
||||
@SerializedName("poll")
|
||||
POLL(R.string.notification_type_poll);
|
||||
POLL(R.string.notification_type_poll),
|
||||
@SerializedName("status")
|
||||
STATUS(R.string.sk_notification_type_status);
|
||||
|
||||
@StringRes
|
||||
public final int localizedName;
|
||||
|
||||
@@ -43,10 +43,11 @@ public class PushSubscription extends BaseModel implements Cloneable{
|
||||
public boolean reblog;
|
||||
public boolean mention;
|
||||
public boolean poll;
|
||||
public boolean status;
|
||||
|
||||
public static Alerts ofAll(){
|
||||
Alerts alerts=new Alerts();
|
||||
alerts.follow=alerts.favourite=alerts.reblog=alerts.mention=alerts.poll=true;
|
||||
alerts.follow=alerts.favourite=alerts.reblog=alerts.mention=alerts.poll=alerts.status=true;
|
||||
return alerts;
|
||||
}
|
||||
|
||||
@@ -58,6 +59,7 @@ public class PushSubscription extends BaseModel implements Cloneable{
|
||||
", reblog="+reblog+
|
||||
", mention="+mention+
|
||||
", poll="+poll+
|
||||
", status="+status+
|
||||
'}';
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package org.joinmastodon.android.model;
|
||||
|
||||
public class TranslatedStatus extends BaseModel {
|
||||
public String content;
|
||||
public String detectedSourceLanguage;
|
||||
public String provider;
|
||||
}
|
||||
@@ -2,14 +2,12 @@ package org.joinmastodon.android.ui;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.graphics.drawable.Animatable;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowInsets;
|
||||
@@ -25,8 +23,7 @@ import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.api.requests.oauth.RevokeOauthToken;
|
||||
import org.joinmastodon.android.api.session.AccountSession;
|
||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||
import org.joinmastodon.android.fragments.SplashFragment;
|
||||
import org.joinmastodon.android.model.Account;
|
||||
import org.joinmastodon.android.fragments.onboarding.MegalodonLoginFragment;
|
||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||
|
||||
import java.util.List;
|
||||
@@ -80,7 +77,7 @@ public class AccountSwitcherSheet extends BottomSheet{
|
||||
holder.avatar.setImageResource(R.drawable.ic_fluent_add_circle_24_filled);
|
||||
holder.avatar.setImageTintList(ColorStateList.valueOf(UiUtils.getThemeColor(activity, android.R.attr.textColorPrimary)));
|
||||
adapter.addAdapter(new ClickableSingleViewRecyclerAdapter(holder.itemView, ()->{
|
||||
Nav.go(activity, SplashFragment.class, null);
|
||||
Nav.go(activity, MegalodonLoginFragment.class, null);
|
||||
dismiss();
|
||||
}));
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
@@ -154,11 +155,16 @@ public class ComposeAutocompleteViewController{
|
||||
}else if(mode==Mode.EMOJIS){
|
||||
String _text=text.substring(1); // remove ':'
|
||||
List<WrappedEmoji> oldList=emojis;
|
||||
emojis=AccountSessionManager.getInstance()
|
||||
List<Emoji> allEmojis = AccountSessionManager.getInstance()
|
||||
.getCustomEmojis(AccountSessionManager.getInstance().getAccount(accountID).domain)
|
||||
.stream()
|
||||
.flatMap(ec->ec.emojis.stream())
|
||||
.filter(e->e.visibleInPicker && e.shortcode.startsWith(_text))
|
||||
.filter(e->e.visibleInPicker)
|
||||
.collect(Collectors.toList());
|
||||
List<Emoji> startsWithSearch = allEmojis.stream().filter(e -> e.shortcode.toLowerCase().startsWith(_text.toLowerCase())).collect(Collectors.toList());
|
||||
emojis=Stream.concat(startsWithSearch.stream(), allEmojis.stream()
|
||||
.filter(e -> !startsWithSearch.contains(e))
|
||||
.filter(e -> e.shortcode.toLowerCase().contains(_text.toLowerCase())))
|
||||
.map(WrappedEmoji::new)
|
||||
.collect(Collectors.toList());
|
||||
UiUtils.updateList(oldList, emojis, list, emojisAdapter, (e1, e2)->e1.emoji.shortcode.equals(e2.emoji.shortcode));
|
||||
|
||||
@@ -45,7 +45,7 @@ public class ImageDescriptionSheet extends BottomSheet{
|
||||
}
|
||||
|
||||
TextView heading=new TextView(activity);
|
||||
heading.setText(R.string.image_description);
|
||||
heading.setText(R.string.sk_image_description);
|
||||
heading.setAllCaps(true);
|
||||
heading.setTypeface(null, Typeface.BOLD);
|
||||
heading.setPadding(0, V.dp(24), 0, V.dp(8));
|
||||
|
||||
@@ -147,7 +147,7 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
|
||||
if(id==R.id.favorite_btn)
|
||||
return R.string.button_favorite;
|
||||
if(id==R.id.bookmark_btn)
|
||||
return R.string.button_bookmark;
|
||||
return R.string.add_bookmark;
|
||||
if(id==R.id.share_btn)
|
||||
return R.string.button_share;
|
||||
return 0;
|
||||
|
||||
@@ -6,6 +6,7 @@ import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.joinmastodon.android.GlobalUserPreferences;
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
||||
import org.joinmastodon.android.model.Poll;
|
||||
@@ -44,7 +45,7 @@ public class PollFooterStatusDisplayItem extends StatusDisplayItem{
|
||||
text+=" · "+item.parentFragment.getString(R.string.poll_closed);
|
||||
}
|
||||
this.text.setText(text);
|
||||
button.setVisibility(item.poll.isExpired() || item.poll.voted || !item.poll.multiple ? View.GONE : View.VISIBLE);
|
||||
button.setVisibility(item.poll.isExpired() || item.poll.voted || (!item.poll.multiple && !GlobalUserPreferences.voteButtonForSingleChoice) ? View.GONE : View.VISIBLE);
|
||||
button.setEnabled(item.poll.selectedOptions!=null && !item.poll.selectedOptions.isEmpty());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import android.graphics.drawable.Animatable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.joinmastodon.android.R;
|
||||
@@ -60,7 +61,8 @@ public class PollOptionStatusDisplayItem extends StatusDisplayItem{
|
||||
|
||||
public static class Holder extends StatusDisplayItem.Holder<PollOptionStatusDisplayItem> implements ImageLoaderViewHolder{
|
||||
private final TextView text, percent;
|
||||
private final View icon, button;
|
||||
private final View button;
|
||||
private final ImageView icon;
|
||||
private final Drawable progressBg;
|
||||
|
||||
public Holder(Activity activity, ViewGroup parent){
|
||||
@@ -76,17 +78,23 @@ public class PollOptionStatusDisplayItem extends StatusDisplayItem{
|
||||
@Override
|
||||
public void onBind(PollOptionStatusDisplayItem item){
|
||||
text.setText(item.text);
|
||||
icon.setVisibility(item.showResults ? View.GONE : View.VISIBLE);
|
||||
percent.setVisibility(item.showResults ? View.VISIBLE : View.GONE);
|
||||
itemView.setClickable(!item.showResults);
|
||||
icon.setImageDrawable(itemView.getContext().getDrawable(item.poll.multiple ?
|
||||
item.showResults ? R.drawable.ic_poll_checkbox_regular_selector : R.drawable.ic_poll_checkbox_filled_selector :
|
||||
item.showResults ? R.drawable.ic_poll_option_button : R.drawable.ic_fluent_radio_button_24_selector
|
||||
));
|
||||
if(item.showResults){
|
||||
progressBg.setLevel(Math.round(10000f*item.votesFraction));
|
||||
button.setBackground(progressBg);
|
||||
itemView.setSelected(item.isMostVoted);
|
||||
icon.setSelected(item.poll.ownVotes.contains(item.poll.options.indexOf(item.option)));
|
||||
icon.setVisibility(item.poll.voted && item.poll.ownVotes.isEmpty() ? View.GONE : View.VISIBLE);
|
||||
percent.setText(String.format(Locale.getDefault(), "%d%%", Math.round(item.votesFraction*100f)));
|
||||
}else{
|
||||
itemView.setSelected(item.poll.selectedOptions!=null && item.poll.selectedOptions.contains(item.option));
|
||||
button.setBackgroundResource(R.drawable.bg_poll_option_clickable);
|
||||
icon.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
package org.joinmastodon.android.ui.displayitems;
|
||||
|
||||
import static org.joinmastodon.android.MastodonApp.context;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.util.TypedValue;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
@@ -38,6 +41,8 @@ public class ReblogOrReplyLineStatusDisplayItem extends StatusDisplayItem{
|
||||
emojiHelper.setText(ssb);
|
||||
this.icon=icon;
|
||||
this.handleClick=handleClick;
|
||||
TypedValue outValue = new TypedValue();
|
||||
context.getTheme().resolveAttribute(android.R.attr.selectableItemBackground, outValue, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -67,6 +72,8 @@ public class ReblogOrReplyLineStatusDisplayItem extends StatusDisplayItem{
|
||||
text.setText(item.text);
|
||||
text.setCompoundDrawablesRelativeWithIntrinsicBounds(item.icon, 0, 0, 0);
|
||||
if(item.handleClick!=null) text.setOnClickListener(item.handleClick);
|
||||
text.setEnabled(!item.inset);
|
||||
text.setClickable(!item.inset);
|
||||
if(Build.VERSION.SDK_INT<Build.VERSION_CODES.N)
|
||||
UiUtils.fixCompoundDrawableTintOnAndroid6(text);
|
||||
}
|
||||
|
||||
@@ -9,16 +9,25 @@ import android.util.TypedValue;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.api.requests.statuses.TranslateStatus;
|
||||
import org.joinmastodon.android.api.session.AccountSession;
|
||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
||||
import org.joinmastodon.android.model.Instance;
|
||||
import org.joinmastodon.android.model.Status;
|
||||
import org.joinmastodon.android.ui.drawables.SpoilerStripesDrawable;
|
||||
import org.joinmastodon.android.model.StatusPrivacy;
|
||||
import org.joinmastodon.android.model.TranslatedStatus;
|
||||
import org.joinmastodon.android.ui.text.HtmlParser;
|
||||
import org.joinmastodon.android.ui.utils.CustomEmojiHelper;
|
||||
import org.joinmastodon.android.ui.views.LinkedTextView;
|
||||
|
||||
import me.grishka.appkit.api.Callback;
|
||||
import me.grishka.appkit.api.ErrorResponse;
|
||||
import me.grishka.appkit.imageloader.ImageLoaderViewHolder;
|
||||
import me.grishka.appkit.imageloader.MovieDrawable;
|
||||
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
|
||||
@@ -30,6 +39,12 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
|
||||
private CharSequence parsedSpoilerText;
|
||||
public boolean textSelectable;
|
||||
public final Status status;
|
||||
public boolean translated = false;
|
||||
public TranslatedStatus translation = null;
|
||||
|
||||
private AccountSession session;
|
||||
private Instance instanceInfo;
|
||||
private boolean translateEnabled;
|
||||
|
||||
public TextStatusDisplayItem(String parentID, CharSequence text, BaseStatusListFragment parentFragment, Status status){
|
||||
super(parentID, parentFragment);
|
||||
@@ -41,6 +56,9 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
|
||||
spoilerEmojiHelper=new CustomEmojiHelper();
|
||||
spoilerEmojiHelper.setText(parsedSpoilerText);
|
||||
}
|
||||
session = AccountSessionManager.getInstance().getAccount(parentFragment.getAccountID());
|
||||
instanceInfo = AccountSessionManager.getInstance().getInstanceInfo(session.domain);
|
||||
translateEnabled = instanceInfo.v2 != null && instanceInfo.v2.configuration.translation != null && instanceInfo.v2.configuration.translation.enabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -65,9 +83,10 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
|
||||
public static class Holder extends StatusDisplayItem.Holder<TextStatusDisplayItem> implements ImageLoaderViewHolder{
|
||||
private final LinkedTextView text;
|
||||
private final LinearLayout spoilerHeader;
|
||||
private final TextView spoilerTitle, spoilerTitleInline;
|
||||
private final View spoilerOverlay, borderTop, borderBottom;
|
||||
private final TextView spoilerTitle, spoilerTitleInline, translateInfo;
|
||||
private final View spoilerOverlay, borderTop, borderBottom, textWrap, translateWrap, translateProgress;
|
||||
private final Drawable backgroundColor, borderColor;
|
||||
private final Button translateButton;
|
||||
|
||||
public Holder(Activity activity, ViewGroup parent){
|
||||
super(activity, R.layout.display_item_text, parent);
|
||||
@@ -78,6 +97,11 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
|
||||
spoilerOverlay=findViewById(R.id.spoiler_overlay);
|
||||
borderTop=findViewById(R.id.border_top);
|
||||
borderBottom=findViewById(R.id.border_bottom);
|
||||
textWrap=findViewById(R.id.text_wrap);
|
||||
translateWrap=findViewById(R.id.translate_wrap);
|
||||
translateButton=findViewById(R.id.translate_btn);
|
||||
translateInfo=findViewById(R.id.translate_info);
|
||||
translateProgress=findViewById(R.id.translate_progress);
|
||||
itemView.setOnClickListener(v->item.parentFragment.onRevealSpoilerClick(this));
|
||||
|
||||
TypedValue outValue=new TypedValue();
|
||||
@@ -91,7 +115,9 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
|
||||
|
||||
@Override
|
||||
public void onBind(TextStatusDisplayItem item){
|
||||
text.setText(item.text);
|
||||
text.setText(item.translated
|
||||
? HtmlParser.parse(item.translation.content, item.status.emojis, item.status.mentions, item.status.tags, item.parentFragment.getAccountID())
|
||||
: item.text);
|
||||
text.setTextIsSelectable(item.textSelectable);
|
||||
spoilerTitleInline.setTextIsSelectable(item.textSelectable);
|
||||
text.setInvalidateOnEveryFrame(false);
|
||||
@@ -105,20 +131,53 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
|
||||
if(item.status.spoilerRevealed){
|
||||
spoilerOverlay.setVisibility(View.GONE);
|
||||
spoilerHeader.setVisibility(View.VISIBLE);
|
||||
text.setVisibility(View.VISIBLE);
|
||||
textWrap.setVisibility(View.VISIBLE);
|
||||
itemView.setClickable(false);
|
||||
}else{
|
||||
spoilerOverlay.setVisibility(View.VISIBLE);
|
||||
spoilerHeader.setVisibility(View.GONE);
|
||||
text.setVisibility(View.GONE);
|
||||
textWrap.setVisibility(View.GONE);
|
||||
itemView.setClickable(true);
|
||||
}
|
||||
}else{
|
||||
spoilerOverlay.setVisibility(View.GONE);
|
||||
spoilerHeader.setVisibility(View.GONE);
|
||||
text.setVisibility(View.VISIBLE);
|
||||
textWrap.setVisibility(View.VISIBLE);
|
||||
itemView.setClickable(false);
|
||||
}
|
||||
|
||||
translateWrap.setVisibility(item.textSelectable && item.translateEnabled &&
|
||||
!item.status.visibility.isLessVisibleThan(StatusPrivacy.UNLISTED) &&
|
||||
(item.session.preferences == null || !item.status.language.equalsIgnoreCase(item.session.preferences.postingDefaultLanguage))
|
||||
? View.VISIBLE : View.GONE);
|
||||
translateButton.setText(item.translated ? R.string.sk_translate_show_original : R.string.sk_translate_post);
|
||||
translateInfo.setText(item.translated ? itemView.getResources().getString(R.string.sk_translated_using, item.translation.provider) : "");
|
||||
translateButton.setOnClickListener(v->{
|
||||
if (item.translation == null) {
|
||||
translateProgress.setVisibility(View.VISIBLE);
|
||||
translateButton.setClickable(false);
|
||||
new TranslateStatus(item.status.id).setCallback(new Callback<>() {
|
||||
@Override
|
||||
public void onSuccess(TranslatedStatus translatedStatus) {
|
||||
item.translation = translatedStatus;
|
||||
item.translated = true;
|
||||
translateProgress.setVisibility(View.GONE);
|
||||
translateButton.setClickable(true);
|
||||
rebind();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(ErrorResponse error) {
|
||||
translateProgress.setVisibility(View.GONE);
|
||||
translateButton.setClickable(true);
|
||||
error.showToast(itemView.getContext());
|
||||
}
|
||||
}).exec(item.parentFragment.getAccountID());
|
||||
} else {
|
||||
item.translated = !item.translated;
|
||||
rebind();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -178,7 +178,7 @@ public class PhotoViewer implements ZoomPanView.Listener{
|
||||
toolbar=uiOverlay.findViewById(R.id.toolbar);
|
||||
toolbar.setNavigationOnClickListener(v->onStartSwipeToDismissTransition(0));
|
||||
imageDescriptionButton = toolbar.getMenu()
|
||||
.add(R.string.image_description)
|
||||
.add(R.string.sk_image_description)
|
||||
.setIcon(R.drawable.ic_fluent_image_alt_text_24_regular)
|
||||
.setVisible(attachments.get(pager.getCurrentItem()).description != null
|
||||
&& !attachments.get(pager.getCurrentItem()).description.isEmpty())
|
||||
|
||||
@@ -1,12 +1,29 @@
|
||||
package org.joinmastodon.android.ui.text;
|
||||
|
||||
import android.graphics.Typeface;
|
||||
import android.graphics.fonts.FontFamily;
|
||||
import android.graphics.fonts.FontStyle;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.Spanned;
|
||||
import android.text.TextUtils;
|
||||
import android.text.style.BackgroundColorSpan;
|
||||
import android.text.style.BulletSpan;
|
||||
import android.text.style.ForegroundColorSpan;
|
||||
import android.text.style.LeadingMarginSpan;
|
||||
import android.text.style.RelativeSizeSpan;
|
||||
import android.text.style.StrikethroughSpan;
|
||||
import android.text.style.StyleSpan;
|
||||
import android.text.style.SubscriptSpan;
|
||||
import android.text.style.SuperscriptSpan;
|
||||
import android.text.style.TypefaceSpan;
|
||||
import android.text.style.UnderlineSpan;
|
||||
import android.util.TypedValue;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.twitter.twittertext.Regex;
|
||||
|
||||
import org.joinmastodon.android.MastodonApp;
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.model.Emoji;
|
||||
import org.joinmastodon.android.model.Hashtag;
|
||||
import org.joinmastodon.android.model.Mention;
|
||||
@@ -15,11 +32,11 @@ import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Element;
|
||||
import org.jsoup.nodes.Node;
|
||||
import org.jsoup.nodes.TextNode;
|
||||
import org.jsoup.safety.Cleaner;
|
||||
import org.jsoup.safety.Safelist;
|
||||
import org.jsoup.select.NodeVisitor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
@@ -29,6 +46,8 @@ import java.util.stream.Collectors;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import me.grishka.appkit.utils.V;
|
||||
|
||||
public class HtmlParser{
|
||||
private static final String TAG="HtmlParser";
|
||||
private static final String VALID_URL_PATTERN_STRING =
|
||||
@@ -67,11 +86,17 @@ public class HtmlParser{
|
||||
public Object span;
|
||||
public int start;
|
||||
public Element element;
|
||||
public boolean more;
|
||||
|
||||
public SpanInfo(Object span, int start, Element element){
|
||||
this(span, start, element, false);
|
||||
}
|
||||
|
||||
public SpanInfo(Object span, int start, Element element, boolean more){
|
||||
this.span=span;
|
||||
this.start=start;
|
||||
this.element=element;
|
||||
this.more=more;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,24 +144,59 @@ public class HtmlParser{
|
||||
openSpans.add(new SpanInfo(new InvisibleSpan(), ssb.length(), el));
|
||||
}
|
||||
}
|
||||
case "li" -> openSpans.add(new SpanInfo(new BulletSpan(V.dp(8)), ssb.length(), el));
|
||||
case "em", "i" -> openSpans.add(new SpanInfo(new StyleSpan(Typeface.ITALIC), ssb.length(), el));
|
||||
case "h1", "h2", "h3", "h4", "h5", "h6" -> {
|
||||
// increase line height above heading (multiplying the margin)
|
||||
if (node.previousSibling()!=null) ssb.setSpan(new RelativeSizeSpan(2), ssb.length() - 1, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
if (!node.nodeName().equals("h1")) {
|
||||
openSpans.add(new SpanInfo(new StyleSpan(Typeface.BOLD), ssb.length(), el));
|
||||
}
|
||||
openSpans.add(new SpanInfo(new RelativeSizeSpan(switch(node.nodeName()) {
|
||||
case "h1" -> 1.5f;
|
||||
case "h2" -> 1.25f;
|
||||
case "h3" -> 1.125f;
|
||||
default -> 1;
|
||||
}), ssb.length(), el, !node.nodeName().equals("h1")));
|
||||
}
|
||||
case "strong", "b" -> openSpans.add(new SpanInfo(new StyleSpan(Typeface.BOLD), ssb.length(), el));
|
||||
case "u" -> openSpans.add(new SpanInfo(new UnderlineSpan(), ssb.length(), el));
|
||||
case "s", "del" -> openSpans.add(new SpanInfo(new StrikethroughSpan(), ssb.length(), el));
|
||||
case "sub", "sup" -> {
|
||||
openSpans.add(new SpanInfo(node.nodeName().equals("sub") ? new SubscriptSpan() : new SuperscriptSpan(), ssb.length(), el));
|
||||
openSpans.add(new SpanInfo(new RelativeSizeSpan(0.8f), ssb.length(), el, true));
|
||||
}
|
||||
case "code", "pre" -> openSpans.add(new SpanInfo(new TypefaceSpan("monospace"), ssb.length(), el));
|
||||
case "blockquote" -> openSpans.add(new SpanInfo(new LeadingMarginSpan.Standard(V.dp(10)), ssb.length(), el));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final static List<String> blockElements = Arrays.asList("p", "ul", "ol", "blockquote", "h1", "h2", "h3", "h4", "h5", "h6");
|
||||
|
||||
@Override
|
||||
public void tail(@NonNull Node node, int depth){
|
||||
if(node instanceof Element el){
|
||||
processOpenSpan(el);
|
||||
if("span".equals(el.nodeName()) && el.hasClass("ellipsis")){
|
||||
ssb.append("…", new DeleteWhenCopiedSpan(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
}else if("p".equals(el.nodeName())){
|
||||
if(node.nextSibling()!=null)
|
||||
ssb.append("\n\n");
|
||||
}else if(!openSpans.isEmpty()){
|
||||
SpanInfo si=openSpans.get(openSpans.size()-1);
|
||||
if(si.element==el){
|
||||
ssb.setSpan(si.span, si.start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
openSpans.remove(openSpans.size()-1);
|
||||
}
|
||||
}else if(blockElements.contains(el.nodeName()) && node.nextSibling()!=null){
|
||||
ssb.append("\n"); // line end
|
||||
ssb.append("\n", new RelativeSizeSpan(0.75f), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); // margin after block
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void processOpenSpan(Element el) {
|
||||
if(!openSpans.isEmpty()){
|
||||
SpanInfo si=openSpans.get(openSpans.size()-1);
|
||||
if(si.element==el){
|
||||
ssb.setSpan(si.span, si.start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
openSpans.remove(openSpans.size()-1);
|
||||
if(si.more) processOpenSpan(el);
|
||||
}
|
||||
if("li".equals(el.nodeName()) && el.nextSibling()!=null) {
|
||||
ssb.append('\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ public class DiscoverInfoBannerHelper{
|
||||
case TRENDING_HASHTAGS -> R.string.trending_hashtags_info_banner;
|
||||
case TRENDING_LINKS -> R.string.trending_links_info_banner;
|
||||
case LOCAL_TIMELINE -> R.string.local_timeline_info_banner;
|
||||
case FEDERATED_TIMELINE -> R.string.federated_timeline_info_banner;
|
||||
case FEDERATED_TIMELINE -> R.string.sk_federated_timeline_info_banner;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.provider.OpenableColumns;
|
||||
import android.provider.Settings;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.Spanned;
|
||||
import android.view.Menu;
|
||||
@@ -410,7 +411,7 @@ public class UiUtils{
|
||||
}
|
||||
|
||||
public static void confirmDeletePost(Activity activity, String accountID, Status status, Consumer<Status> resultCallback, boolean forRedraft){
|
||||
showConfirmationAlert(activity, forRedraft ? R.string.confirm_delete_and_redraft_title : R.string.confirm_delete_title, forRedraft ? R.string.confirm_delete_and_redraft : R.string.confirm_delete, forRedraft ? R.string.delete_and_redraft : R.string.delete, ()->{
|
||||
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)
|
||||
.setCallback(new Callback<>(){
|
||||
@Override
|
||||
@@ -432,9 +433,9 @@ public class UiUtils{
|
||||
|
||||
public static void confirmPinPost(Activity activity, String accountID, Status status, boolean pinned, Consumer<Status> resultCallback){
|
||||
showConfirmationAlert(activity,
|
||||
pinned ? R.string.confirm_pin_post_title : R.string.confirm_unpin_post_title,
|
||||
pinned ? R.string.confirm_pin_post : R.string.confirm_unpin_post,
|
||||
pinned ? R.string.pin_post : R.string.unpin_post,
|
||||
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,
|
||||
()->{
|
||||
new SetStatusPinned(status.id, pinned)
|
||||
.setCallback(new Callback<>() {
|
||||
@@ -451,7 +452,7 @@ public class UiUtils{
|
||||
error.showToast(activity);
|
||||
}
|
||||
})
|
||||
.wrapProgress(activity, pinned ? R.string.pinning : R.string.unpinning, false)
|
||||
.wrapProgress(activity, pinned ? R.string.sk_pinning : R.string.sk_unpinning, false)
|
||||
.exec(accountID);
|
||||
}
|
||||
);
|
||||
@@ -507,7 +508,7 @@ public class UiUtils{
|
||||
public void onSuccess(Relationship result) {
|
||||
resultCallback.accept(result);
|
||||
progressCallback.accept(false);
|
||||
Toast.makeText(activity, activity.getString(result.notifying ? R.string.user_post_notifications_on : R.string.user_post_notifications_off, '@'+account.username), Toast.LENGTH_SHORT).show();
|
||||
Toast.makeText(activity, activity.getString(result.notifying ? R.string.sk_user_post_notifications_on : R.string.sk_user_post_notifications_off, '@'+account.username), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -655,13 +656,71 @@ public class UiUtils{
|
||||
}
|
||||
|
||||
public static void setUserPreferredTheme(Context context){
|
||||
context.setTheme(switch(GlobalUserPreferences.theme){
|
||||
case AUTO -> GlobalUserPreferences.trueBlackTheme ? R.style.Theme_Mastodon_AutoLightDark_TrueBlack : R.style.Theme_Mastodon_AutoLightDark;
|
||||
case LIGHT -> R.style.Theme_Mastodon_Light;
|
||||
case DARK -> GlobalUserPreferences.trueBlackTheme ? R.style.Theme_Mastodon_Dark_TrueBlack : R.style.Theme_Mastodon_Dark;
|
||||
});
|
||||
}
|
||||
// boolean isDarkTheme = isDarkTheme();
|
||||
switch(GlobalUserPreferences.color){
|
||||
case PINK:
|
||||
context.setTheme(switch(GlobalUserPreferences.theme){
|
||||
case AUTO ->
|
||||
GlobalUserPreferences.trueBlackTheme ? R.style.Theme_Mastodon_AutoLightDark_TrueBlack : R.style.Theme_Mastodon_AutoLightDark;
|
||||
case LIGHT ->
|
||||
R.style.Theme_Mastodon_Light;
|
||||
case DARK ->
|
||||
GlobalUserPreferences.trueBlackTheme ? R.style.Theme_Mastodon_Dark_TrueBlack : R.style.Theme_Mastodon_Dark;
|
||||
});
|
||||
break;
|
||||
case PURPLE:
|
||||
context.setTheme(switch(GlobalUserPreferences.theme){
|
||||
case AUTO ->
|
||||
GlobalUserPreferences.trueBlackTheme ? R.style.Theme_Mastodon_AutoLightDark_TrueBlack_Original : R.style.Theme_Mastodon_AutoLightDark_Original;
|
||||
case LIGHT ->
|
||||
R.style.Theme_Mastodon_Light_Original;
|
||||
case DARK ->
|
||||
GlobalUserPreferences.trueBlackTheme ? R.style.Theme_Mastodon_Dark_TrueBlack_Original : R.style.Theme_Mastodon_Dark_Original;
|
||||
});
|
||||
break;
|
||||
case GREEN:
|
||||
context.setTheme(switch(GlobalUserPreferences.theme){
|
||||
case AUTO ->
|
||||
GlobalUserPreferences.trueBlackTheme ? R.style.Theme_Mastodon_AutoLightDark_TrueBlack_Green : R.style.Theme_Mastodon_AutoLightDark_Green;
|
||||
case LIGHT ->
|
||||
R.style.Theme_Mastodon_Light_Green;
|
||||
case DARK ->
|
||||
GlobalUserPreferences.trueBlackTheme ? R.style.Theme_Mastodon_Dark_TrueBlack_Green : R.style.Theme_Mastodon_Dark_Green;
|
||||
});
|
||||
break;
|
||||
case BLUE:
|
||||
context.setTheme(switch(GlobalUserPreferences.theme){
|
||||
case AUTO ->
|
||||
GlobalUserPreferences.trueBlackTheme ? R.style.Theme_Mastodon_AutoLightDark_TrueBlack_Blue : R.style.Theme_Mastodon_AutoLightDark_Blue;
|
||||
case LIGHT ->
|
||||
R.style.Theme_Mastodon_Light_Blue;
|
||||
case DARK ->
|
||||
GlobalUserPreferences.trueBlackTheme ? R.style.Theme_Mastodon_Dark_TrueBlack_Blue : R.style.Theme_Mastodon_Dark_Blue;
|
||||
});
|
||||
break;
|
||||
case BROWN:
|
||||
context.setTheme(switch(GlobalUserPreferences.theme){
|
||||
case AUTO ->
|
||||
GlobalUserPreferences.trueBlackTheme ? R.style.Theme_Mastodon_AutoLightDark_TrueBlack_Brown : R.style.Theme_Mastodon_AutoLightDark_Brown;
|
||||
case LIGHT ->
|
||||
R.style.Theme_Mastodon_Light_Brown;
|
||||
case DARK ->
|
||||
GlobalUserPreferences.trueBlackTheme ? R.style.Theme_Mastodon_Dark_TrueBlack_Brown : R.style.Theme_Mastodon_Dark_Brown;
|
||||
});
|
||||
break;
|
||||
case YELLOW:
|
||||
context.setTheme(switch(GlobalUserPreferences.theme){
|
||||
case AUTO ->
|
||||
GlobalUserPreferences.trueBlackTheme ? R.style.Theme_Mastodon_AutoLightDark_TrueBlack_Yellow : R.style.Theme_Mastodon_AutoLightDark_Yellow;
|
||||
case LIGHT ->
|
||||
R.style.Theme_Mastodon_Light_Yellow;
|
||||
case DARK ->
|
||||
GlobalUserPreferences.trueBlackTheme ? R.style.Theme_Mastodon_Dark_TrueBlack_Yellow : R.style.Theme_Mastodon_Dark_Yellow;
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
public static boolean isDarkTheme(){
|
||||
if(GlobalUserPreferences.theme==GlobalUserPreferences.ThemePreference.AUTO)
|
||||
return (MastodonApp.context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK)==Configuration.UI_MODE_NIGHT_YES;
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
package org.joinmastodon.android.ui.views;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.text.Editable;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Gravity;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.EditText;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.ui.utils.SimpleTextWatcher;
|
||||
|
||||
import me.grishka.appkit.utils.CubicBezierInterpolator;
|
||||
import me.grishka.appkit.utils.V;
|
||||
|
||||
public class FloatingHintEditTextLayout extends FrameLayout{
|
||||
private EditText edit;
|
||||
private TextView label;
|
||||
private int labelTextSize;
|
||||
private int offsetY;
|
||||
private boolean hintVisible;
|
||||
private Animator currentAnim;
|
||||
|
||||
public FloatingHintEditTextLayout(Context context){
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public FloatingHintEditTextLayout(Context context, AttributeSet attrs){
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public FloatingHintEditTextLayout(Context context, AttributeSet attrs, int defStyle){
|
||||
super(context, attrs, defStyle);
|
||||
if(isInEditMode())
|
||||
V.setApplicationContext(context);
|
||||
TypedArray ta=context.obtainStyledAttributes(attrs, R.styleable.FloatingHintEditTextLayout);
|
||||
labelTextSize=ta.getDimensionPixelSize(R.styleable.FloatingHintEditTextLayout_android_labelTextSize, V.dp(12));
|
||||
offsetY=ta.getDimensionPixelOffset(R.styleable.FloatingHintEditTextLayout_editTextOffsetY, 0);
|
||||
ta.recycle();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFinishInflate(){
|
||||
super.onFinishInflate();
|
||||
if(getChildCount()>0 && getChildAt(0) instanceof EditText et){
|
||||
edit=et;
|
||||
}else{
|
||||
throw new IllegalStateException("First child must be an EditText");
|
||||
}
|
||||
|
||||
label=new TextView(getContext());
|
||||
label.setTextSize(TypedValue.COMPLEX_UNIT_PX, labelTextSize);
|
||||
label.setTextColor(edit.getHintTextColors());
|
||||
label.setText(edit.getHint());
|
||||
label.setSingleLine();
|
||||
label.setPivotX(0f);
|
||||
label.setPivotY(0f);
|
||||
LayoutParams lp=new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.START | Gravity.TOP);
|
||||
lp.setMarginStart(edit.getPaddingStart());
|
||||
addView(label, lp);
|
||||
|
||||
hintVisible=edit.getText().length()==0;
|
||||
if(hintVisible)
|
||||
label.setAlpha(0f);
|
||||
|
||||
edit.addTextChangedListener(new SimpleTextWatcher(this::onTextChanged));
|
||||
}
|
||||
|
||||
private void onTextChanged(Editable text){
|
||||
boolean newHintVisible=text.length()==0;
|
||||
if(newHintVisible==hintVisible)
|
||||
return;
|
||||
if(currentAnim!=null)
|
||||
currentAnim.cancel();
|
||||
hintVisible=newHintVisible;
|
||||
|
||||
label.setAlpha(1);
|
||||
float scale=edit.getLineHeight()/(float)label.getLineHeight();
|
||||
float transY=edit.getHeight()/2f-edit.getLineHeight()/2f+(edit.getTop()-label.getTop())-(label.getHeight()/2f-label.getLineHeight()/2f);
|
||||
|
||||
AnimatorSet anim=new AnimatorSet();
|
||||
if(hintVisible){
|
||||
anim.playTogether(
|
||||
ObjectAnimator.ofFloat(edit, TRANSLATION_Y, 0),
|
||||
ObjectAnimator.ofFloat(label, SCALE_X, scale),
|
||||
ObjectAnimator.ofFloat(label, SCALE_Y, scale),
|
||||
ObjectAnimator.ofFloat(label, TRANSLATION_Y, transY)
|
||||
);
|
||||
edit.setHintTextColor(0);
|
||||
}else{
|
||||
label.setScaleX(scale);
|
||||
label.setScaleY(scale);
|
||||
label.setTranslationY(transY);
|
||||
anim.playTogether(
|
||||
ObjectAnimator.ofFloat(edit, TRANSLATION_Y, offsetY),
|
||||
ObjectAnimator.ofFloat(label, SCALE_X, 1f),
|
||||
ObjectAnimator.ofFloat(label, SCALE_Y, 1f),
|
||||
ObjectAnimator.ofFloat(label, TRANSLATION_Y, 0f)
|
||||
);
|
||||
}
|
||||
anim.setDuration(150);
|
||||
anim.setInterpolator(CubicBezierInterpolator.DEFAULT);
|
||||
anim.start();
|
||||
anim.addListener(new AnimatorListenerAdapter(){
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation){
|
||||
currentAnim=null;
|
||||
if(hintVisible){
|
||||
label.setAlpha(0);
|
||||
edit.setHintTextColor(label.getTextColors());
|
||||
}
|
||||
}
|
||||
});
|
||||
currentAnim=anim;
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:color="@color/boost_selected" android:state_selected="true"/>
|
||||
<item android:color="?android:colorAccent" android:state_selected="true"/>
|
||||
<item android:color="?android:textColorSecondary" android:state_enabled="true"/>
|
||||
<item android:color="?android:textColorSecondary" android:alpha="0.3"/>
|
||||
</selector>
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:color="@color/gray_800" android:state_enabled="true"/>
|
||||
<item android:color="@color/gray_300"/>
|
||||
<item android:color="?android:colorPrimary" android:state_enabled="true"/>
|
||||
<item android:color="?colorPollVoted"/>
|
||||
</selector>
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:color="@color/gray_100" android:state_enabled="true"/>
|
||||
<item android:color="@color/gray_500"/>
|
||||
<item android:color="?colorSecondary" android:state_enabled="true"/>
|
||||
<item android:color="?colorPollVoted"/>
|
||||
</selector>
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:color="@color/gray_600" android:state_enabled="true"/>
|
||||
<item android:color="@color/gray_300"/>
|
||||
<item android:color="?colorPollVoted" android:state_enabled="true"/>
|
||||
<item android:color="?colorSearchHint"/>
|
||||
</selector>
|
||||
5
mastodon/src/main/res/color/button_text_m3_filled.xml
Normal file
5
mastodon/src/main/res/color/button_text_m3_filled.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:color="?colorM3OnPrimary" android:state_enabled="true"/>
|
||||
<item android:color="?colorM3OnSurface" android:alpha="0.38"/>
|
||||
</selector>
|
||||
5
mastodon/src/main/res/color/button_text_m3_text.xml
Normal file
5
mastodon/src/main/res/color/button_text_m3_text.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:color="?colorM3Primary" android:state_enabled="true"/>
|
||||
<item android:color="?colorM3OnSurface" android:alpha="0.38"/>
|
||||
</selector>
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:color="@color/gray_50" android:state_enabled="true"/>
|
||||
<item android:color="@color/gray_400"/>
|
||||
<item android:color="?colorTabInactive"/>
|
||||
</selector>
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:color="@color/gray_800" android:state_enabled="true"/>
|
||||
<item android:color="@color/gray_400"/>
|
||||
<item android:color="?colorTabInactive"/>
|
||||
</selector>
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:color="@color/gray_800" android:state_enabled="true"/>
|
||||
<item android:color="@color/gray_400"/>
|
||||
<item android:color="?android:colorPrimary" android:state_enabled="true"/>
|
||||
<item android:color="?colorTabInactive"/>
|
||||
</selector>
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:color="@color/gray_50"/>
|
||||
<item android:color="?colorSecondary"/>
|
||||
</selector>
|
||||
4
mastodon/src/main/res/color/m3_pressed_overlay.xml
Normal file
4
mastodon/src/main/res/color/m3_pressed_overlay.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:color="?colorM3PressedOverlay" android:alpha="0.12"/>
|
||||
</selector>
|
||||
4
mastodon/src/main/res/color/m3_primary_overlay.xml
Normal file
4
mastodon/src/main/res/color/m3_primary_overlay.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:color="?colorM3Primary" android:alpha="0.12"/>
|
||||
</selector>
|
||||
5
mastodon/src/main/res/color/m3_radiobutton_tint.xml
Normal file
5
mastodon/src/main/res/color/m3_radiobutton_tint.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:color="?colorM3Primary" android:state_checked="true"/>
|
||||
<item android:color="?colorM3OnSurfaceVariant"/>
|
||||
</selector>
|
||||
19
mastodon/src/main/res/drawable/bg_button_m3_filled.xml
Normal file
19
mastodon/src/main/res/drawable/bg_button_m3_filled.xml
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_enabled="true">
|
||||
<ripple android:color="@color/m3_pressed_overlay">
|
||||
<item>
|
||||
<shape>
|
||||
<solid android:color="?colorM3Primary"/>
|
||||
<corners android:radius="20dp"/>
|
||||
</shape>
|
||||
</item>
|
||||
</ripple>
|
||||
</item>
|
||||
<item>
|
||||
<shape>
|
||||
<solid android:color="?colorM3DisabledBackground"/>
|
||||
<corners android:radius="20dp"/>
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
||||
9
mastodon/src/main/res/drawable/bg_button_m3_text.xml
Normal file
9
mastodon/src/main/res/drawable/bg_button_m3_text.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ripple android:color="@color/m3_primary_overlay" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:id="@android:id/mask">
|
||||
<shape>
|
||||
<solid android:color="#000"/>
|
||||
<corners android:radius="20dp"/>
|
||||
</shape>
|
||||
</item>
|
||||
</ripple>
|
||||
@@ -2,7 +2,7 @@
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="@color/highlight_over_dark">
|
||||
<item>
|
||||
<shape android:shape="oval">
|
||||
<solid android:color="@color/gray_600"/>
|
||||
<solid android:color="?colorSearchHint"/>
|
||||
</shape>
|
||||
</item>
|
||||
</ripple>
|
||||
@@ -0,0 +1,3 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
||||
<path android:pathData="M12 7c0.414 0 0.75 0.336 0.75 0.75v3.5h3.5c0.414 0 0.75 0.336 0.75 0.75s-0.336 0.75-0.75 0.75h-3.5v3.5c0 0.414-0.336 0.75-0.75 0.75s-0.75-0.336-0.75-0.75v-3.5h-3.5C7.336 12.75 7 12.414 7 12s0.336-0.75 0.75-0.75h3.5v-3.5C11.25 7.336 11.586 7 12 7zM3 6.25C3 4.455 4.455 3 6.25 3h11.5C19.545 3 21 4.455 21 6.25v11.5c0 1.795-1.455 3.25-3.25 3.25H6.25C4.455 21 3 19.545 3 17.75V6.25zM6.25 4.5C5.284 4.5 4.5 5.284 4.5 6.25v11.5c0 0.966 0.784 1.75 1.75 1.75h11.5c0.966 0 1.75-0.784 1.75-1.75V6.25c0-0.966-0.784-1.75-1.75-1.75H6.25z" android:fillColor="@color/fluent_default_icon_tint"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,3 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
||||
<path android:pathData="M6.25 3C4.455 3 3 4.455 3 6.25v11.5C3 19.545 4.455 21 6.25 21h11.5c1.795 0 3.25-1.455 3.25-3.25V6.25C21 4.455 19.545 3 17.75 3H6.25zm11.03 6.28l-6.754 6.747c-0.293 0.292-0.767 0.292-1.06 0L6.72 13.28c-0.293-0.293-0.293-0.768 0-1.06 0.293-0.293 0.768-0.293 1.06 0l2.217 2.216 6.223-6.217c0.293-0.292 0.768-0.292 1.06 0.001 0.293 0.293 0.293 0.768 0 1.06z" android:fillColor="@color/fluent_default_icon_tint"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,3 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
||||
<path android:pathData="M6.25 3C4.455 3 3 4.455 3 6.25v11.5C3 19.545 4.455 21 6.25 21h11.5c1.795 0 3.25-1.455 3.25-3.25V6.25C21 4.455 19.545 3 17.75 3H6.25zM4.5 6.25c0-0.966 0.784-1.75 1.75-1.75h11.5c0.966 0 1.75 0.784 1.75 1.75v11.5c0 0.966-0.784 1.75-1.75 1.75H6.25c-0.966 0-1.75-0.784-1.75-1.75V6.25zm12.78 3.03c0.293-0.292 0.293-0.767 0-1.06-0.292-0.293-0.767-0.293-1.06 0l-6.223 6.216L7.78 12.22c-0.293-0.293-0.768-0.293-1.06 0-0.294 0.292-0.294 0.767 0 1.06l2.745 2.746c0.293 0.293 0.767 0.293 1.06 0l6.754-6.745z" android:fillColor="@color/fluent_default_icon_tint"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,3 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
||||
<path android:pathData="M3 6.25C3 4.455 4.455 3 6.25 3h11.5C19.545 3 21 4.455 21 6.25v11.5c0 1.795-1.455 3.25-3.25 3.25H6.25C4.455 21 3 19.545 3 17.75V6.25zM6.25 5C5.56 5 5 5.56 5 6.25v11.5C5 18.44 5.56 19 6.25 19h11.5c0.69 0 1.25-0.56 1.25-1.25V6.25C19 5.56 18.44 5 17.75 5H6.25z" android:fillColor="@color/fluent_default_icon_tint"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,3 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
||||
<path android:pathData="M3 6.25C3 4.455 4.455 3 6.25 3h11.5C19.545 3 21 4.455 21 6.25v11.5c0 1.795-1.455 3.25-3.25 3.25H6.25C4.455 21 3 19.545 3 17.75V6.25zM6.25 4.5C5.284 4.5 4.5 5.284 4.5 6.25v11.5c0 0.966 0.784 1.75 1.75 1.75h11.5c0.966 0 1.75-0.784 1.75-1.75V6.25c0-0.966-0.784-1.75-1.75-1.75H6.25z" android:fillColor="@color/fluent_default_icon_tint"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,3 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
||||
<path android:pathData="M3.839 5.858c2.94-3.916 9.03-5.055 13.364-2.36 4.28 2.66 5.854 7.777 4.1 12.577-1.655 4.533-6.016 6.328-9.159 4.048-1.177-0.854-1.634-1.925-1.854-3.664l-0.106-0.987-0.045-0.398c-0.123-0.934-0.311-1.352-0.705-1.572C8.9 13.204 8.542 13.197 7.84 13.47l-0.351 0.146-0.179 0.078c-1.014 0.44-1.688 0.595-2.541 0.416l-0.2-0.047-0.164-0.047c-2.79-0.865-3.203-4.648-0.565-8.158zm0.984 6.716l0.123 0.037 0.134 0.03c0.439 0.087 0.814 0.015 1.437-0.242l0.602-0.257c1.202-0.493 1.985-0.54 3.046 0.05 0.917 0.512 1.275 1.298 1.457 2.66l0.053 0.459 0.055 0.532 0.047 0.422c0.172 1.361 0.485 2.09 1.248 2.644 2.275 1.65 5.534 0.309 6.87-3.349 1.516-4.152 0.174-8.514-3.484-10.789-3.675-2.284-8.899-1.306-11.373 1.987-2.075 2.763-1.82 5.28-0.215 5.816zm11.225-1.994c-0.18-0.667 0.217-1.353 0.883-1.531 0.667-0.179 1.353 0.217 1.531 0.884 0.18 0.667-0.217 1.352-0.884 1.53-0.666 0.18-1.352-0.216-1.53-0.883zm0.494 3.488c-0.179-0.666 0.217-1.352 0.884-1.53 0.667-0.18 1.352 0.216 1.531 0.883 0.179 0.667-0.217 1.353-0.884 1.531-0.667 0.179-1.352-0.217-1.53-0.884zM14.07 7.577c-0.179-0.667 0.217-1.352 0.884-1.53 0.667-0.18 1.352 0.216 1.53 0.883 0.18 0.667-0.216 1.352-0.883 1.53-0.667 0.18-1.352-0.216-1.53-0.883zm-0.028 8.998c-0.18-0.666 0.217-1.352 0.883-1.53 0.667-0.18 1.353 0.216 1.531 0.883 0.18 0.667-0.217 1.353-0.883 1.531-0.667 0.179-1.353-0.217-1.531-0.884zm-3.497-9.97c-0.179-0.666 0.217-1.352 0.883-1.53 0.667-0.18 1.353 0.217 1.532 0.883 0.178 0.667-0.218 1.353-0.884 1.531-0.667 0.179-1.353-0.217-1.531-0.884z" android:fillColor="@color/fluent_default_icon_tint"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,3 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="20dp" android:height="20dp" android:viewportWidth="20" android:viewportHeight="20">
|
||||
<path android:pathData="M10 18c4.418 0 8-3.582 8-8s-3.582-8-8-8-8 3.582-8 8 3.582 8 8 8zm0-15c0.657 0 1.407 0.59 2.022 1.908 0.217 0.466 0.406 1.002 0.559 1.592H7.419c0.153-0.59 0.342-1.126 0.56-1.592C8.592 3.59 9.342 3 10 3zM7.072 4.485C6.796 5.077 6.565 5.757 6.389 6.5H3.936c0.837-1.446 2.176-2.565 3.778-3.118-0.241 0.33-0.456 0.704-0.642 1.103zM6.192 7.5C6.068 8.288 6 9.13 6 10c0 0.87 0.067 1.712 0.193 2.5H3.46C3.163 11.724 3 10.88 3 10c0-0.88 0.163-1.724 0.46-2.5h2.733zm0.197 6c0.176 0.743 0.407 1.422 0.683 2.015 0.186 0.399 0.401 0.773 0.642 1.103-1.602-0.553-2.941-1.672-3.778-3.118H6.39zm1.03 0h5.162c-0.153 0.59-0.342 1.126-0.56 1.592C11.408 16.41 10.658 17 10 17c-0.657 0-1.407-0.59-2.022-1.908C7.761 14.626 7.572 14.09 7.42 13.5zm5.375-1H7.206C7.073 11.725 7 10.883 7 10s0.074-1.725 0.206-2.5h5.588C12.927 8.275 13 9.117 13 10s-0.073 1.725-0.206 2.5zm0.817 1h2.453c-0.837 1.446-2.176 2.565-3.778 3.118 0.241-0.33 0.456-0.704 0.642-1.103 0.276-0.593 0.507-1.272 0.683-2.015zm2.93-1h-2.734C13.933 11.712 14 10.87 14 10c0-0.87-0.067-1.712-0.193-2.5h2.733C16.837 8.276 17 9.12 17 10c0 0.88-0.163 1.724-0.46 2.5zm-4.255-9.118c1.602 0.553 2.941 1.672 3.778 3.118H13.61c-0.176-0.743-0.407-1.423-0.683-2.015-0.186-0.399-0.401-0.773-0.642-1.103z" android:fillColor="@color/fluent_default_icon_tint"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,3 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
||||
<path android:pathData="M12 1.999c5.524 0 10.002 4.478 10.002 10.002 0 5.523-4.478 10.001-10.002 10.001-5.524 0-10.002-4.478-10.002-10.001C1.998 6.477 6.476 1.999 12 1.999zm0 1.5c-4.695 0-8.502 3.806-8.502 8.502 0 4.695 3.807 8.501 8.502 8.501s8.502-3.806 8.502-8.501c0-4.696-3.807-8.502-8.502-8.502zm-0.003 2.5c3.312 0 5.998 2.686 5.998 5.998 0 3.313-2.686 5.998-5.998 5.998-3.313 0-5.999-2.685-5.999-5.998S8.684 6 11.997 6z" android:fillColor="@color/fluent_default_icon_tint"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--~ Copyright (c) 2022. ~ Microsoft Corporation. All rights reserved.-->
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@drawable/ic_fluent_radio_button_24_filled" android:state_activated="true"/>
|
||||
<item android:drawable="@drawable/ic_fluent_radio_button_24_filled" android:state_checked="true"/>
|
||||
<item android:drawable="@drawable/ic_fluent_radio_button_24_filled" android:state_selected="true"/>
|
||||
<item android:drawable="@drawable/ic_fluent_radio_button_24_regular"/>
|
||||
</selector>
|
||||
@@ -4,7 +4,7 @@
|
||||
<item android:width="14dp" android:height="14dp" android:gravity="top|right">
|
||||
<shape android:shape="oval">
|
||||
<stroke android:color="?android:colorPrimary" android:width="2dp"/>
|
||||
<solid android:color="@color/primary_600"/>
|
||||
<solid android:color="?android:colorAccent"/>
|
||||
</shape>
|
||||
</item>
|
||||
</layer-list>
|
||||
9
mastodon/src/main/res/drawable/ic_m3_cancel.xml
Normal file
9
mastodon/src/main/res/drawable/ic_m3_cancel.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M12,2C6.47,2 2,6.47 2,12C2,17.53 6.47,22 12,22C17.53,22 22,17.53 22,12C22,6.47 17.53,2 12,2ZM12,20C7.59,20 4,16.41 4,12C4,7.59 7.59,4 12,4C16.41,4 20,7.59 20,12C20,16.41 16.41,20 12,20ZM12,10.59L15.59,7L17,8.41L13.41,12L17,15.59L15.59,17L12,13.41L8.41,17L7,15.59L10.59,12L7,8.41L8.41,7L12,10.59Z"
|
||||
android:fillColor="#49454F"/>
|
||||
</vector>
|
||||
9
mastodon/src/main/res/drawable/ic_m3_search.xml
Normal file
9
mastodon/src/main/res/drawable/ic_m3_search.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="20dp"
|
||||
android:height="20dp"
|
||||
android:viewportWidth="20"
|
||||
android:viewportHeight="20">
|
||||
<path
|
||||
android:pathData="M12.3,11.058L17.075,15.833L15.833,17.075L11.058,12.3C10.167,12.942 9.092,13.333 7.917,13.333C4.925,13.333 2.5,10.908 2.5,7.917C2.5,4.925 4.925,2.5 7.917,2.5C10.908,2.5 13.333,4.925 13.333,7.917C13.333,9.092 12.942,10.167 12.3,11.058ZM7.917,4.167C5.842,4.167 4.167,5.842 4.167,7.917C4.167,9.992 5.842,11.667 7.917,11.667C9.992,11.667 11.667,9.992 11.667,7.917C11.667,5.842 9.992,4.167 7.917,4.167Z"
|
||||
android:fillColor="#49454F"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@drawable/ic_fluent_checkbox_checked_24_filled" android:state_activated="true"/>
|
||||
<item android:drawable="@drawable/ic_fluent_checkbox_checked_24_filled" android:state_checked="true"/>
|
||||
<item android:drawable="@drawable/ic_fluent_checkbox_checked_24_filled" android:state_selected="true"/>
|
||||
<item android:drawable="@drawable/ic_fluent_checkbox_unchecked_24_filled"/>
|
||||
</selector>
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@drawable/ic_fluent_checkbox_checked_24_regular" android:state_activated="true"/>
|
||||
<item android:drawable="@drawable/ic_fluent_checkbox_checked_24_regular" android:state_checked="true"/>
|
||||
<item android:drawable="@drawable/ic_fluent_checkbox_checked_24_regular" android:state_selected="true"/>
|
||||
<item android:drawable="@drawable/ic_fluent_checkbox_unchecked_24_regular"/>
|
||||
</selector>
|
||||
@@ -4,7 +4,7 @@
|
||||
<item android:width="14dp" android:height="14dp" android:gravity="top|right">
|
||||
<shape android:shape="oval">
|
||||
<stroke android:color="?android:colorPrimary" android:width="2dp"/>
|
||||
<solid android:color="@color/primary_600"/>
|
||||
<solid android:color="?android:colorAccent"/>
|
||||
</shape>
|
||||
</item>
|
||||
</layer-list>
|
||||
@@ -1,9 +1,34 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="110dp"
|
||||
android:height="18dp"
|
||||
android:viewportWidth="110"
|
||||
android:viewportHeight="18">
|
||||
android:width="124.78dp"
|
||||
android:height="22.75dp"
|
||||
android:viewportWidth="124.78"
|
||||
android:viewportHeight="22.75">
|
||||
<path
|
||||
android:pathData="M18.406,9.999V17.878H15.287V10.231C15.287,8.618 14.609,7.8 13.254,7.8C11.755,7.8 11.004,8.771 11.004,10.69V14.876H7.903V10.69C7.903,8.771 7.151,7.8 5.653,7.8C4.297,7.8 3.619,8.618 3.619,10.231V17.878H0.5V9.999C0.5,8.389 0.91,7.109 1.733,6.162C2.581,5.215 3.693,4.73 5.072,4.73C6.668,4.73 7.877,5.344 8.676,6.572L9.453,7.876L10.23,6.572C11.029,5.344 12.238,4.73 13.834,4.73C15.214,4.73 16.325,5.215 17.174,6.162C17.997,7.109 18.406,8.389 18.406,9.999ZM29.152,13.916C29.795,13.235 30.105,12.378 30.105,11.345C30.105,10.311 29.795,9.454 29.152,8.798C28.532,8.118 27.745,7.79 26.791,7.79C25.838,7.79 25.051,8.118 24.431,8.798C23.811,9.454 23.501,10.311 23.501,11.345C23.501,12.378 23.811,13.235 24.431,13.916C25.051,14.571 25.838,14.899 26.791,14.899C27.745,14.899 28.532,14.571 29.152,13.916ZM30.105,5.042H33.181V17.647H30.105V16.16C29.176,17.395 27.888,18 26.219,18C24.622,18 23.263,17.37 22.118,16.084C20.998,14.798 20.426,13.21 20.426,11.345C20.426,9.504 20.998,7.916 22.118,6.63C23.263,5.345 24.622,4.689 26.219,4.689C27.888,4.689 29.176,5.294 30.105,6.529V5.042ZM43.528,11.118C44.434,11.798 44.888,12.756 44.864,13.966C44.864,15.252 44.41,16.26 43.481,16.966C42.551,17.647 41.431,18 40.072,18C37.616,18 35.947,16.992 35.065,15L37.735,13.412C38.093,14.496 38.879,15.05 40.072,15.05C41.168,15.05 41.717,14.697 41.717,13.966C41.717,13.437 41.001,12.958 39.547,12.58C38.999,12.429 38.546,12.277 38.188,12.151C37.688,11.95 37.258,11.723 36.901,11.445C36.019,10.765 35.566,9.857 35.566,8.698C35.566,7.462 35.995,6.479 36.853,5.773C37.735,5.042 38.808,4.689 40.095,4.689C42.146,4.689 43.648,5.571 44.625,7.361L42.003,8.874C41.621,8.017 40.978,7.588 40.095,7.588C39.166,7.588 38.713,7.941 38.713,8.622C38.713,9.151 39.428,9.63 40.882,10.008C42.003,10.26 42.885,10.639 43.528,11.118ZM53.304,8.168H50.61V13.412C50.61,14.042 50.848,14.42 51.301,14.597C51.635,14.723 52.303,14.748 53.304,14.697V17.647C51.23,17.899 49.728,17.698 48.846,17.017C47.964,16.361 47.534,15.151 47.534,13.412V8.168H45.46V5.042H47.534V2.496L50.61,1.513V5.042H53.304V8.168ZM63.103,13.84C63.723,13.185 64.033,12.353 64.033,11.344C64.033,10.336 63.723,9.504 63.103,8.849C62.483,8.193 61.721,7.866 60.791,7.866C59.861,7.866 59.098,8.193 58.478,8.849C57.882,9.529 57.572,10.361 57.572,11.344C57.572,12.328 57.882,13.16 58.478,13.84C59.098,14.496 59.861,14.823 60.791,14.823C61.721,14.823 62.483,14.496 63.103,13.84ZM56.308,16.084C55.093,14.798 54.497,13.235 54.497,11.344C54.497,9.479 55.093,7.916 56.308,6.63C57.524,5.345 59.026,4.689 60.791,4.689C62.555,4.689 64.057,5.345 65.273,6.63C66.489,7.916 67.109,9.504 67.109,11.344C67.109,13.21 66.489,14.798 65.273,16.084C64.057,17.37 62.579,18 60.791,18C59.002,18 57.524,17.37 56.308,16.084ZM77.385,13.916C78.005,13.235 78.314,12.378 78.314,11.345C78.314,10.311 78.005,9.454 77.385,8.798C76.765,8.118 75.978,7.79 75.024,7.79C74.071,7.79 73.284,8.118 72.64,8.798C72.021,9.454 71.71,10.311 71.71,11.345C71.71,12.378 72.021,13.235 72.64,13.916C73.284,14.571 74.094,14.899 75.024,14.899C75.978,14.899 76.765,14.571 77.385,13.916ZM78.314,0H81.39V17.647H78.314V16.16C77.409,17.395 76.121,18 74.452,18C72.855,18 71.472,17.37 70.328,16.084C69.207,14.798 68.635,13.21 68.635,11.345C68.635,9.504 69.207,7.916 70.328,6.63C71.472,5.345 72.855,4.689 74.452,4.689C76.121,4.689 77.409,5.294 78.314,6.529V0ZM92.191,13.84C92.811,13.185 93.12,12.353 93.12,11.344C93.12,10.336 92.811,9.504 92.191,8.849C91.571,8.193 90.808,7.866 89.878,7.866C88.948,7.866 88.185,8.193 87.565,8.849C86.969,9.529 86.659,10.361 86.659,11.344C86.659,12.328 86.969,13.16 87.565,13.84C88.185,14.496 88.948,14.823 89.878,14.823C90.808,14.823 91.571,14.496 92.191,13.84ZM85.396,16.084C84.18,14.798 83.584,13.235 83.584,11.344C83.584,9.479 84.18,7.916 85.396,6.63C86.612,5.345 88.114,4.689 89.878,4.689C91.642,4.689 93.144,5.345 94.36,6.63C95.576,7.916 96.196,9.504 96.196,11.344C96.196,13.21 95.576,14.798 94.36,16.084C93.144,17.37 91.666,18 89.878,18C88.09,18 86.612,17.37 85.396,16.084ZM109.5,9.908V17.647H106.424V10.311C106.424,9.479 106.21,8.849 105.781,8.37C105.375,7.941 104.803,7.714 104.064,7.714C102.324,7.714 101.442,8.748 101.442,10.84V17.647H98.366V5.042H101.442V6.454C102.181,5.269 103.349,4.689 104.994,4.689C106.305,4.689 107.378,5.143 108.213,6.076C109.071,7.009 109.5,8.269 109.5,9.908Z"
|
||||
android:fillColor="#282C37"/>
|
||||
android:pathData="m26.16,17.57q-1.82,0 -3.29,-0.84 -1.46,-0.84 -2.3,-2.3 -0.82,-1.49 -0.82,-3.34 0,-1.75 0.82,-3.24 0.82,-1.51 2.26,-2.4 1.44,-0.89 3.22,-0.89 1.9,0 3.26,0.82 1.37,0.82 2.06,2.23 0.72,1.42 0.72,3.14 0,0.55 -0.07,1.06h-9.19q0.24,1.46 1.15,2.23 0.94,0.74 2.23,0.74 1.08,0 1.85,-0.46 0.79,-0.48 1.25,-1.27l2.54,1.25q-1.87,3.26 -5.69,3.26zM29.07,9.58q-0.05,-0.58 -0.43,-1.13 -0.36,-0.58 -1.03,-0.94 -0.65,-0.38 -1.54,-0.38 -1.13,0 -1.94,0.67 -0.79,0.65 -1.15,1.78z"
|
||||
android:fillColor="#282c37"/>
|
||||
<path
|
||||
android:pathData="m40.27,22.75q-2.33,0 -3.86,-1.06 -1.54,-1.03 -2.06,-2.5l2.95,-1.2q0.38,0.91 1.18,1.44 0.79,0.53 1.8,0.53 1.49,0 2.35,-0.91 0.89,-0.91 0.89,-2.59v-0.96h-0.19q-0.58,0.82 -1.51,1.25 -0.94,0.43 -2.14,0.43 -1.51,0 -2.83,-0.77 -1.3,-0.79 -2.09,-2.21 -0.79,-1.44 -0.79,-3.34 0,-1.9 0.79,-3.31 0.79,-1.44 2.09,-2.21 1.32,-0.79 2.83,-0.79 1.2,0 2.14,0.43 0.94,0.43 1.51,1.25h0.19v-1.3h3.02L46.54,16.34q0,1.97 -0.77,3.41 -0.77,1.44 -2.18,2.21 -1.42,0.79 -3.31,0.79zM40.32,14.33q1.32,0 2.26,-0.91 0.94,-0.94 0.94,-2.54 0,-1.66 -0.94,-2.54 -0.91,-0.91 -2.26,-0.91 -1.34,0 -2.28,0.91 -0.94,0.91 -0.94,2.54 0,1.63 0.94,2.54 0.94,0.91 2.28,0.91z"
|
||||
android:fillColor="#282c37"/>
|
||||
<path
|
||||
android:pathData="m53.23,17.57q-1.94,0 -3.19,-1.13 -1.25,-1.15 -1.25,-3 0,-1.22 0.65,-2.16 0.65,-0.94 1.78,-1.44 1.13,-0.5 2.5,-0.5 1.9,0 3.24,0.55L56.95,9.36q0,-1.01 -0.77,-1.63 -0.74,-0.62 -2.02,-0.62 -0.86,0 -1.68,0.41 -0.79,0.38 -1.32,1.03l-2.02,-1.58q0.89,-1.15 2.23,-1.78 1.34,-0.62 2.93,-0.62 2.81,0 4.27,1.3 1.46,1.3 1.46,3.79v7.54h-3.1v-1.25h-0.19q-0.55,0.72 -1.46,1.18 -0.89,0.46 -2.06,0.46zM53.98,15.12q1.37,0 2.16,-0.86 0.82,-0.89 0.82,-2.06 -1.22,-0.58 -2.57,-0.58 -2.45,0 -2.45,1.82 0,0.74 0.53,1.22 0.53,0.46 1.51,0.46z"
|
||||
android:fillColor="#282c37"/>
|
||||
<path
|
||||
android:pathData="m62.95,0h3.14L66.1,17.18h-3.14z"
|
||||
android:fillColor="#282c37"/>
|
||||
<path
|
||||
android:pathData="m74.93,17.57q-1.9,0 -3.38,-0.84 -1.49,-0.86 -2.33,-2.33 -0.82,-1.49 -0.82,-3.34 0,-1.82 0.82,-3.31 0.84,-1.49 2.33,-2.33 1.49,-0.86 3.38,-0.86 1.87,0 3.36,0.86 1.49,0.84 2.3,2.33 0.84,1.49 0.84,3.31 0,1.85 -0.84,3.34 -0.82,1.46 -2.3,2.33 -1.49,0.84 -3.36,0.84zM74.93,14.66q0.91,0 1.68,-0.43 0.77,-0.43 1.22,-1.25 0.46,-0.82 0.46,-1.92 0,-1.08 -0.46,-1.9 -0.46,-0.82 -1.22,-1.25 -0.77,-0.43 -1.68,-0.43 -0.91,0 -1.68,0.43 -0.77,0.43 -1.25,1.25 -0.46,0.82 -0.46,1.9 0,1.08 0.46,1.92 0.48,0.82 1.25,1.25 0.77,0.43 1.68,0.43z"
|
||||
android:fillColor="#282c37"/>
|
||||
<path
|
||||
android:pathData="m89.09,17.57q-1.66,0 -3.02,-0.82 -1.34,-0.84 -2.14,-2.3 -0.77,-1.49 -0.77,-3.38 0,-1.87 0.77,-3.36 0.79,-1.49 2.14,-2.3 1.37,-0.84 3.02,-0.84 1.25,0 2.21,0.55 0.96,0.53 1.46,1.32h0.19l-0.19,-1.73L92.76,0h3.12L95.88,17.18h-2.93v-1.46h-0.19q-0.48,0.79 -1.46,1.32 -0.96,0.53 -2.21,0.53zM89.62,14.66q0.89,0 1.66,-0.46 0.77,-0.46 1.22,-1.27 0.46,-0.82 0.46,-1.87 0,-1.06 -0.46,-1.87 -0.46,-0.82 -1.22,-1.25 -0.77,-0.46 -1.66,-0.46 -0.86,0 -1.63,0.46 -0.77,0.43 -1.22,1.25 -0.46,0.82 -0.46,1.87 0,1.06 0.46,1.87 0.46,0.82 1.22,1.27 0.77,0.46 1.63,0.46z"
|
||||
android:fillColor="#282c37"/>
|
||||
<path
|
||||
android:pathData="m104.72,17.57q-1.9,0 -3.38,-0.84 -1.49,-0.86 -2.33,-2.33 -0.82,-1.49 -0.82,-3.34 0,-1.82 0.82,-3.31 0.84,-1.49 2.33,-2.33 1.49,-0.86 3.38,-0.86 1.87,0 3.36,0.86 1.49,0.84 2.3,2.33 0.84,1.49 0.84,3.31 0,1.85 -0.84,3.34 -0.82,1.46 -2.3,2.33 -1.49,0.84 -3.36,0.84zM104.72,14.66q0.91,0 1.68,-0.43 0.77,-0.43 1.22,-1.25 0.46,-0.82 0.46,-1.92 0,-1.08 -0.46,-1.9 -0.46,-0.82 -1.22,-1.25 -0.77,-0.43 -1.68,-0.43 -0.91,0 -1.68,0.43 -0.77,0.43 -1.25,1.25 -0.46,0.82 -0.46,1.9 0,1.08 0.46,1.92 0.48,0.82 1.25,1.25 0.77,0.43 1.68,0.43z"
|
||||
android:fillColor="#282c37"/>
|
||||
<path
|
||||
android:pathData="m113.38,4.94h2.95v1.54h0.19q0.55,-0.89 1.56,-1.39 1.01,-0.53 2.18,-0.53 2.21,0 3.36,1.34 1.15,1.34 1.15,3.67v7.61h-3.14v-7.22q0,-1.18 -0.6,-1.82 -0.6,-0.65 -1.68,-0.65 -1.27,0 -2.06,0.98 -0.77,0.98 -0.77,2.47v6.24h-3.14z"
|
||||
android:fillColor="#282c37"/>
|
||||
<path
|
||||
android:pathData="m4.53,4.54c-1.37,0 -2.47,0.48 -3.31,1.42C0.41,6.9 0,8.16 0,9.76v7.8h3.09v-7.57c0,-1.6 0.67,-2.41 2.01,-2.41 1.48,0 2.23,0.96 2.23,2.86v4.14h3.07v-4.14c0,-1.9 0.74,-2.86 2.23,-2.86 1.34,0 2.01,0.81 2.01,2.41v7.57h3.09v-7.8c0,-1.59 -0.41,-2.86 -1.22,-3.8 -0.84,-0.94 -1.94,-1.42 -3.31,-1.42 -1.58,0 -2.78,0.61 -3.57,1.82l-0.77,1.29 -0.77,-1.29C7.3,5.15 6.11,4.54 4.53,4.54Z"
|
||||
android:strokeWidth="0.990258"
|
||||
android:fillColor="#282c37"/>
|
||||
</vector>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item>
|
||||
<shape android:shape="oval">
|
||||
<stroke android:color="@color/gray_50" android:width="4dp"/>
|
||||
<stroke android:color="?colorSecondary" android:width="4dp"/>
|
||||
<solid android:color="#80000000"/>
|
||||
<size android:width="52dp" android:height="52dp"/>
|
||||
</shape>
|
||||
|
||||
5
mastodon/src/main/res/drawable/rect_4dp.xml
Normal file
5
mastodon/src/main/res/drawable/rect_4dp.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="#000"/>
|
||||
<corners android:radius="4dp"/>
|
||||
</shape>
|
||||
@@ -1,7 +1,7 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="257dp"
|
||||
android:height="67dp"
|
||||
android:width="134dp"
|
||||
android:height="34dp"
|
||||
android:viewportWidth="313"
|
||||
android:viewportHeight="81">
|
||||
<path
|
||||
@@ -20,8 +20,8 @@
|
||||
</path>
|
||||
<path
|
||||
android:pathData="M14.81,23.2C14.81,20.72 16.77,18.72 19.2,18.72C21.62,18.72 23.58,20.73 23.58,23.2C23.58,25.67 21.62,27.68 19.2,27.68C16.77,27.68 14.81,25.67 14.81,23.2Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
android:fillColor="#000"/>
|
||||
<path
|
||||
android:pathData="M80.02,27.06V47.66H72.03V27.67C72.03,23.45 70.3,21.32 66.83,21.32C63,21.32 61.07,23.87 61.07,28.87V39.82H53.14V28.87C53.14,23.84 51.24,21.32 47.38,21.32C43.92,21.32 42.18,23.45 42.18,27.67V47.65H34.21V27.06C34.21,22.86 35.25,19.51 37.35,17.03C39.53,14.54 42.37,13.29 45.89,13.29C49.97,13.29 53.07,14.9 55.11,18.11L57.11,21.52L59.1,18.11C61.14,14.91 64.23,13.29 68.32,13.29C71.84,13.29 74.69,14.55 76.86,17.03C78.96,19.51 80.01,22.83 80.01,27.06H80.02ZM107.49,37.3C109.15,35.51 109.93,33.29 109.93,30.59C109.93,27.89 109.14,25.65 107.49,23.94C105.91,22.15 103.89,21.3 101.45,21.3C99.02,21.3 97.01,22.15 95.41,23.94C93.83,25.65 93.04,27.89 93.04,30.59C93.04,33.29 93.83,35.53 95.41,37.3C97,39 99.02,39.87 101.45,39.87C103.89,39.87 105.9,39.02 107.49,37.3ZM109.93,14.12H117.8V47.06H109.93V43.18C107.55,46.41 104.26,48 99.99,48C95.71,48 92.42,46.36 89.5,43C86.64,39.64 85.18,35.48 85.18,30.61C85.18,25.74 86.65,21.65 89.5,18.29C92.43,14.93 95.92,13.23 99.99,13.23C104.06,13.23 107.55,14.81 109.93,18.02V14.14V14.12ZM144.26,29.97C146.58,31.76 147.73,34.25 147.67,37.41C147.67,40.77 146.52,43.41 144.14,45.24C141.76,47.03 138.89,47.94 135.41,47.94C129.13,47.94 124.87,45.3 122.61,40.11L129.43,35.96C130.34,38.78 132.35,40.25 135.41,40.25C138.22,40.25 139.62,39.33 139.62,37.42C139.62,36.03 137.79,34.78 134.07,33.8C132.66,33.41 131.5,33.01 130.6,32.68C129.31,32.16 128.22,31.56 127.31,30.83C125.05,29.04 123.9,26.68 123.9,23.65C123.9,20.42 124.99,17.85 127.19,16C129.45,14.09 132.19,13.18 135.48,13.18C140.73,13.18 144.56,15.48 147.07,20.16L140.37,24.1C139.4,21.86 137.74,20.74 135.48,20.74C133.11,20.74 131.95,21.65 131.95,23.44C131.95,24.83 133.78,26.08 137.5,27.06C140.37,27.72 142.63,28.7 144.26,29.97H144.27H144.26ZM169.26,22.27H162.37V35.98C162.37,37.63 162.98,38.63 164.15,39.08C165,39.4 166.71,39.47 169.27,39.34V47.05C163.98,47.71 160.14,47.17 157.88,45.41C155.62,43.7 154.53,40.53 154.53,36V22.27H149.23V14.1H154.53V7.46L162.39,4.89V14.12H169.29V22.29H169.27L169.26,22.27ZM194.34,37.1C195.92,35.4 196.71,33.22 196.71,30.58C196.71,27.94 195.92,25.78 194.34,24.05C192.74,22.35 190.79,21.48 188.42,21.48C186.04,21.48 184.09,22.33 182.49,24.05C180.97,25.84 180.18,28 180.18,30.58C180.18,33.16 180.97,35.31 182.49,37.1C184.08,38.81 186.04,39.67 188.42,39.67C190.79,39.67 192.74,38.82 194.34,37.1ZM176.96,42.96C173.85,39.6 172.32,35.52 172.32,30.58C172.32,25.63 173.85,21.62 176.96,18.26C180.07,14.9 183.91,13.19 188.42,13.19C192.92,13.19 196.77,14.9 199.87,18.26C202.97,21.62 204.57,25.77 204.57,30.58C204.57,35.39 202.97,39.6 199.87,42.96C196.76,46.32 192.98,47.96 188.42,47.96C183.85,47.96 180.06,46.32 176.96,42.96ZM230.86,37.29C232.45,35.5 233.24,33.28 233.24,30.58C233.24,27.87 232.45,25.63 230.86,23.93C229.28,22.14 227.26,21.29 224.82,21.29C222.39,21.29 220.37,22.14 218.73,23.93C217.14,25.63 216.35,27.87 216.35,30.58C216.35,33.28 217.14,35.52 218.73,37.29C220.38,38.99 222.45,39.86 224.82,39.86C227.2,39.86 229.27,39 230.86,37.29ZM233.24,0.92H241.11V47.05H233.24V43.17C230.93,46.39 227.63,47.99 223.36,47.99C219.09,47.99 215.75,46.35 212.8,42.98C209.93,39.62 208.48,35.47 208.48,30.6C208.48,25.73 209.95,21.64 212.8,18.28C215.72,14.92 219.26,13.22 223.36,13.22C227.45,13.22 230.93,14.8 233.24,18.01V0.93V0.92ZM268.74,37.07C270.32,35.36 271.12,33.18 271.12,30.54C271.12,27.9 270.32,25.74 268.74,24.01C267.15,22.31 265.21,21.45 262.82,21.45C260.43,21.45 258.5,22.3 256.9,24.01C255.37,25.8 254.58,27.96 254.58,30.54C254.58,33.12 255.37,35.28 256.9,37.07C258.48,38.77 260.44,39.64 262.82,39.64C265.2,39.64 267.14,38.78 268.74,37.07ZM251.36,42.92C248.26,39.56 246.73,35.48 246.73,30.54C246.73,25.6 248.25,21.58 251.36,18.22C254.47,14.86 258.32,13.15 262.82,13.15C267.32,13.15 271.18,14.86 274.27,18.22C277.38,21.58 278.97,25.73 278.97,30.54C278.97,35.35 277.38,39.56 274.27,42.92C271.16,46.28 267.38,47.93 262.82,47.93C258.26,47.93 254.46,46.28 251.36,42.92ZM313,26.78V47.01H305.14V27.84C305.14,25.66 304.59,24.01 303.48,22.77C302.45,21.65 300.98,21.07 299.09,21.07C294.65,21.07 292.39,23.77 292.39,29.24V47.03H284.53V14.1H292.39V17.81C294.28,14.71 297.28,13.19 301.47,13.19C304.82,13.19 307.57,14.37 309.71,16.81C311.91,19.24 313,22.54 313,26.82"
|
||||
android:fillColor="#ffffff"/>
|
||||
android:fillColor="#000"/>
|
||||
</vector>
|
||||
|
||||
4
mastodon/src/main/res/drawable/white_circle.xml
Normal file
4
mastodon/src/main/res/drawable/white_circle.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
|
||||
<solid android:color="#fff"/>
|
||||
</shape>
|
||||
@@ -15,6 +15,7 @@
|
||||
android:outlineProvider="background"
|
||||
android:elevation="2dp">
|
||||
<ImageView
|
||||
android:id="@+id/icon"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_margin="16dp"
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
<LinearLayout
|
||||
android:id="@+id/button"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingVertical="12dp"
|
||||
android:outlineProvider="background"
|
||||
android:elevation="2dp"
|
||||
android:background="@drawable/bg_poll_option_clickable"
|
||||
@@ -25,7 +26,6 @@
|
||||
android:layout_height="24dp"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:duplicateParentState="true"
|
||||
android:tint="?colorDarkIcon"
|
||||
android:src="@drawable/ic_poll_option_button"/>
|
||||
|
||||
@@ -48,9 +48,7 @@
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginRight="16dp"
|
||||
android:textAppearance="@style/m3_title_medium"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="end"
|
||||
tools:text="scream into void"/>
|
||||
tools:text="scream into void. like this: aaaaaaaaaaaaaaaaaaaa"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
android:paddingBottom="12dp">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/text_wrap"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
@@ -17,7 +18,8 @@
|
||||
android:layout_marginTop="6dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone">
|
||||
<View
|
||||
android:id="@+id/border_top"
|
||||
android:layout_width="match_parent"
|
||||
@@ -47,10 +49,57 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:textSize="16sp"
|
||||
android:textAppearance="@style/m3_body_large"
|
||||
tools:text="setting up my mstdn"/>
|
||||
</LinearLayout>
|
||||
android:textAppearance="@style/m3_body_large"/>
|
||||
|
||||
<org.joinmastodon.android.ui.views.AutoOrientationLinearLayout
|
||||
android:id="@+id/translate_wrap"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/action_btn_wrap"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="8dp"
|
||||
android:clipToPadding="false">
|
||||
<org.joinmastodon.android.ui.views.ProgressBarButton
|
||||
android:id="@+id/translate_btn"
|
||||
style="?secondaryButtonStyle"
|
||||
android:background="?android:selectableItemBackground"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="8dp"
|
||||
tools:text="@string/translate_post"/>
|
||||
<ProgressBar
|
||||
android:id="@+id/translate_progress"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:indeterminate="true"
|
||||
style="?android:progressBarStyleSmall"
|
||||
android:elevation="10dp"
|
||||
android:outlineProvider="none"
|
||||
android:indeterminateTint="?android:textColorPrimary"
|
||||
android:visibility="gone"/>
|
||||
</FrameLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/translate_info"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
android:layout_marginVertical="4dp"
|
||||
android:layout_weight="1"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:textAlignment="textEnd"
|
||||
tools:text="Translated using TranslateEngine" />
|
||||
|
||||
</org.joinmastodon.android.ui.views.AutoOrientationLinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:visibility="gone"
|
||||
|
||||
@@ -132,6 +132,7 @@
|
||||
android:outlineProvider="background"
|
||||
android:elevation="2dp">
|
||||
<ImageView
|
||||
android:id="@+id/add_poll_option_icon"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_margin="16dp"
|
||||
@@ -148,6 +149,39 @@
|
||||
android:textAppearance="@style/m3_label_large"
|
||||
android:textColor="?android:textColorPrimary"
|
||||
tools:text="Duration: 7 days"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/poll_allow_multiple"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:gravity="center_vertical"
|
||||
android:layoutDirection="locale"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingRight="16dp"
|
||||
android:paddingTop="12dp"
|
||||
android:paddingBottom="12dp"
|
||||
android:background="?android:selectableItemBackground">
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/poll_allow_multiple_checkbox"
|
||||
android:clickable="false"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="24dp"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:duplicateParentState="true"
|
||||
android:importantForAccessibility="no"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:textSize="16sp"
|
||||
android:singleLine="true"
|
||||
android:text="@string/sk_poll_allow_multiple" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<org.joinmastodon.android.ui.views.ComposeMediaLayout
|
||||
@@ -162,6 +196,7 @@
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:gravity="center_vertical"
|
||||
android:layoutDirection="locale"
|
||||
@@ -187,7 +222,7 @@
|
||||
android:layout_weight="1"
|
||||
android:textSize="16sp"
|
||||
android:singleLine="true"
|
||||
android:text="@string/mark_media_as_sensitive" />
|
||||
android:text="@string/sk_mark_media_as_sensitive" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
|
||||
52
mastodon/src/main/res/layout/fragment_login.xml
Normal file
52
mastodon/src/main/res/layout/fragment_login.xml
Normal file
@@ -0,0 +1,52 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<me.grishka.appkit.views.FragmentRootLinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="vertical"
|
||||
android:id="@+id/appkit_loader_root"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:background="?colorM3Background">
|
||||
|
||||
<include layout="@layout/appkit_toolbar"/>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/appkit_loader_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0px"
|
||||
android:layout_weight="1">
|
||||
|
||||
<include layout="@layout/loading"
|
||||
android:id="@+id/loading"/>
|
||||
|
||||
<ViewStub android:layout="?errorViewLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/error"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/content_stub"/>
|
||||
|
||||
</FrameLayout>
|
||||
<LinearLayout
|
||||
android:id="@+id/button_bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
<Button
|
||||
android:id="@+id/btn_next"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginRight="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:minWidth="145dp"
|
||||
style="@style/Widget.Mastodon.M3.Button.Filled"
|
||||
android:text="@string/next" />
|
||||
|
||||
</LinearLayout>
|
||||
</me.grishka.appkit.views.FragmentRootLinearLayout>
|
||||
60
mastodon/src/main/res/layout/fragment_megalodon_welcome.xml
Normal file
60
mastodon/src/main/res/layout/fragment_megalodon_welcome.xml
Normal file
@@ -0,0 +1,60 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<me.grishka.appkit.views.FragmentRootLinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="vertical"
|
||||
android:id="@+id/appkit_loader_root"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:background="?colorBackgroundLight">
|
||||
|
||||
<include layout="@layout/appkit_toolbar"/>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/appkit_loader_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1">
|
||||
|
||||
<include layout="@layout/loading"
|
||||
android:id="@+id/loading"/>
|
||||
|
||||
<ViewStub android:layout="?errorViewLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/error"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/content_stub"/>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0.5dp"
|
||||
android:background="?attr/colorPollVoted"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/button_bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?colorBackgroundLight"
|
||||
android:outlineProvider="bounds"
|
||||
android:orientation="horizontal"
|
||||
android:elevation="0dp">
|
||||
|
||||
<Button
|
||||
style="?primaryLargeButtonStyle"
|
||||
android:id="@+id/btn_next"
|
||||
android:minWidth="145dp"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/next" />
|
||||
|
||||
</LinearLayout>
|
||||
</me.grishka.appkit.views.FragmentRootLinearLayout>
|
||||
@@ -1,12 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<org.joinmastodon.android.ui.views.SizeListenerFrameLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<org.joinmastodon.android.ui.views.SizeListenerFrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true"
|
||||
android:clipToPadding="false"
|
||||
android:clipChildren="false">
|
||||
android:clipToPadding="false">
|
||||
|
||||
<View
|
||||
android:id="@+id/blue_fill"
|
||||
@@ -18,51 +17,60 @@
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/art_container"
|
||||
android:layout_width="450dp"
|
||||
android:layout_height="631dp"
|
||||
android:layout_marginTop="40dp"
|
||||
android:layout_gravity="center">
|
||||
android:layout_width="360dp"
|
||||
android:layout_height="640dp"
|
||||
android:layout_gravity="center"
|
||||
tools:ignore="rtlHardcoded">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/art_clouds"
|
||||
android:layout_width="450dp"
|
||||
android:layout_height="589dp"
|
||||
android:layout_gravity="bottom|center_horizontal"
|
||||
android:layout_width="414dp"
|
||||
android:layout_height="541dp"
|
||||
android:layout_marginTop="91dp"
|
||||
android:layout_gravity="top|left"
|
||||
android:alpha="0.3"
|
||||
android:importantForAccessibility="no"
|
||||
android:src="@drawable/splash_art_layer0"/>
|
||||
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/art_plane_elephant"
|
||||
android:layout_width="245.64dp"
|
||||
android:layout_height="72.65dp"
|
||||
android:layout_marginLeft="-101.55dp"
|
||||
android:layout_marginTop="238.12dp"
|
||||
android:layout_gravity="left|top"
|
||||
android:alpha="0.3"
|
||||
android:importantForAccessibility="no"
|
||||
android:src="@drawable/splash_art_layer4"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/art_right_hill"
|
||||
android:layout_width="218dp"
|
||||
android:layout_height="255dp"
|
||||
android:layout_gravity="bottom|right"
|
||||
android:layout_marginBottom="156dp"
|
||||
android:layout_marginRight="11dp"
|
||||
android:layout_width="150.84dp"
|
||||
android:layout_height="176.44dp"
|
||||
android:layout_gravity="top|left"
|
||||
android:layout_marginLeft="322dp"
|
||||
android:layout_marginTop="310dp"
|
||||
android:importantForAccessibility="no"
|
||||
android:src="@drawable/splash_art_layer1"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/art_left_hill"
|
||||
android:layout_width="285dp"
|
||||
android:layout_height="222dp"
|
||||
android:layout_gravity="bottom|left"
|
||||
android:layout_marginLeft="-6dp"
|
||||
android:layout_marginBottom="243dp"
|
||||
android:layout_width="197.2dp"
|
||||
android:layout_height="153.61dp"
|
||||
android:layout_gravity="top|left"
|
||||
android:layout_marginTop="294dp"
|
||||
android:importantForAccessibility="no"
|
||||
android:src="@drawable/splash_art_layer2"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/art_center_hill"
|
||||
android:layout_width="457dp"
|
||||
android:layout_height="397dp"
|
||||
android:layout_gravity="bottom|center_horizontal"
|
||||
android:layout_marginBottom="51dp"
|
||||
android:layout_width="400dp"
|
||||
android:layout_height="346dp"
|
||||
android:layout_gravity="top|left"
|
||||
android:layout_marginTop="294dp"
|
||||
android:importantForAccessibility="no"
|
||||
android:src="@drawable/splash_art_layer3"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/art_plane_elephant"
|
||||
android:layout_width="355dp"
|
||||
android:layout_height="105dp"
|
||||
android:layout_gravity="left|top"
|
||||
android:src="@drawable/splash_art_layer4"/>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<View
|
||||
@@ -73,36 +81,65 @@
|
||||
android:transformPivotY="1px"
|
||||
android:background="#478E6A"/>
|
||||
|
||||
<org.joinmastodon.android.ui.views.SplashLogoView
|
||||
android:layout_width="261dp"
|
||||
android:layout_height="71dp"
|
||||
android:layout_gravity="center_horizontal|top"
|
||||
android:layout_marginTop="24dp"
|
||||
android:scaleType="center"
|
||||
android:importantForAccessibility="no"
|
||||
android:src="@drawable/splash_logo"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom"
|
||||
android:padding="16dp"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:orientation="vertical">
|
||||
<Button
|
||||
android:id="@+id/btn_get_started"
|
||||
|
||||
<androidx.viewpager2.widget.ViewPager2
|
||||
android:id="@+id/pager"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/pager_dots"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="13dp"
|
||||
style="@style/Widget.Mastodon.Button.Large.Primary_LightOnDark"
|
||||
android:text="@string/get_started"/>
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<View
|
||||
android:layout_width="8dp"
|
||||
android:layout_height="8dp"
|
||||
android:background="@drawable/white_circle"/>
|
||||
|
||||
<View
|
||||
android:layout_width="8dp"
|
||||
android:layout_height="8dp"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginRight="16dp"
|
||||
android:alpha="0.3"
|
||||
android:background="@drawable/white_circle"/>
|
||||
|
||||
<View
|
||||
android:layout_width="8dp"
|
||||
android:layout_height="8dp"
|
||||
android:alpha="0.3"
|
||||
android:background="@drawable/white_circle"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_log_in"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/Widget.Mastodon.Button.Large.Primary_DarkOnLight"
|
||||
android:background="@drawable/bg_button_green"
|
||||
android:text="@string/log_in"/>
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginRight="16dp"
|
||||
style="@style/Widget.Mastodon.M3.Button.Text"
|
||||
android:textColor="#fff"
|
||||
android:text="@string/already_have_account"/>
|
||||
<Button
|
||||
android:id="@+id/btn_get_started"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginRight="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
style="@style/Widget.Mastodon.M3.Button.Filled"
|
||||
android:text="@string/get_started"/>
|
||||
</LinearLayout>
|
||||
|
||||
</org.joinmastodon.android.ui.views.SizeListenerFrameLayout>
|
||||
59
mastodon/src/main/res/layout/header_megalodon_welcome.xml
Normal file
59
mastodon/src/main/res/layout/header_megalodon_welcome.xml
Normal file
@@ -0,0 +1,59 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<include layout="@layout/display_item_header" />
|
||||
|
||||
<TextView
|
||||
style="@style/m3_headline_small"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
android:layout_marginVertical="12dp"
|
||||
android:text="@string/sk_welcome_title"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
style="@style/m3_body_large"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:text="@string/sk_welcome_text" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0.5dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:background="?attr/colorPollVoted"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/search_edit"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
android:inputType="textFilter|textNoSuggestions"
|
||||
android:singleLine="true"
|
||||
android:imeOptions="actionGo"
|
||||
android:drawableStart="@drawable/ic_fluent_globe_20_regular"
|
||||
android:drawablePadding="12dp"
|
||||
android:drawableTint="?android:textColorSecondary"
|
||||
android:background="@drawable/bg_search_field"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:elevation="0dp"
|
||||
android:hint="@string/sk_example_domain"/>
|
||||
|
||||
|
||||
<ViewStub
|
||||
android:layout="?errorViewLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/error"
|
||||
android:visibility="gone" />
|
||||
|
||||
</LinearLayout>
|
||||
71
mastodon/src/main/res/layout/header_onboarding_login.xml
Normal file
71
mastodon/src/main/res/layout/header_onboarding_login.xml
Normal file
@@ -0,0 +1,71 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="@style/m3_body_large"
|
||||
android:paddingStart="56dp"
|
||||
android:paddingEnd="24dp"
|
||||
android:text="@string/login_subtitle"/>
|
||||
|
||||
<org.joinmastodon.android.ui.views.FloatingHintEditTextLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginRight="16dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="8dp"
|
||||
app:editTextOffsetY="8dp"
|
||||
android:background="@drawable/rect_4dp"
|
||||
android:backgroundTint="?colorM3SurfaceVariant">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/search_edit"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@null"
|
||||
android:stateListAnimator="@null"
|
||||
android:textColor="?colorM3OnSurfaceVariant"
|
||||
android:textColorHint="?colorM3OnSurfaceVariant"
|
||||
android:inputType="textUri"
|
||||
android:importantForAutofill="no"
|
||||
android:paddingStart="48dp"
|
||||
android:layout_marginEnd="52dp"
|
||||
android:drawablePadding="16dp"
|
||||
android:textAppearance="@style/m3_body_large"
|
||||
android:hint="@string/server_url"/>
|
||||
|
||||
<ImageView
|
||||
android:layout_width="44dp"
|
||||
android:layout_height="20dp"
|
||||
android:layout_gravity="start|center_vertical"
|
||||
android:scaleType="center"
|
||||
android:importantForAccessibility="no"
|
||||
android:tint="?colorM3OnSurfaceVariant"
|
||||
android:src="@drawable/ic_m3_search"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/search_clear"
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="36dp"
|
||||
android:layout_marginEnd="6dp"
|
||||
android:background="?android:selectableItemBackgroundBorderless"
|
||||
android:layout_gravity="center_vertical|end"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"
|
||||
android:contentDescription="@string/clear"
|
||||
android:stateListAnimator="@null"
|
||||
android:elevation="0dp"
|
||||
android:src="@drawable/ic_m3_cancel"/>
|
||||
|
||||
</org.joinmastodon.android.ui.views.FloatingHintEditTextLayout>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -174,7 +174,7 @@
|
||||
style="?secondaryButtonStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription="@string/reject_follow_request"
|
||||
android:contentDescription="@string/sk_reject_follow_request"
|
||||
android:drawableStart="@drawable/ic_fluent_dismiss_24_filled"
|
||||
android:singleLine="true" />
|
||||
|
||||
@@ -204,7 +204,7 @@
|
||||
android:id="@+id/accept_btn"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription="@string/accept_follow_request"
|
||||
android:contentDescription="@string/sk_accept_follow_request"
|
||||
android:drawableStart="@drawable/ic_fluent_checkmark_24_filled"
|
||||
android:singleLine="true" />
|
||||
|
||||
|
||||
53
mastodon/src/main/res/layout/item_instance_login.xml
Normal file
53
mastodon/src/main/res/layout/item_instance_login.xml
Normal file
@@ -0,0 +1,53 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="24dp"
|
||||
android:paddingTop="12dp"
|
||||
android:paddingBottom="12dp"
|
||||
android:background="?colorM3SurfaceVariant">
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/radiobtn"
|
||||
android:layout_width="28dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_marginEnd="23dp"
|
||||
android:layout_marginStart="-3dp"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:buttonTint="@color/m3_radiobutton_tint"
|
||||
android:background="@null"
|
||||
android:focusable="false"
|
||||
android:clickable="false"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_toEndOf="@id/radiobtn"
|
||||
android:layout_alignParentTop="true"
|
||||
android:textAppearance="@style/m3_body_large"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center_vertical"
|
||||
android:textSize="16sp"
|
||||
android:minHeight="24dp"
|
||||
android:textColor="?colorM3OnSurface"
|
||||
tools:text="mastodon.social"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/description"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_toEndOf="@id/radiobtn"
|
||||
android:layout_below="@id/title"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:textAppearance="@style/m3_body_medium"
|
||||
android:textColor="?colorM3OnSurfaceVariant"
|
||||
android:textSize="14sp"
|
||||
android:lineSpacingExtra="4sp"
|
||||
tools:text="General-purpose server run by the lead developer of Mastodon"/>
|
||||
|
||||
</RelativeLayout>
|
||||
14
mastodon/src/main/res/layout/item_megalodon_instance.xml
Normal file
14
mastodon/src/main/res/layout/item_megalodon_instance.xml
Normal file
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:paddingVertical="8dp">
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_search_field">
|
||||
<include layout="@layout/item_instance_catalog" />
|
||||
</FrameLayout>
|
||||
</FrameLayout>
|
||||
46
mastodon/src/main/res/layout/item_settings_color_picker.xml
Normal file
46
mastodon/src/main/res/layout/item_settings_color_picker.xml
Normal file
@@ -0,0 +1,46 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<org.joinmastodon.android.ui.views.AutoOrientationLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:gravity="center_vertical"
|
||||
android:layoutDirection="locale">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/icon"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="32dp"
|
||||
android:importantForAccessibility="no"
|
||||
android:tint="?android:textColorPrimary"
|
||||
tools:src="@drawable/ic_color_theme_preference"/>
|
||||
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:gravity="center_vertical"
|
||||
android:textColor="?android:textColorPrimary"
|
||||
android:textSize="16sp"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="end"
|
||||
android:text="@string/sk_settings_color_picker"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/color_picker_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="32dp"
|
||||
android:background="@drawable/bg_inline_button"
|
||||
android:elevation="0dp"
|
||||
android:ellipsize="middle"
|
||||
android:fontFamily="sans-serif-medium"
|
||||
android:singleLine="true"
|
||||
android:stateListAnimator="@null"
|
||||
android:textColor="?android:textColorPrimary"
|
||||
android:textSize="16sp"
|
||||
tools:text="@string/sk_color_theme_pink" />
|
||||
|
||||
</org.joinmastodon.android.ui.views.AutoOrientationLinearLayout>
|
||||
@@ -23,7 +23,7 @@
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:textAppearance="@style/m3_body_medium"
|
||||
tools:text="@string/update_available"/>
|
||||
tools:text="@string/sk_update_available"/>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="wrap_content"
|
||||
|
||||
9
mastodon/src/main/res/menu/color_picker.xml
Normal file
9
mastodon/src/main/res/menu/color_picker.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:id="@+id/pink_color" android:title="@string/sk_color_theme_pink"/>
|
||||
<item android:id="@+id/purple_color" android:title="@string/sk_color_theme_purple"/>
|
||||
<item android:id="@+id/green_color" android:title="@string/sk_color_theme_green"/>
|
||||
<item android:id="@+id/blue_color" android:title="@string/sk_color_theme_blue"/>
|
||||
<item android:id="@+id/brown_color" android:title="@string/sk_color_theme_brown"/>
|
||||
<item android:id="@+id/yellow_color" android:title="@string/sk_color_theme_yellow"/>
|
||||
</menu>
|
||||
@@ -5,7 +5,7 @@
|
||||
android:title="@string/visibility_public"/>
|
||||
<item android:id="@+id/vis_unlisted"
|
||||
android:icon="@drawable/ic_fluent_people_community_24_regular"
|
||||
android:title="@string/visibility_unlisted"/>
|
||||
android:title="@string/sk_visibility_unlisted"/>
|
||||
<item android:id="@+id/vis_followers"
|
||||
android:icon="@drawable/ic_fluent_people_checkmark_24_regular"
|
||||
android:title="@string/visibility_followers_only"/>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user