Compare commits
294 Commits
1.1.4+fork
...
1.1.4+fork
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
024d358213 | ||
|
|
5562c93855 | ||
|
|
98e897d6a8 | ||
|
|
4aac6aa4f4 | ||
|
|
2bb4616e40 | ||
|
|
56e8476d2e | ||
|
|
97d81eb1b2 | ||
|
|
ffa21b26af | ||
|
|
9917712f66 | ||
|
|
11cdce6c90 | ||
|
|
8e82cf1e99 | ||
|
|
9767b11626 | ||
|
|
0f95694083 | ||
|
|
7dfc7dd9ef | ||
|
|
0407e958f1 | ||
|
|
e6a5fa1c3f | ||
|
|
6f48a7c4a4 | ||
|
|
80c56d71cb | ||
|
|
f77d9dcee2 | ||
|
|
f7195c7787 | ||
|
|
ca92cc6dc1 | ||
|
|
cd31b2ae5a | ||
|
|
00bec7174a | ||
|
|
236acab54f | ||
|
|
ba362f4457 | ||
|
|
8ed93baf8d | ||
|
|
bf953e96fa | ||
|
|
6b89a747e2 | ||
|
|
2fa1d54268 | ||
|
|
02ef34b451 | ||
|
|
1701fc71c4 | ||
|
|
fe200996db | ||
|
|
659333342f | ||
|
|
1ca5b6def2 | ||
|
|
4e8e3ee440 | ||
|
|
86dd724222 | ||
|
|
8242995027 | ||
|
|
49962a4734 | ||
|
|
509b16aee1 | ||
|
|
f3f5e4a887 | ||
|
|
7aabc1fa76 | ||
|
|
dcb5e36041 | ||
|
|
e0c072ab9c | ||
|
|
0231903868 | ||
|
|
f63bbeee79 | ||
|
|
db9e427444 | ||
|
|
4474a584df | ||
|
|
ab00ad68f1 | ||
|
|
d1e77efa1c | ||
|
|
de00353864 | ||
|
|
feec459d47 | ||
|
|
ad68d7e4f2 | ||
|
|
cf27c6bbf3 | ||
|
|
0115656d67 | ||
|
|
002687d2b1 | ||
|
|
a3267f6cd3 | ||
|
|
0ca9c536cd | ||
|
|
382a23c0b6 | ||
|
|
1f51331f67 | ||
|
|
cce6ba0746 | ||
|
|
be3c12dfb3 | ||
|
|
bfd87cf94e | ||
|
|
857bb1e483 | ||
|
|
75a131b675 | ||
|
|
d98b1c5ee1 | ||
|
|
1eeab25b7d | ||
|
|
82cc0c3c09 | ||
|
|
e102faff6c | ||
|
|
34369bd7e9 | ||
|
|
c71b620402 | ||
|
|
21b4bf23a1 | ||
|
|
d034311f2d | ||
|
|
2deed69766 | ||
|
|
bfbd21b826 | ||
|
|
ba8683301d | ||
|
|
0ed178167b | ||
|
|
b34e34de51 | ||
|
|
ba38e21e07 | ||
|
|
90bef7fddb | ||
|
|
c1b382ef34 | ||
|
|
028b88aa24 | ||
|
|
9d0ce33f5e | ||
|
|
dbb23d952c | ||
|
|
7fe7e47d53 | ||
|
|
d0c93dfd4d | ||
|
|
acdccaf80a | ||
|
|
769293ce1a | ||
|
|
8d0fe18b70 | ||
|
|
6926432a6c | ||
|
|
83f12b0840 | ||
|
|
290b7db7e4 | ||
|
|
f352c20ed9 | ||
|
|
2ccbffa165 | ||
|
|
06cd80a352 | ||
|
|
de97493e6a | ||
|
|
3a24ff0d15 | ||
|
|
c463a3fc39 | ||
|
|
fc845685cc | ||
|
|
0ef0aa1a44 | ||
|
|
337689aa45 | ||
|
|
f7e3423f9c | ||
|
|
b465c09cc8 | ||
|
|
06f2f67f0c | ||
|
|
05c33be3f4 | ||
|
|
ac6c0651d6 | ||
|
|
e274b7e6d5 | ||
|
|
0806d0c5ea | ||
|
|
5c67dd0188 | ||
|
|
b4358f51cb | ||
|
|
622c6d503d | ||
|
|
b190480d77 | ||
|
|
9a085beea8 | ||
|
|
1a42a77e24 | ||
|
|
e35794ef7d | ||
|
|
1f9611fc3e | ||
|
|
563afd487c | ||
|
|
e10faeefc4 | ||
|
|
65dbbb3d61 | ||
|
|
fa69868ca1 | ||
|
|
9c18de7b90 | ||
|
|
61bd19f6ff | ||
|
|
ba0689aef7 | ||
|
|
ad54e6bb4b | ||
|
|
f15fcb43da | ||
|
|
4e5c2a9ecf | ||
|
|
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: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
41
README.md
41
README.md
@@ -1,10 +1,9 @@
|
|||||||

|

|
||||||
|
|
||||||
# Moshidon
|
# Moshidon, the material you mastodon client!
|
||||||
|
|
||||||
> A fork of [megalodon](https://github.com/sk22/megalodon) which is a fork of [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.
|
> A fork of [megalodon](https://github.com/sk22/megalodon) which is a fork of [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.
|
||||||
|
|
||||||
[//]: # (**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!**)
|
|
||||||
|
|
||||||
[](https://github.com/LucasGGamerM/moshidon/releases/latest/download/moshidon.apk)
|
[](https://github.com/LucasGGamerM/moshidon/releases/latest/download/moshidon.apk)
|
||||||
|
|
||||||
@@ -13,6 +12,17 @@
|
|||||||
|
|
||||||
## Key features
|
## Key features
|
||||||
|
|
||||||
|
### **Material you theme support on Android 12+ devices!**
|
||||||
|
|
||||||
|
### **Translate button**
|
||||||
|
|
||||||
|
**Allows you to translate posts in instances with the translate feature!**
|
||||||
|
|
||||||
|
**Screenshots**
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
### **Color themes**
|
### **Color themes**
|
||||||
|
|
||||||
**Allows you to change theme within the app. Supports Purple, pink, green, blue, orange and yellow!**
|
**Allows you to change theme within the app. Supports Purple, pink, green, blue, orange and yellow!**
|
||||||
@@ -53,34 +63,21 @@ To bookmark a post, press the button between the Favorite and Share buttons on t
|
|||||||
|
|
||||||
## Installation
|
## 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.**
|
**Press the download button above to download the APK. Open the downloaded file on your Android device to install it. Moshidon will automatically notify you about new updates inside the app.**
|
||||||
|
|
||||||
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.
|
To install this app on your Android device, download the [latest release from GitHub](https://github.com/LucasGGamerM/moshidon/releases/latest/download/moshidon.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/LucasGGamerM/moshidon/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!
|
Moshidon 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
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
## Release variants
|
## Release variants
|
||||||
|
|
||||||
All downloads can be found on the [Releases](https://github.com/sk22/megalodon/releases) page.
|
All downloads can be found on the [Releases](https://github.com/LucasGGamerM/moshidon/releases) page.
|
||||||
|
|
||||||
**`megalodon.apk`**
|
**`moshidon.apk`**
|
||||||
|
|
||||||
Variant with an integrated updater. If you download Megalodon from here (and not from an app store), just download the regular `megalodon.apk`.
|
Variant with an integrated updater. If you download Moshidon from here (and not from an app store), just download the regular `moshidon.apk`.
|
||||||
|
|
||||||
**`upstream-1234abc.apk`**
|
|
||||||
|
|
||||||
This is an **unmodified version** of the official [Mastodon for Android](https://github.com/mastodon/mastodon-android) app the respective Megalodon release is based on. Should you find any bugs in Megalodon (which you will), try to see if it occurs with this variant, too. The last 7 digits of the file name are important to know which version of the official app you're using.
|
|
||||||
|
|
||||||
<!-- **`megalodon-fdroid.apk`**
|
|
||||||
|
|
||||||
Variant without the integrated updater. This is the variant to be published to F-Droid.org where an integrated updater is not necessary. -->
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -143,4 +140,4 @@ This project is released under the [GPL-3 License](./LICENSE).
|
|||||||
|
|
||||||
## Links
|
## Links
|
||||||
|
|
||||||
<a rel="me" href="https://floss.social/@megalodon">@megalodon<wbr>@floss.social</a>
|
<a rel="me" href="https://floss.social/@moshidon">@moshidon<wbr>@floss.social</a>
|
||||||
|
|||||||
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.moshinda"
|
applicationId "org.joinmastodon.android.moshinda"
|
||||||
minSdk 23
|
minSdk 23
|
||||||
targetSdk 33
|
targetSdk 33
|
||||||
versionCode 57
|
versionCode 65
|
||||||
versionName "1.1.4+fork.57.moshinda"
|
versionName "1.1.4+fork.65.moshinda"
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
resConfigs "en", "ar-rSA", "bs-rBA", "ca-rES", "cs-rCZ", "de-rDE", "el-rGR", "es-rES",
|
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",
|
"eu-rES", "fi-rFI", "fr-rFR", "gl-rES", "hr-rHR", "hy-rAM", "it-rIT", "iw-rIL",
|
||||||
@@ -32,9 +32,11 @@ android {
|
|||||||
githubRelease{
|
githubRelease{
|
||||||
initWith release
|
initWith release
|
||||||
}
|
}
|
||||||
noFederatedRelease{
|
playRelease{
|
||||||
initWith release
|
initWith release
|
||||||
versionNameSuffix '-nofederated'
|
minifyEnabled true
|
||||||
|
shrinkResources true
|
||||||
|
versionNameSuffix '-play'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
compileOptions {
|
compileOptions {
|
||||||
|
|||||||
@@ -33,7 +33,7 @@
|
|||||||
<action android:name="android.intent.action.VIEW"/>
|
<action android:name="android.intent.action.VIEW"/>
|
||||||
<category android:name="android.intent.category.BROWSABLE"/>
|
<category android:name="android.intent.category.BROWSABLE"/>
|
||||||
<category android:name="android.intent.category.DEFAULT"/>
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
<data android:scheme="megalodon-android-auth" android:host="callback"/>
|
<data android:scheme="moshidon-android-auth" android:host="callback"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<activity android:name=".ExternalShareActivity" android:exported="true" android:configChanges="orientation|screenSize" android:windowSoftInputMode="adjustResize">
|
<activity android:name=".ExternalShareActivity" android:exported="true" android:configChanges="orientation|screenSize" android:windowSoftInputMode="adjustResize">
|
||||||
|
|||||||
@@ -51,7 +51,10 @@ public class ExternalShareActivity extends FragmentStackActivity{
|
|||||||
getWindow().setBackgroundDrawable(null);
|
getWindow().setBackgroundDrawable(null);
|
||||||
|
|
||||||
Intent intent=getIntent();
|
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;
|
List<Uri> mediaUris;
|
||||||
if(Intent.ACTION_SEND.equals(intent.getAction())){
|
if(Intent.ACTION_SEND.equals(intent.getAction())){
|
||||||
Uri singleUri=intent.getParcelableExtra(Intent.EXTRA_STREAM);
|
Uri singleUri=intent.getParcelableExtra(Intent.EXTRA_STREAM);
|
||||||
|
|||||||
@@ -10,9 +10,11 @@ public class GlobalUserPreferences{
|
|||||||
public static boolean showReplies;
|
public static boolean showReplies;
|
||||||
public static boolean showBoosts;
|
public static boolean showBoosts;
|
||||||
public static boolean loadNewPosts;
|
public static boolean loadNewPosts;
|
||||||
|
public static boolean showFederatedTimeline;
|
||||||
public static boolean showInteractionCounts;
|
public static boolean showInteractionCounts;
|
||||||
public static boolean alwaysExpandContentWarnings;
|
public static boolean alwaysExpandContentWarnings;
|
||||||
public static boolean disableMarquee;
|
public static boolean disableMarquee;
|
||||||
|
public static boolean voteButtonForSingleChoice;
|
||||||
public static ThemePreference theme;
|
public static ThemePreference theme;
|
||||||
public static ColorPreference color;
|
public static ColorPreference color;
|
||||||
|
|
||||||
@@ -28,9 +30,11 @@ public class GlobalUserPreferences{
|
|||||||
showReplies=prefs.getBoolean("showReplies", true);
|
showReplies=prefs.getBoolean("showReplies", true);
|
||||||
showBoosts=prefs.getBoolean("showBoosts", true);
|
showBoosts=prefs.getBoolean("showBoosts", true);
|
||||||
loadNewPosts=prefs.getBoolean("loadNewPosts", true);
|
loadNewPosts=prefs.getBoolean("loadNewPosts", true);
|
||||||
|
showFederatedTimeline=prefs.getBoolean("showFederatedTimeline", !BuildConfig.BUILD_TYPE.equals("playRelease"));
|
||||||
showInteractionCounts=prefs.getBoolean("showInteractionCounts", false);
|
showInteractionCounts=prefs.getBoolean("showInteractionCounts", false);
|
||||||
alwaysExpandContentWarnings=prefs.getBoolean("alwaysExpandContentWarnings", false);
|
alwaysExpandContentWarnings=prefs.getBoolean("alwaysExpandContentWarnings", false);
|
||||||
disableMarquee=prefs.getBoolean("disableMarquee", false);
|
disableMarquee=prefs.getBoolean("disableMarquee", false);
|
||||||
|
voteButtonForSingleChoice=prefs.getBoolean("voteButtonForSingleChoice", true);
|
||||||
theme=ThemePreference.values()[prefs.getInt("theme", 0)];
|
theme=ThemePreference.values()[prefs.getInt("theme", 0)];
|
||||||
color=ColorPreference.values()[prefs.getInt("color", 1)];
|
color=ColorPreference.values()[prefs.getInt("color", 1)];
|
||||||
}
|
}
|
||||||
@@ -42,6 +46,7 @@ public class GlobalUserPreferences{
|
|||||||
.putBoolean("showReplies", showReplies)
|
.putBoolean("showReplies", showReplies)
|
||||||
.putBoolean("showBoosts", showBoosts)
|
.putBoolean("showBoosts", showBoosts)
|
||||||
.putBoolean("loadNewPosts", loadNewPosts)
|
.putBoolean("loadNewPosts", loadNewPosts)
|
||||||
|
.putBoolean("showFederatedTimeline", showFederatedTimeline)
|
||||||
.putBoolean("trueBlackTheme", trueBlackTheme)
|
.putBoolean("trueBlackTheme", trueBlackTheme)
|
||||||
.putBoolean("showInteractionCounts", showInteractionCounts)
|
.putBoolean("showInteractionCounts", showInteractionCounts)
|
||||||
.putBoolean("alwaysExpandContentWarnings", alwaysExpandContentWarnings)
|
.putBoolean("alwaysExpandContentWarnings", alwaysExpandContentWarnings)
|
||||||
@@ -57,7 +62,8 @@ public class GlobalUserPreferences{
|
|||||||
GREEN,
|
GREEN,
|
||||||
BLUE,
|
BLUE,
|
||||||
ORANGE,
|
ORANGE,
|
||||||
YELLOW
|
YELLOW,
|
||||||
|
MATERIAL3
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum ThemePreference{
|
public enum ThemePreference{
|
||||||
@@ -66,4 +72,3 @@ public class GlobalUserPreferences{
|
|||||||
DARK
|
DARK
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,9 +14,9 @@ import org.joinmastodon.android.api.session.AccountSessionManager;
|
|||||||
import org.joinmastodon.android.fragments.ComposeFragment;
|
import org.joinmastodon.android.fragments.ComposeFragment;
|
||||||
import org.joinmastodon.android.fragments.HomeFragment;
|
import org.joinmastodon.android.fragments.HomeFragment;
|
||||||
import org.joinmastodon.android.fragments.ProfileFragment;
|
import org.joinmastodon.android.fragments.ProfileFragment;
|
||||||
import org.joinmastodon.android.fragments.SplashFragment;
|
|
||||||
import org.joinmastodon.android.fragments.ThreadFragment;
|
import org.joinmastodon.android.fragments.ThreadFragment;
|
||||||
import org.joinmastodon.android.fragments.onboarding.AccountActivationFragment;
|
import org.joinmastodon.android.fragments.onboarding.AccountActivationFragment;
|
||||||
|
import org.joinmastodon.android.fragments.onboarding.MoshidonLoginFragment;
|
||||||
import org.joinmastodon.android.model.Notification;
|
import org.joinmastodon.android.model.Notification;
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
import org.joinmastodon.android.updater.GithubSelfUpdater;
|
import org.joinmastodon.android.updater.GithubSelfUpdater;
|
||||||
@@ -33,7 +33,7 @@ public class MainActivity extends FragmentStackActivity{
|
|||||||
|
|
||||||
if(savedInstanceState==null){
|
if(savedInstanceState==null){
|
||||||
if(AccountSessionManager.getInstance().getLoggedInAccounts().isEmpty()){
|
if(AccountSessionManager.getInstance().getLoggedInAccounts().isEmpty()){
|
||||||
showFragmentClearingBackStack(new SplashFragment());
|
showFragmentClearingBackStack(new MoshidonLoginFragment());
|
||||||
}else{
|
}else{
|
||||||
AccountSessionManager.getInstance().maybeUpdateLocalInfo();
|
AccountSessionManager.getInstance().maybeUpdateLocalInfo();
|
||||||
AccountSession session;
|
AccountSession session;
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ public class PushNotificationReceiver extends BroadcastReceiver{
|
|||||||
.setShowWhen(true)
|
.setShowWhen(true)
|
||||||
.setCategory(Notification.CATEGORY_SOCIAL)
|
.setCategory(Notification.CATEGORY_SOCIAL)
|
||||||
.setAutoCancel(true)
|
.setAutoCancel(true)
|
||||||
.setColor(context.getColor(R.color.primary_700));
|
.setColor(context.getColor(R.color.shortcut_icon_background));
|
||||||
if(avatar!=null){
|
if(avatar!=null){
|
||||||
builder.setLargeIcon(UiUtils.getBitmapFromDrawable(avatar));
|
builder.setLargeIcon(UiUtils.getBitmapFromDrawable(avatar));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package org.joinmastodon.android.api.requests.lists;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class AddList extends MastodonAPIRequest<Object> {
|
||||||
|
public AddList(String listName){
|
||||||
|
super(HttpMethod.POST, "/lists", Object.class);
|
||||||
|
Request req = new Request();
|
||||||
|
req.title = listName;
|
||||||
|
setRequestBody(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Request{
|
||||||
|
public String title;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package org.joinmastodon.android.api.requests.lists;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class EditListName extends MastodonAPIRequest<Object> {
|
||||||
|
public EditListName(String newListName, String listId){
|
||||||
|
super(HttpMethod.PUT, "/lists/"+listId, Object.class);
|
||||||
|
Request req = new Request();
|
||||||
|
req.title = newListName;
|
||||||
|
setRequestBody(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Request{
|
||||||
|
public String title;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package org.joinmastodon.android.api.requests.lists;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class RemoveList extends MastodonAPIRequest<Object> {
|
||||||
|
public RemoveList(String listId){
|
||||||
|
super(HttpMethod.DELETE, "/lists/"+listId, Object.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,9 +11,9 @@ public class CreateOAuthApp extends MastodonAPIRequest<Application>{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static class Request{
|
private static class Request{
|
||||||
public String clientName="Megalodon";
|
public String clientName="Moshidon";
|
||||||
public String redirectUris=AccountSessionManager.REDIRECT_URI;
|
public String redirectUris=AccountSessionManager.REDIRECT_URI;
|
||||||
public String scopes=AccountSessionManager.SCOPE;
|
public String scopes=AccountSessionManager.SCOPE;
|
||||||
public String website="https://sk22.github.io/megalodon";
|
public String website="https://github.com/LucasGGamerM/moshidon";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package org.joinmastodon.android.api.requests.statuses;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.api.AllFieldsAreRequired;
|
||||||
|
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||||
|
import org.joinmastodon.android.model.BaseModel;
|
||||||
|
import org.joinmastodon.android.model.Status;
|
||||||
|
import org.joinmastodon.android.model.StatusTranslation;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
public class GetStatusTranslation extends MastodonAPIRequest<StatusTranslation>{
|
||||||
|
public GetStatusTranslation(String id){
|
||||||
|
super(HttpMethod.POST, "/statuses/"+id+"/translate", StatusTranslation.class);
|
||||||
|
Request r = new Request();
|
||||||
|
setRequestBody(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Request{
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -61,7 +61,7 @@ import me.grishka.appkit.api.ErrorResponse;
|
|||||||
public class AccountSessionManager{
|
public class AccountSessionManager{
|
||||||
private static final String TAG="AccountSessionManager";
|
private static final String TAG="AccountSessionManager";
|
||||||
public static final String SCOPE="read write follow push";
|
public static final String SCOPE="read write follow push";
|
||||||
public static final String REDIRECT_URI="megalodon-android-auth://callback";
|
public static final String REDIRECT_URI="moshidon-android-auth://callback";
|
||||||
|
|
||||||
private static final AccountSessionManager instance=new AccountSessionManager();
|
private static final AccountSessionManager instance=new AccountSessionManager();
|
||||||
|
|
||||||
@@ -211,7 +211,7 @@ public class AccountSessionManager{
|
|||||||
.path("/oauth/authorize")
|
.path("/oauth/authorize")
|
||||||
.appendQueryParameter("response_type", "code")
|
.appendQueryParameter("response_type", "code")
|
||||||
.appendQueryParameter("client_id", result.clientId)
|
.appendQueryParameter("client_id", result.clientId)
|
||||||
.appendQueryParameter("redirect_uri", "megalodon-android-auth://callback")
|
.appendQueryParameter("redirect_uri", "moshidon-android-auth://callback")
|
||||||
.appendQueryParameter("scope", SCOPE)
|
.appendQueryParameter("scope", SCOPE)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
|||||||
@@ -400,10 +400,12 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
|||||||
public void onPollOptionClick(PollOptionStatusDisplayItem.Holder holder){
|
public void onPollOptionClick(PollOptionStatusDisplayItem.Holder holder){
|
||||||
Poll poll=holder.getItem().poll;
|
Poll poll=holder.getItem().poll;
|
||||||
Poll.Option option=holder.getItem().option;
|
Poll.Option option=holder.getItem().option;
|
||||||
if(poll.multiple){
|
if(poll.multiple || GlobalUserPreferences.voteButtonForSingleChoice){
|
||||||
if(poll.selectedOptions==null)
|
if(poll.selectedOptions==null)
|
||||||
poll.selectedOptions=new ArrayList<>();
|
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);
|
poll.selectedOptions.remove(option);
|
||||||
holder.itemView.setSelected(false);
|
holder.itemView.setSelected(false);
|
||||||
}else{
|
}else{
|
||||||
@@ -412,6 +414,9 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
|||||||
}
|
}
|
||||||
for(int i=0;i<list.getChildCount();i++){
|
for(int i=0;i<list.getChildCount();i++){
|
||||||
RecyclerView.ViewHolder vh=list.getChildViewHolder(list.getChildAt(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(vh instanceof PollFooterStatusDisplayItem.Holder footer){
|
||||||
if(footer.getItemID().equals(holder.getItemID())){
|
if(footer.getItemID().equals(holder.getItemID())){
|
||||||
footer.rebind();
|
footer.rebind();
|
||||||
@@ -457,6 +462,21 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
|||||||
Status status=holder.getItem().status;
|
Status status=holder.getItem().status;
|
||||||
revealSpoiler(status, holder.getItemID());
|
revealSpoiler(status, holder.getItemID());
|
||||||
}
|
}
|
||||||
|
public void onRevealTranslationClick(HeaderStatusDisplayItem.Holder holder){
|
||||||
|
Status status=holder.getItem().status;
|
||||||
|
revealTranslation(status, holder.getItemID());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void revealTranslation(Status status, String itemID){
|
||||||
|
status.wantsTranslation=!status.wantsTranslation;
|
||||||
|
TextStatusDisplayItem.Holder text=findHolderOfType(itemID, TextStatusDisplayItem.Holder.class);
|
||||||
|
if(text!=null)
|
||||||
|
adapter.notifyItemChanged(text.getAbsoluteAdapterPosition()-getMainAdapterOffset());
|
||||||
|
HeaderStatusDisplayItem.Holder header=findHolderOfType(itemID, HeaderStatusDisplayItem.Holder.class);
|
||||||
|
if(header!=null)
|
||||||
|
header.rebind();
|
||||||
|
updateImagesSpoilerState(status, itemID);
|
||||||
|
}
|
||||||
|
|
||||||
protected void revealSpoiler(Status status, String itemID){
|
protected void revealSpoiler(Status status, String itemID){
|
||||||
status.spoilerRevealed=true;
|
status.spoilerRevealed=true;
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ import android.view.WindowManager;
|
|||||||
import android.view.animation.LinearInterpolator;
|
import android.view.animation.LinearInterpolator;
|
||||||
import android.view.inputmethod.InputMethodManager;
|
import android.view.inputmethod.InputMethodManager;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
|
import android.widget.CheckBox;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
import android.widget.ImageButton;
|
import android.widget.ImageButton;
|
||||||
@@ -152,6 +153,8 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
private ReorderableLinearLayout pollOptionsView;
|
private ReorderableLinearLayout pollOptionsView;
|
||||||
private View pollWrap;
|
private View pollWrap;
|
||||||
private View addPollOptionBtn;
|
private View addPollOptionBtn;
|
||||||
|
private View pollAllowMultipleItem;
|
||||||
|
private CheckBox pollAllowMultipleCheckbox;
|
||||||
private View sensitiveItem;
|
private View sensitiveItem;
|
||||||
private TextView pollDurationView;
|
private TextView pollDurationView;
|
||||||
|
|
||||||
@@ -297,6 +300,9 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
pollOptionsView=view.findViewById(R.id.poll_options);
|
pollOptionsView=view.findViewById(R.id.poll_options);
|
||||||
pollWrap=view.findViewById(R.id.poll_wrap);
|
pollWrap=view.findViewById(R.id.poll_wrap);
|
||||||
addPollOptionBtn=view.findViewById(R.id.add_poll_option);
|
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->{
|
addPollOptionBtn.setOnClickListener(v->{
|
||||||
createDraftPollOption().edit.requestFocus();
|
createDraftPollOption().edit.requestFocus();
|
||||||
@@ -311,6 +317,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
pollBtn.setSelected(true);
|
pollBtn.setSelected(true);
|
||||||
mediaBtn.setEnabled(false);
|
mediaBtn.setEnabled(false);
|
||||||
pollWrap.setVisibility(View.VISIBLE);
|
pollWrap.setVisibility(View.VISIBLE);
|
||||||
|
updatePollAllowMultiple(savedInstanceState.getBoolean("pollAllowMultiple", false));
|
||||||
for(String oldText:savedInstanceState.getStringArrayList("pollOptions")){
|
for(String oldText:savedInstanceState.getStringArrayList("pollOptions")){
|
||||||
DraftPollOption opt=createDraftPollOption();
|
DraftPollOption opt=createDraftPollOption();
|
||||||
opt.edit.setText(oldText);
|
opt.edit.setText(oldText);
|
||||||
@@ -321,6 +328,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
pollBtn.setSelected(true);
|
pollBtn.setSelected(true);
|
||||||
mediaBtn.setEnabled(false);
|
mediaBtn.setEnabled(false);
|
||||||
pollWrap.setVisibility(View.VISIBLE);
|
pollWrap.setVisibility(View.VISIBLE);
|
||||||
|
updatePollAllowMultiple(editingStatus.poll.multiple);
|
||||||
for(Poll.Option eopt:editingStatus.poll.options){
|
for(Poll.Option eopt:editingStatus.poll.options){
|
||||||
DraftPollOption opt=createDraftPollOption();
|
DraftPollOption opt=createDraftPollOption();
|
||||||
opt.edit.setText(eopt.title);
|
opt.edit.setText(eopt.title);
|
||||||
@@ -391,6 +399,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
outState.putStringArrayList("pollOptions", opts);
|
outState.putStringArrayList("pollOptions", opts);
|
||||||
outState.putInt("pollDuration", pollDuration);
|
outState.putInt("pollDuration", pollDuration);
|
||||||
outState.putString("pollDurationStr", pollDurationStr);
|
outState.putString("pollDurationStr", pollDurationStr);
|
||||||
|
outState.putBoolean("pollAllowMultiple", pollAllowMultipleItem.isSelected());
|
||||||
}
|
}
|
||||||
outState.putBoolean("sensitive", sensitive);
|
outState.putBoolean("sensitive", sensitive);
|
||||||
outState.putBoolean("hasSpoiler", hasSpoiler);
|
outState.putBoolean("hasSpoiler", hasSpoiler);
|
||||||
@@ -487,7 +496,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
replyText.setText(getString(R.string.in_reply_to, replyTo.account.displayName));
|
replyText.setText(getString(R.string.in_reply_to, replyTo.account.displayName));
|
||||||
int visibilityNameRes = switch (statusVisibility) {
|
int visibilityNameRes = switch (statusVisibility) {
|
||||||
case PUBLIC -> R.string.visibility_public;
|
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 PRIVATE -> R.string.visibility_followers_only;
|
||||||
case DIRECT -> R.string.visibility_private;
|
case DIRECT -> R.string.visibility_private;
|
||||||
};
|
};
|
||||||
@@ -527,15 +536,14 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
ignoreSelectionChanges=true;
|
ignoreSelectionChanges=true;
|
||||||
mainEditText.setSelection(mainEditText.length());
|
mainEditText.setSelection(mainEditText.length());
|
||||||
ignoreSelectionChanges=false;
|
ignoreSelectionChanges=false;
|
||||||
if(!TextUtils.isEmpty(replyTo.spoilerText)){
|
if(!TextUtils.isEmpty(replyTo.spoilerText) && AccountSessionManager.getInstance().isSelf(accountID, replyTo.account)){
|
||||||
hasSpoiler=true;
|
hasSpoiler=true;
|
||||||
spoilerEdit.setVisibility(View.VISIBLE);
|
spoilerEdit.setVisibility(View.VISIBLE);
|
||||||
spoilerEdit.setText(replyTo.spoilerText);
|
spoilerEdit.setText(replyTo.spoilerText);
|
||||||
spoilerBtn.setSelected(true);
|
spoilerBtn.setSelected(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}else if (editingStatus==null || editingStatus.inReplyToId==null){
|
}else{
|
||||||
// TODO: remove workaround after https://github.com/mastodon/mastodon-android/issues/341 gets fixed
|
|
||||||
replyText.setVisibility(View.GONE);
|
replyText.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
if(savedInstanceState==null){
|
if(savedInstanceState==null){
|
||||||
@@ -1208,6 +1216,11 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
option.view=LayoutInflater.from(getActivity()).inflate(R.layout.compose_poll_option, pollOptionsView, false);
|
option.view=LayoutInflater.from(getActivity()).inflate(R.layout.compose_poll_option, pollOptionsView, false);
|
||||||
option.edit=option.view.findViewById(R.id.edit);
|
option.edit=option.view.findViewById(R.id.edit);
|
||||||
option.dragger=option.view.findViewById(R.id.dragger_thingy);
|
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->{
|
option.dragger.setOnLongClickListener(v->{
|
||||||
pollOptionsView.startDragging(option.view);
|
pollOptionsView.startDragging(option.view);
|
||||||
@@ -1385,6 +1398,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
|
@Override
|
||||||
public void onSelectionChanged(int start, int end){
|
public void onSelectionChanged(int start, int end){
|
||||||
if(ignoreSelectionChanges)
|
if(ignoreSelectionChanges)
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ public class FollowRequestsListFragment extends BaseRecyclerFragment<FollowReque
|
|||||||
@Override
|
@Override
|
||||||
public void onAttach(Activity activity) {
|
public void onAttach(Activity activity) {
|
||||||
super.onAttach(activity);
|
super.onAttach(activity);
|
||||||
setTitle(R.string.follow_requests);
|
setTitle(R.string.sk_follow_requests);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
|
|||||||
public void onCreate(Bundle savedInstanceState){
|
public void onCreate(Bundle savedInstanceState){
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
accountID=getArguments().getString("account");
|
accountID=getArguments().getString("account");
|
||||||
setTitle(R.string.app_name);
|
setTitle(R.string.sk_app_name);
|
||||||
|
|
||||||
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.N)
|
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.N)
|
||||||
setRetainInstance(true);
|
setRetainInstance(true);
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ public class ListTimelinesFragment extends BaseRecyclerFragment<ListTimeline> im
|
|||||||
if(args.containsKey("profileAccount")){
|
if(args.containsKey("profileAccount")){
|
||||||
profileAccountId=args.getString("profileAccount");
|
profileAccountId=args.getString("profileAccount");
|
||||||
profileDisplayUsername=args.getString("profileDisplayUsername");
|
profileDisplayUsername=args.getString("profileDisplayUsername");
|
||||||
setTitle(getString(R.string.lists_with_user, profileDisplayUsername));
|
setTitle(getString(R.string.sk_lists_with_user, profileDisplayUsername));
|
||||||
// setHasOptionsMenu(true);
|
// setHasOptionsMenu(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -249,7 +249,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
tab.setText(switch(position){
|
tab.setText(switch(position){
|
||||||
case 0 -> R.string.posts;
|
case 0 -> R.string.posts;
|
||||||
case 1 -> R.string.posts_and_replies;
|
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 3 -> R.string.media;
|
||||||
case 4 -> R.string.profile_about;
|
case 4 -> R.string.profile_about;
|
||||||
default -> throw new IllegalStateException();
|
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.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.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.report).setTitle(getString(R.string.report_user, account.getDisplayUsername()));
|
||||||
|
MenuItem manageUserLists=menu.findItem(R.id.manage_user_lists);
|
||||||
if(relationship.following) {
|
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()));
|
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 {
|
}else {
|
||||||
menu.findItem(R.id.hide_boosts).setVisible(false);
|
menu.findItem(R.id.hide_boosts).setVisible(false);
|
||||||
menu.findItem(R.id.manage_user_lists).setVisible(false);
|
manageUserLists.setVisible(false);
|
||||||
}
|
}
|
||||||
if(!account.isLocal())
|
if(!account.isLocal())
|
||||||
menu.findItem(R.id.block_domain).setTitle(getString(relationship.domainBlocking ? R.string.unblock_domain : R.string.block_domain, account.getDomain()));
|
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());
|
notifyProgress.setIndeterminateTintList(notifyButton.getTextColors());
|
||||||
followsYouView.setVisibility(relationship.followedBy ? View.VISIBLE : View.GONE);
|
followsYouView.setVisibility(relationship.followedBy ? View.VISIBLE : View.GONE);
|
||||||
notifyButton.setSelected(relationship.notifying);
|
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){
|
private void onScrollChanged(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY){
|
||||||
|
|||||||
@@ -9,14 +9,12 @@ import android.graphics.Canvas;
|
|||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.provider.Settings;
|
|
||||||
import android.view.Gravity;
|
import android.view.Gravity;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.WindowInsets;
|
import android.view.WindowInsets;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
import android.view.animation.AlphaAnimation;
|
|
||||||
import android.view.animation.LinearInterpolator;
|
import android.view.animation.LinearInterpolator;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.ImageButton;
|
import android.widget.ImageButton;
|
||||||
@@ -71,6 +69,7 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
|||||||
private NotificationPolicyItem notificationPolicyItem;
|
private NotificationPolicyItem notificationPolicyItem;
|
||||||
private String accountID;
|
private String accountID;
|
||||||
private boolean needUpdateNotificationSettings;
|
private boolean needUpdateNotificationSettings;
|
||||||
|
private boolean needAppRestart;
|
||||||
private PushSubscription pushSubscription;
|
private PushSubscription pushSubscription;
|
||||||
|
|
||||||
private ImageView themeTransitionWindowView;
|
private ImageView themeTransitionWindowView;
|
||||||
@@ -111,7 +110,7 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
|||||||
GlobalUserPreferences.useCustomTabs=i.checked;
|
GlobalUserPreferences.useCustomTabs=i.checked;
|
||||||
GlobalUserPreferences.save();
|
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.showInteractionCounts=i.checked;
|
||||||
GlobalUserPreferences.save();
|
GlobalUserPreferences.save();
|
||||||
}));
|
}));
|
||||||
@@ -121,18 +120,23 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
items.add(new HeaderItem(R.string.home_timeline));
|
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.showReplies=i.checked;
|
||||||
GlobalUserPreferences.save();
|
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.showBoosts=i.checked;
|
||||||
GlobalUserPreferences.save();
|
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.loadNewPosts=i.checked;
|
||||||
GlobalUserPreferences.save();
|
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(new HeaderItem(R.string.settings_notifications));
|
||||||
items.add(notificationPolicyItem=new NotificationPolicyItem());
|
items.add(notificationPolicyItem=new NotificationPolicyItem());
|
||||||
@@ -141,6 +145,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_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_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.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 HeaderItem(R.string.settings_boring));
|
||||||
items.add(new TextItem(R.string.settings_account, ()->UiUtils.launchWebBrowser(getActivity(), "https://"+session.domain+"/auth/edit")));
|
items.add(new TextItem(R.string.settings_account, ()->UiUtils.launchWebBrowser(getActivity(), "https://"+session.domain+"/auth/edit")));
|
||||||
@@ -149,7 +154,7 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
|||||||
|
|
||||||
items.add(new RedHeaderItem(R.string.settings_spicy));
|
items.add(new RedHeaderItem(R.string.settings_spicy));
|
||||||
if (GithubSelfUpdater.needSelfUpdating()) {
|
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(checkForUpdateItem);
|
||||||
}
|
}
|
||||||
items.add(new TextItem(R.string.settings_contribute_fork, ()->UiUtils.launchWebBrowser(getActivity(), "https://github.com/LucasGGamerM/moshidon")));
|
items.add(new TextItem(R.string.settings_contribute_fork, ()->UiUtils.launchWebBrowser(getActivity(), "https://github.com/LucasGGamerM/moshidon")));
|
||||||
@@ -204,6 +209,11 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
|||||||
if(needUpdateNotificationSettings && PushSubscriptionManager.arePushNotificationsAvailable()){
|
if(needUpdateNotificationSettings && PushSubscriptionManager.arePushNotificationsAvailable()){
|
||||||
AccountSessionManager.getInstance().getAccount(accountID).getPushSubscriptionManager().updatePushSettings(pushSubscription);
|
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
|
@Override
|
||||||
@@ -227,6 +237,7 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void onColorPreferenceClick(GlobalUserPreferences.ColorPreference color){
|
private void onColorPreferenceClick(GlobalUserPreferences.ColorPreference color){
|
||||||
|
|
||||||
GlobalUserPreferences.color=color;
|
GlobalUserPreferences.color=color;
|
||||||
GlobalUserPreferences.save();
|
GlobalUserPreferences.save();
|
||||||
restartActivityToApplyNewTheme();
|
restartActivityToApplyNewTheme();
|
||||||
@@ -291,6 +302,7 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
|||||||
case FOLLOW -> subscription.alerts.follow=enabled;
|
case FOLLOW -> subscription.alerts.follow=enabled;
|
||||||
case REBLOG -> subscription.alerts.reblog=enabled;
|
case REBLOG -> subscription.alerts.reblog=enabled;
|
||||||
case MENTION -> subscription.alerts.mention=subscription.alerts.poll=enabled;
|
case MENTION -> subscription.alerts.mention=subscription.alerts.poll=enabled;
|
||||||
|
case STATUS -> subscription.alerts.status=enabled;
|
||||||
}
|
}
|
||||||
needUpdateNotificationSettings=true;
|
needUpdateNotificationSettings=true;
|
||||||
}
|
}
|
||||||
@@ -385,7 +397,7 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ev.state == GithubSelfUpdater.UpdateState.NO_UPDATE) {
|
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -693,6 +705,15 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
|||||||
pref = GlobalUserPreferences.ColorPreference.YELLOW;
|
pref = GlobalUserPreferences.ColorPreference.YELLOW;
|
||||||
onColorPreferenceClick(pref);
|
onColorPreferenceClick(pref);
|
||||||
}
|
}
|
||||||
|
else if(id==R.id.m3_color) {
|
||||||
|
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
|
pref = GlobalUserPreferences.ColorPreference.MATERIAL3;
|
||||||
|
onColorPreferenceClick(pref);
|
||||||
|
}else{
|
||||||
|
Toast.makeText(getActivity(), R.string.sk_not_supported,
|
||||||
|
Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
return true;
|
return true;
|
||||||
@@ -706,12 +727,13 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
|||||||
public void onBind(ColorPicker item){
|
public void onBind(ColorPicker item){
|
||||||
icon.setImageResource(R.drawable.ic_color_theme_preference);
|
icon.setImageResource(R.drawable.ic_color_theme_preference);
|
||||||
button.setText(switch(GlobalUserPreferences.color){
|
button.setText(switch(GlobalUserPreferences.color){
|
||||||
case PINK -> R.string.pink_color;
|
case PINK -> R.string.sk_color_theme_pink;
|
||||||
case PURPLE -> R.string.purple_color;
|
case PURPLE -> R.string.sk_color_theme_purple;
|
||||||
case GREEN -> R.string.green_color;
|
case GREEN -> R.string.sk_color_theme_green;
|
||||||
case BLUE -> R.string.blue_color;
|
case BLUE -> R.string.sk_color_theme_blue;
|
||||||
case ORANGE -> R.string.orange_color;
|
case ORANGE -> R.string.sk_color_theme_brown;
|
||||||
case YELLOW -> R.string.yellow_color;
|
case YELLOW -> R.string.sk_color_theme_yellow;
|
||||||
|
case MATERIAL3 -> R.string.sk_color_theme_material_you;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package org.joinmastodon.android.fragments;
|
package org.joinmastodon.android.fragments;
|
||||||
|
|
||||||
import android.content.res.Configuration;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@@ -10,7 +9,8 @@ import android.view.WindowInsets;
|
|||||||
|
|
||||||
import org.joinmastodon.android.MastodonApp;
|
import org.joinmastodon.android.MastodonApp;
|
||||||
import org.joinmastodon.android.R;
|
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.InterpolatingMotionEffect;
|
||||||
import org.joinmastodon.android.ui.views.SizeListenerFrameLayout;
|
import org.joinmastodon.android.ui.views.SizeListenerFrameLayout;
|
||||||
|
|
||||||
@@ -66,8 +66,9 @@ public class SplashFragment extends AppKitFragment{
|
|||||||
|
|
||||||
private void onButtonClick(View v){
|
private void onButtonClick(View v){
|
||||||
Bundle extras=new Bundle();
|
Bundle extras=new Bundle();
|
||||||
extras.putBoolean("signup", v.getId()==R.id.btn_get_started);
|
boolean isSignup=v.getId()==R.id.btn_get_started;
|
||||||
Nav.go(getActivity(), InstanceCatalogFragment.class, extras);
|
extras.putBoolean("signup", isSignup);
|
||||||
|
Nav.go(getActivity(), isSignup ? InstanceCatalogSignupFragment.class : InstanceChooserLoginFragment.class, extras);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateArtSize(int w, int h){
|
private void updateArtSize(int w, int h){
|
||||||
|
|||||||
@@ -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.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.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.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 hideBoosts=menu.findItem(R.id.hide_boosts);
|
||||||
|
MenuItem manageUserLists=menu.findItem(R.id.manage_user_lists);
|
||||||
if(relationship.following){
|
if(relationship.following){
|
||||||
hideBoosts.setTitle(getString(relationship.showingReblogs ? R.string.hide_boosts_from_user : R.string.show_boosts_from_user, account.getDisplayUsername()));
|
hideBoosts.setTitle(getString(relationship.showingReblogs ? R.string.hide_boosts_from_user : R.string.show_boosts_from_user, account.getDisplayUsername()));
|
||||||
hideBoosts.setVisible(true);
|
hideBoosts.setVisible(true);
|
||||||
|
manageUserLists.setTitle(getString(R.string.sk_lists_with_user, account.getDisplayUsername()));
|
||||||
|
manageUserLists.setVisible(true);
|
||||||
}else{
|
}else{
|
||||||
hideBoosts.setVisible(false);
|
hideBoosts.setVisible(false);
|
||||||
|
manageUserLists.setVisible(true);
|
||||||
}
|
}
|
||||||
MenuItem blockDomain=menu.findItem(R.id.block_domain);
|
MenuItem blockDomain=menu.findItem(R.id.block_domain);
|
||||||
if(!account.isLocal()){
|
if(!account.isLocal()){
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import android.widget.ProgressBar;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import org.joinmastodon.android.BuildConfig;
|
import org.joinmastodon.android.BuildConfig;
|
||||||
|
import org.joinmastodon.android.GlobalUserPreferences;
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.fragments.ScrollableToTop;
|
import org.joinmastodon.android.fragments.ScrollableToTop;
|
||||||
import org.joinmastodon.android.fragments.ListTimelinesFragment;
|
import org.joinmastodon.android.fragments.ListTimelinesFragment;
|
||||||
@@ -61,7 +62,7 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
|
|||||||
private String accountID;
|
private String accountID;
|
||||||
private Runnable searchDebouncer=this::onSearchChangedDebounced;
|
private Runnable searchDebouncer=this::onSearchChangedDebounced;
|
||||||
|
|
||||||
private static final boolean noFederated = BuildConfig.BUILD_TYPE.equals("noFederatedRelease");
|
private final boolean noFederated = !GlobalUserPreferences.showFederatedTimeline;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState){
|
public void onCreate(Bundle savedInstanceState){
|
||||||
@@ -87,11 +88,11 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
|
|||||||
tabView.setId(switch(switchIndex){
|
tabView.setId(switch(switchIndex){
|
||||||
case 0 -> R.id.discover_local_timeline;
|
case 0 -> R.id.discover_local_timeline;
|
||||||
case 1 -> R.id.discover_federated_timeline;
|
case 1 -> R.id.discover_federated_timeline;
|
||||||
case 2 -> R.id.discover_hashtags;
|
case 2 -> R.id.discover_lists;
|
||||||
case 3 -> R.id.discover_posts;
|
case 3 -> R.id.discover_hashtags;
|
||||||
case 4 -> R.id.discover_news;
|
case 4 -> R.id.discover_posts;
|
||||||
case 5 -> R.id.discover_users;
|
case 5 -> R.id.discover_news;
|
||||||
case 6 -> R.id.discover_lists;
|
case 6 -> R.id.discover_users;
|
||||||
default -> throw new IllegalStateException("Unexpected value: "+switchIndex);
|
default -> throw new IllegalStateException("Unexpected value: "+switchIndex);
|
||||||
});
|
});
|
||||||
tabView.setVisibility(View.GONE);
|
tabView.setVisibility(View.GONE);
|
||||||
@@ -163,12 +164,13 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
|
|||||||
if (noFederated && position > 0) position++;
|
if (noFederated && position > 0) position++;
|
||||||
tab.setText(switch(position){
|
tab.setText(switch(position){
|
||||||
case 0 -> R.string.local_timeline;
|
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 2 -> R.string.sk_list_timelines;
|
||||||
case 3 -> R.string.posts;
|
case 3 -> R.string.hashtags;
|
||||||
case 4 -> R.string.news;
|
case 4 -> R.string.posts;
|
||||||
case 5 -> R.string.for_you;
|
case 5 -> R.string.news;
|
||||||
case 6 -> R.string.list_timelines;
|
case 6 -> R.string.for_you;
|
||||||
|
|
||||||
default -> throw new IllegalStateException("Unexpected value: "+position);
|
default -> throw new IllegalStateException("Unexpected value: "+position);
|
||||||
});
|
});
|
||||||
tab.view.textView.setAllCaps(true);
|
tab.view.textView.setAllCaps(true);
|
||||||
|
|||||||
@@ -2,46 +2,30 @@ package org.joinmastodon.android.fragments.onboarding;
|
|||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.ProgressDialog;
|
import android.app.ProgressDialog;
|
||||||
import android.content.Context;
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.LocaleList;
|
import android.os.LocaleList;
|
||||||
import android.text.Editable;
|
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.text.TextWatcher;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.WindowInsets;
|
import android.view.WindowInsets;
|
||||||
import android.view.inputmethod.InputMethodManager;
|
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
import android.widget.ImageView;
|
|
||||||
import android.widget.RadioButton;
|
import android.widget.RadioButton;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.MastodonAPIController;
|
import org.joinmastodon.android.api.MastodonAPIController;
|
||||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
|
||||||
import org.joinmastodon.android.api.MastodonErrorResponse;
|
import org.joinmastodon.android.api.MastodonErrorResponse;
|
||||||
import org.joinmastodon.android.api.requests.instance.GetInstance;
|
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.Instance;
|
||||||
import org.joinmastodon.android.model.catalog.CatalogCategory;
|
|
||||||
import org.joinmastodon.android.model.catalog.CatalogInstance;
|
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.M3AlertDialogBuilder;
|
||||||
import org.joinmastodon.android.ui.tabs.TabLayout;
|
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
import org.parceler.Parcels;
|
|
||||||
import org.w3c.dom.Document;
|
import org.w3c.dom.Document;
|
||||||
import org.w3c.dom.Element;
|
import org.w3c.dom.Element;
|
||||||
import org.w3c.dom.Node;
|
|
||||||
import org.w3c.dom.NodeList;
|
import org.w3c.dom.NodeList;
|
||||||
import org.xml.sax.InputSource;
|
import org.xml.sax.InputSource;
|
||||||
|
|
||||||
@@ -59,317 +43,53 @@ import java.util.stream.Collectors;
|
|||||||
import javax.xml.parsers.DocumentBuilderFactory;
|
import javax.xml.parsers.DocumentBuilderFactory;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.recyclerview.widget.DiffUtil;
|
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import me.grishka.appkit.Nav;
|
|
||||||
import me.grishka.appkit.api.Callback;
|
import me.grishka.appkit.api.Callback;
|
||||||
import me.grishka.appkit.api.ErrorResponse;
|
import me.grishka.appkit.api.ErrorResponse;
|
||||||
import me.grishka.appkit.fragments.BaseRecyclerFragment;
|
import me.grishka.appkit.fragments.BaseRecyclerFragment;
|
||||||
import me.grishka.appkit.utils.BindableViewHolder;
|
import me.grishka.appkit.utils.BindableViewHolder;
|
||||||
import me.grishka.appkit.utils.MergeRecyclerAdapter;
|
import me.grishka.appkit.utils.MergeRecyclerAdapter;
|
||||||
import me.grishka.appkit.utils.SingleViewRecyclerAdapter;
|
|
||||||
import me.grishka.appkit.utils.V;
|
import me.grishka.appkit.utils.V;
|
||||||
import me.grishka.appkit.views.UsableRecyclerView;
|
import me.grishka.appkit.views.UsableRecyclerView;
|
||||||
import okhttp3.Call;
|
import okhttp3.Call;
|
||||||
import okhttp3.Request;
|
import okhttp3.Request;
|
||||||
import okhttp3.Response;
|
import okhttp3.Response;
|
||||||
|
|
||||||
public class InstanceCatalogFragment extends BaseRecyclerFragment<CatalogInstance>{
|
abstract class InstanceCatalogFragment extends BaseRecyclerFragment<CatalogInstance>{
|
||||||
private InstancesAdapter adapter;
|
protected RecyclerView.Adapter adapter;
|
||||||
private MergeRecyclerAdapter mergeAdapter;
|
protected MergeRecyclerAdapter mergeAdapter;
|
||||||
private View headerView;
|
protected CatalogInstance chosenInstance;
|
||||||
private CatalogInstance chosenInstance;
|
protected Button nextButton;
|
||||||
private List<CatalogInstance> filteredData=new ArrayList<>();
|
protected EditText searchEdit;
|
||||||
private Button nextButton;
|
protected Runnable searchDebouncer=this::onSearchChangedDebounced;
|
||||||
private MastodonAPIRequest<?> getCategoriesRequest;
|
protected String currentSearchQuery;
|
||||||
private EditText searchEdit;
|
protected String loadingInstanceDomain;
|
||||||
private TabLayout categoriesList;
|
protected HashMap<String, Instance> instancesCache=new HashMap<>();
|
||||||
private Runnable searchDebouncer=this::onSearchChangedDebounced;
|
protected View buttonBar;
|
||||||
private String currentSearchQuery;
|
protected List<CatalogInstance> filteredData=new ArrayList<>();
|
||||||
private String currentCategory="all";
|
protected GetInstance loadingInstanceRequest;
|
||||||
private List<CatalogCategory> categories=new ArrayList<>();
|
protected Call loadingInstanceRedirectRequest;
|
||||||
private String loadingInstanceDomain;
|
protected ProgressDialog instanceProgressDialog;
|
||||||
private GetInstance loadingInstanceRequest;
|
protected HashMap<String, String> redirects=new HashMap<>();
|
||||||
private Call loadingInstanceRedirectRequest;
|
protected HashMap<String, String> redirectsInverse=new HashMap<>();
|
||||||
private HashMap<String, Instance> instancesCache=new HashMap<>();
|
protected boolean isSignup;
|
||||||
private ProgressDialog instanceProgressDialog;
|
protected CatalogInstance fakeInstance=new CatalogInstance();
|
||||||
private View buttonBar;
|
|
||||||
private HashMap<String, String> redirects=new HashMap<>(), redirectsInverse=new HashMap<>();
|
|
||||||
|
|
||||||
private boolean isSignup;
|
|
||||||
|
|
||||||
private static final double DUNBAR=Math.log(800);
|
private static final double DUNBAR=Math.log(800);
|
||||||
|
|
||||||
public InstanceCatalogFragment(){
|
public InstanceCatalogFragment(int layout, int perPage){
|
||||||
super(R.layout.fragment_onboarding_common, 10);
|
super(layout, perPage);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState){
|
public void onCreate(Bundle savedInstanceState){
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
isSignup=getArguments().getBoolean("signup");
|
isSignup=getArguments() != null && getArguments().getBoolean("signup");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
protected abstract void proceedWithAuthOrSignup(Instance instance);
|
||||||
public void onAttach(Context context){
|
|
||||||
super.onAttach(context);
|
|
||||||
setRefreshEnabled(false);
|
|
||||||
loadData();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
protected boolean onSearchEnterPressed(TextView v, int actionId, KeyEvent event){
|
||||||
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){
|
|
||||||
if(event!=null && event.getAction()!=KeyEvent.ACTION_DOWN)
|
if(event!=null && event.getAction()!=KeyEvent.ACTION_DOWN)
|
||||||
return true;
|
return true;
|
||||||
currentSearchQuery=searchEdit.getText().toString().toLowerCase();
|
currentSearchQuery=searchEdit.getText().toString().toLowerCase();
|
||||||
@@ -385,60 +105,73 @@ public class InstanceCatalogFragment extends BaseRecyclerFragment<CatalogInstanc
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onSearchChangedDebounced(){
|
protected void onSearchChangedDebounced(){
|
||||||
currentSearchQuery=searchEdit.getText().toString().toLowerCase();
|
currentSearchQuery=searchEdit.getText().toString().toLowerCase();
|
||||||
updateFilteredList();
|
updateFilteredList();
|
||||||
loadInstanceInfo(currentSearchQuery, false);
|
loadInstanceInfo(currentSearchQuery, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateFilteredList(){
|
protected List<CatalogInstance> sortInstances(List<CatalogInstance> result){
|
||||||
ArrayList<CatalogInstance> prevData=new ArrayList<>(filteredData);
|
Map<String, List<CatalogInstance>> byLang=result.stream().collect(Collectors.groupingBy(ci->ci.language));
|
||||||
filteredData.clear();
|
for(List<CatalogInstance> group:byLang.values()){
|
||||||
for(CatalogInstance instance:data){
|
Collections.sort(group, (a, b)->{
|
||||||
if(currentCategory.equals("all") || instance.categories.contains(currentCategory)){
|
double aa=Math.abs(DUNBAR-Math.log(a.lastWeekUsers));
|
||||||
if(TextUtils.isEmpty(currentSearchQuery) || instance.domain.contains(currentSearchQuery)){
|
double bb=Math.abs(DUNBAR-Math.log(b.lastWeekUsers));
|
||||||
if(instance.domain.equals(currentSearchQuery) || !isSignup || !instance.approvalRequired)
|
return Double.compare(aa, bb);
|
||||||
filteredData.add(instance);
|
});
|
||||||
}
|
}
|
||||||
|
// 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(){
|
// add instances in preferred languages to the top of the list, in the order of preference
|
||||||
@Override
|
ArrayList<CatalogInstance> sortedList=new ArrayList<>();
|
||||||
public int getOldListSize(){
|
for(String lang:userLangs){
|
||||||
return prevData.size();
|
List<CatalogInstance> langInstances=byLang.remove(lang);
|
||||||
|
if(langInstances!=null){
|
||||||
|
sortedList.addAll(langInstances);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
@Override
|
// sort the remaining language groups by aggregate lastWeekUsers
|
||||||
public int getNewListSize(){
|
class InstanceGroup{
|
||||||
return filteredData.size();
|
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;
|
||||||
@Override
|
}).sorted(Comparator.comparingInt((InstanceGroup g)->g.activeUsers).reversed()).forEachOrdered(ig->sortedList.addAll(ig.instances));
|
||||||
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition){
|
return sortedList;
|
||||||
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 void showProgressDialog(){
|
protected abstract void updateFilteredList();
|
||||||
|
|
||||||
|
protected void showProgressDialog(){
|
||||||
instanceProgressDialog=new ProgressDialog(getActivity());
|
instanceProgressDialog=new ProgressDialog(getActivity());
|
||||||
instanceProgressDialog.setMessage(getString(R.string.loading_instance));
|
instanceProgressDialog.setMessage(getString(R.string.loading_instance));
|
||||||
instanceProgressDialog.setOnCancelListener(dialog->cancelLoadingInstanceInfo());
|
instanceProgressDialog.setOnCancelListener(dialog->cancelLoadingInstanceInfo());
|
||||||
instanceProgressDialog.show();
|
instanceProgressDialog.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
private String normalizeInstanceDomain(String _domain){
|
protected String normalizeInstanceDomain(String _domain){
|
||||||
if(TextUtils.isEmpty(_domain))
|
if(TextUtils.isEmpty(_domain))
|
||||||
return null;
|
return null;
|
||||||
if(_domain.contains(":")){
|
if(_domain.contains(":")){
|
||||||
try{
|
try{
|
||||||
_domain=Uri.parse(_domain).getAuthority();
|
_domain=Uri.parse(_domain).getAuthority();
|
||||||
}catch(Exception ignore){}
|
}catch(Exception ignore){
|
||||||
|
}
|
||||||
if(TextUtils.isEmpty(_domain))
|
if(TextUtils.isEmpty(_domain))
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -453,12 +186,12 @@ public class InstanceCatalogFragment extends BaseRecyclerFragment<CatalogInstanc
|
|||||||
return domain;
|
return domain;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadInstanceInfo(String _domain, boolean isFromRedirect){
|
protected void loadInstanceInfo(String _domain, boolean isFromRedirect){
|
||||||
String domain=normalizeInstanceDomain(_domain);
|
String domain=normalizeInstanceDomain(_domain);
|
||||||
Instance cachedInstance=instancesCache.get(domain);
|
Instance cachedInstance=instancesCache.get(domain);
|
||||||
if(cachedInstance!=null){
|
if(cachedInstance!=null){
|
||||||
for(CatalogInstance ci:filteredData){
|
for(CatalogInstance ci : filteredData){
|
||||||
if(ci.domain.equals(domain))
|
if(ci.domain.equals(domain) && ci!=fakeInstance)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
CatalogInstance ci=cachedInstance.toCatalogInstance();
|
CatalogInstance ci=cachedInstance.toCatalogInstance();
|
||||||
@@ -476,44 +209,57 @@ public class InstanceCatalogFragment extends BaseRecyclerFragment<CatalogInstanc
|
|||||||
loadingInstanceDomain=domain;
|
loadingInstanceDomain=domain;
|
||||||
loadingInstanceRequest=new GetInstance();
|
loadingInstanceRequest=new GetInstance();
|
||||||
loadingInstanceRequest.setCallback(new Callback<>(){
|
loadingInstanceRequest.setCallback(new Callback<>(){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Instance result){
|
public void onSuccess(Instance result){
|
||||||
loadingInstanceRequest=null;
|
loadingInstanceRequest=null;
|
||||||
loadingInstanceDomain=null;
|
loadingInstanceDomain=null;
|
||||||
result.uri=domain; // needed for instances that use domain redirection
|
result.uri=domain; // needed for instances that use domain redirection
|
||||||
instancesCache.put(domain, result);
|
instancesCache.put(domain, result);
|
||||||
if(instanceProgressDialog!=null){
|
if(instanceProgressDialog!=null){
|
||||||
instanceProgressDialog.dismiss();
|
instanceProgressDialog.dismiss();
|
||||||
instanceProgressDialog=null;
|
instanceProgressDialog=null;
|
||||||
proceedWithAuthOrSignup(result);
|
proceedWithAuthOrSignup(result);
|
||||||
}
|
}
|
||||||
if(Objects.equals(domain, currentSearchQuery) || Objects.equals(currentSearchQuery, redirects.get(domain)) || Objects.equals(currentSearchQuery, redirectsInverse.get(domain))){
|
if(Objects.equals(domain, currentSearchQuery) || Objects.equals(currentSearchQuery, redirects.get(domain)) || Objects.equals(currentSearchQuery, redirectsInverse.get(domain))){
|
||||||
boolean found=false;
|
boolean found=false;
|
||||||
for(CatalogInstance ci:filteredData){
|
for(CatalogInstance ci : filteredData){
|
||||||
if(ci.domain.equals(domain)){
|
if(ci.domain.equals(domain) && ci!=fakeInstance){
|
||||||
found=true;
|
found=true;
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
}
|
|
||||||
if(!found){
|
|
||||||
CatalogInstance ci=result.toCatalogInstance();
|
|
||||||
filteredData.add(0, ci);
|
|
||||||
adapter.notifyItemInserted(0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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
|
@Override
|
||||||
public void onError(ErrorResponse error){
|
public void onError(ErrorResponse error){
|
||||||
loadingInstanceRequest=null;
|
loadingInstanceRequest=null;
|
||||||
if(!isFromRedirect && error instanceof MastodonErrorResponse me && me.httpStatus==404){
|
if(!isFromRedirect && error instanceof MastodonErrorResponse me && me.httpStatus==404){
|
||||||
fetchDomainFromHostMetaAndMaybeRetry(domain, error);
|
fetchDomainFromHostMetaAndMaybeRetry(domain, error);
|
||||||
return;
|
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(){
|
private void cancelLoadingInstanceInfo(){
|
||||||
@@ -584,7 +330,7 @@ public class InstanceCatalogFragment extends BaseRecyclerFragment<CatalogInstanc
|
|||||||
InputSource source=new InputSource(response.body().charStream());
|
InputSource source=new InputSource(response.body().charStream());
|
||||||
Document doc=DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(source);
|
Document doc=DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(source);
|
||||||
NodeList list=doc.getElementsByTagName("Link");
|
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){
|
if(list.item(i) instanceof Element el){
|
||||||
String template=el.getAttribute("template");
|
String template=el.getAttribute("template");
|
||||||
if("lrdd".equals(el.getAttribute("rel")) && !TextUtils.isEmpty(template) && template.contains("{uri}")){
|
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>{
|
@Override
|
||||||
public InstancesAdapter(){
|
public void onViewCreated(View view, Bundle savedInstanceState){
|
||||||
super(imgLoader);
|
super.onViewCreated(view, savedInstanceState);
|
||||||
}
|
nextButton=view.findViewById(R.id.btn_next);
|
||||||
|
nextButton.setOnClickListener(this::onNextClick);
|
||||||
@NonNull
|
nextButton.setEnabled(chosenInstance!=null);
|
||||||
@Override
|
buttonBar=view.findViewById(R.id.button_bar);
|
||||||
public InstanceViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
|
setRefreshEnabled(false);
|
||||||
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{
|
protected void onNextClick(View v){
|
||||||
private final TextView title, description, userCount, lang;
|
String domain=chosenInstance.domain;
|
||||||
private final RadioButton radioButton;
|
Instance instance=instancesCache.get(domain);
|
||||||
|
if(instance!=null){
|
||||||
public InstanceViewHolder(){
|
proceedWithAuthOrSignup(instance);
|
||||||
super(getActivity(), R.layout.item_instance_catalog, list);
|
}else{
|
||||||
title=findViewById(R.id.title);
|
showProgressDialog();
|
||||||
description=findViewById(R.id.description);
|
if(!domain.equals(loadingInstanceDomain)){
|
||||||
userCount=findViewById(R.id.user_count);
|
loadInstanceInfo(domain, false);
|
||||||
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 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,259 @@
|
|||||||
|
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.TextWatcher;
|
||||||
|
import android.view.Gravity;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.RadioButton;
|
||||||
|
import android.widget.Space;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toolbar;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
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.utils.UiUtils;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import me.grishka.appkit.FragmentStackActivity;
|
||||||
|
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 MoshidonLoginFragment extends InstanceCatalogFragment {
|
||||||
|
private View headerView;
|
||||||
|
|
||||||
|
public MoshidonLoginFragment() {
|
||||||
|
super(R.layout.fragment_moshidon_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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -43,7 +43,9 @@ public class PushNotification extends BaseModel{
|
|||||||
@SerializedName("follow")
|
@SerializedName("follow")
|
||||||
FOLLOW(R.string.notification_type_follow),
|
FOLLOW(R.string.notification_type_follow),
|
||||||
@SerializedName("poll")
|
@SerializedName("poll")
|
||||||
POLL(R.string.notification_type_poll);
|
POLL(R.string.notification_type_poll),
|
||||||
|
@SerializedName("status")
|
||||||
|
STATUS(R.string.sk_notification_type_status);
|
||||||
|
|
||||||
@StringRes
|
@StringRes
|
||||||
public final int localizedName;
|
public final int localizedName;
|
||||||
|
|||||||
@@ -43,10 +43,11 @@ public class PushSubscription extends BaseModel implements Cloneable{
|
|||||||
public boolean reblog;
|
public boolean reblog;
|
||||||
public boolean mention;
|
public boolean mention;
|
||||||
public boolean poll;
|
public boolean poll;
|
||||||
|
public boolean status;
|
||||||
|
|
||||||
public static Alerts ofAll(){
|
public static Alerts ofAll(){
|
||||||
Alerts alerts=new Alerts();
|
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;
|
return alerts;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,6 +59,7 @@ public class PushSubscription extends BaseModel implements Cloneable{
|
|||||||
", reblog="+reblog+
|
", reblog="+reblog+
|
||||||
", mention="+mention+
|
", mention="+mention+
|
||||||
", poll="+poll+
|
", poll="+poll+
|
||||||
|
", status="+status+
|
||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ public class Status extends BaseModel implements DisplayItemsParent{
|
|||||||
public long favouritesCount;
|
public long favouritesCount;
|
||||||
public long repliesCount;
|
public long repliesCount;
|
||||||
public Instant editedAt;
|
public Instant editedAt;
|
||||||
|
public boolean wantsTranslation;
|
||||||
|
|
||||||
public String url;
|
public String url;
|
||||||
public String inReplyToId;
|
public String inReplyToId;
|
||||||
|
|||||||
@@ -0,0 +1,146 @@
|
|||||||
|
package org.joinmastodon.android.model;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.GlobalUserPreferences;
|
||||||
|
import org.joinmastodon.android.api.ObjectValidationException;
|
||||||
|
import org.joinmastodon.android.api.RequiredField;
|
||||||
|
import org.joinmastodon.android.events.StatusCountersUpdatedEvent;
|
||||||
|
import org.joinmastodon.android.ui.text.HtmlParser;
|
||||||
|
import org.parceler.Parcel;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Parcel
|
||||||
|
public class StatusTranslation extends BaseModel implements DisplayItemsParent{
|
||||||
|
// @RequiredField
|
||||||
|
public String id;
|
||||||
|
// @RequiredField
|
||||||
|
public String uri;
|
||||||
|
// @RequiredField
|
||||||
|
public Instant createdAt;
|
||||||
|
// @RequiredField
|
||||||
|
public Account account;
|
||||||
|
// @RequiredField
|
||||||
|
public String content;
|
||||||
|
// @RequiredField
|
||||||
|
public StatusPrivacy visibility;
|
||||||
|
public boolean sensitive;
|
||||||
|
// @RequiredField
|
||||||
|
public String spoilerText;
|
||||||
|
// @RequiredField
|
||||||
|
public List<Attachment> mediaAttachments;
|
||||||
|
public Application application;
|
||||||
|
// @RequiredField
|
||||||
|
public List<Mention> mentions;
|
||||||
|
// @RequiredField
|
||||||
|
public List<Hashtag> tags;
|
||||||
|
// @RequiredField
|
||||||
|
public List<Emoji> emojis;
|
||||||
|
public long reblogsCount;
|
||||||
|
public long favouritesCount;
|
||||||
|
public long repliesCount;
|
||||||
|
public Instant editedAt;
|
||||||
|
|
||||||
|
public String url;
|
||||||
|
public String inReplyToId;
|
||||||
|
public String inReplyToAccountId;
|
||||||
|
public Status reblog;
|
||||||
|
public Poll poll;
|
||||||
|
public Card card;
|
||||||
|
public String language;
|
||||||
|
public String text;
|
||||||
|
|
||||||
|
public boolean favourited;
|
||||||
|
public boolean reblogged;
|
||||||
|
public boolean muted;
|
||||||
|
public boolean bookmarked;
|
||||||
|
public boolean pinned;
|
||||||
|
|
||||||
|
public transient boolean spoilerRevealed;
|
||||||
|
public transient boolean hasGapAfter;
|
||||||
|
private transient String strippedText;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void postprocess() throws ObjectValidationException{
|
||||||
|
super.postprocess();
|
||||||
|
// if(application!=null)
|
||||||
|
// application.postprocess();
|
||||||
|
// for(Mention m:mentions)
|
||||||
|
// m.postprocess();
|
||||||
|
// for(Hashtag t:tags)
|
||||||
|
// t.postprocess();
|
||||||
|
// for(Emoji e:emojis)
|
||||||
|
// e.postprocess();
|
||||||
|
// for(Attachment a:mediaAttachments)
|
||||||
|
// a.postprocess();
|
||||||
|
// account.postprocess();
|
||||||
|
// if(poll!=null)
|
||||||
|
// poll.postprocess();
|
||||||
|
// if(card!=null)
|
||||||
|
// card.postprocess();
|
||||||
|
// if(reblog!=null)
|
||||||
|
// reblog.postprocess();
|
||||||
|
|
||||||
|
// spoilerRevealed=GlobalUserPreferences.alwaysExpandContentWarnings || !sensitive;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString(){
|
||||||
|
return "Status{"+
|
||||||
|
"id='"+id+'\''+
|
||||||
|
", uri='"+uri+'\''+
|
||||||
|
", createdAt="+createdAt+
|
||||||
|
", account="+account+
|
||||||
|
", content='"+content+'\''+
|
||||||
|
", visibility="+visibility+
|
||||||
|
", sensitive="+sensitive+
|
||||||
|
", spoilerText='"+spoilerText+'\''+
|
||||||
|
", mediaAttachments="+mediaAttachments+
|
||||||
|
", application="+application+
|
||||||
|
", mentions="+mentions+
|
||||||
|
", tags="+tags+
|
||||||
|
", emojis="+emojis+
|
||||||
|
", reblogsCount="+reblogsCount+
|
||||||
|
", favouritesCount="+favouritesCount+
|
||||||
|
", repliesCount="+repliesCount+
|
||||||
|
", url='"+url+'\''+
|
||||||
|
", inReplyToId='"+inReplyToId+'\''+
|
||||||
|
", inReplyToAccountId='"+inReplyToAccountId+'\''+
|
||||||
|
", reblog="+reblog+
|
||||||
|
", poll="+poll+
|
||||||
|
", card="+card+
|
||||||
|
", language='"+language+'\''+
|
||||||
|
", text='"+text+'\''+
|
||||||
|
", favourited="+favourited+
|
||||||
|
", reblogged="+reblogged+
|
||||||
|
", muted="+muted+
|
||||||
|
", bookmarked="+bookmarked+
|
||||||
|
", pinned="+pinned+
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getID(){
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update(StatusCountersUpdatedEvent ev){
|
||||||
|
favouritesCount=ev.favorites;
|
||||||
|
reblogsCount=ev.reblogs;
|
||||||
|
repliesCount=ev.replies;
|
||||||
|
favourited=ev.favorited;
|
||||||
|
reblogged=ev.reblogged;
|
||||||
|
bookmarked=ev.bookmarked;
|
||||||
|
pinned=ev.pinned;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StatusTranslation getContentStatus(){
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getStrippedText(){
|
||||||
|
if(strippedText==null)
|
||||||
|
strippedText=HtmlParser.strip(content);
|
||||||
|
return strippedText;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,14 +2,12 @@ package org.joinmastodon.android.ui;
|
|||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.res.ColorStateList;
|
import android.content.res.ColorStateList;
|
||||||
import android.graphics.drawable.Animatable;
|
import android.graphics.drawable.Animatable;
|
||||||
import android.graphics.drawable.ColorDrawable;
|
import android.graphics.drawable.ColorDrawable;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.WindowInsets;
|
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.requests.oauth.RevokeOauthToken;
|
||||||
import org.joinmastodon.android.api.session.AccountSession;
|
import org.joinmastodon.android.api.session.AccountSession;
|
||||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||||
import org.joinmastodon.android.fragments.SplashFragment;
|
import org.joinmastodon.android.fragments.onboarding.MoshidonLoginFragment;
|
||||||
import org.joinmastodon.android.model.Account;
|
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
|
|
||||||
import java.util.List;
|
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.setImageResource(R.drawable.ic_fluent_add_circle_24_filled);
|
||||||
holder.avatar.setImageTintList(ColorStateList.valueOf(UiUtils.getThemeColor(activity, android.R.attr.textColorPrimary)));
|
holder.avatar.setImageTintList(ColorStateList.valueOf(UiUtils.getThemeColor(activity, android.R.attr.textColorPrimary)));
|
||||||
adapter.addAdapter(new ClickableSingleViewRecyclerAdapter(holder.itemView, ()->{
|
adapter.addAdapter(new ClickableSingleViewRecyclerAdapter(holder.itemView, ()->{
|
||||||
Nav.go(activity, SplashFragment.class, null);
|
Nav.go(activity, MoshidonLoginFragment.class, null);
|
||||||
dismiss();
|
dismiss();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import java.util.Collections;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
@@ -154,11 +155,16 @@ public class ComposeAutocompleteViewController{
|
|||||||
}else if(mode==Mode.EMOJIS){
|
}else if(mode==Mode.EMOJIS){
|
||||||
String _text=text.substring(1); // remove ':'
|
String _text=text.substring(1); // remove ':'
|
||||||
List<WrappedEmoji> oldList=emojis;
|
List<WrappedEmoji> oldList=emojis;
|
||||||
emojis=AccountSessionManager.getInstance()
|
List<Emoji> allEmojis = AccountSessionManager.getInstance()
|
||||||
.getCustomEmojis(AccountSessionManager.getInstance().getAccount(accountID).domain)
|
.getCustomEmojis(AccountSessionManager.getInstance().getAccount(accountID).domain)
|
||||||
.stream()
|
.stream()
|
||||||
.flatMap(ec->ec.emojis.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)
|
.map(WrappedEmoji::new)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
UiUtils.updateList(oldList, emojis, list, emojisAdapter, (e1, e2)->e1.emoji.shortcode.equals(e2.emoji.shortcode));
|
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);
|
TextView heading=new TextView(activity);
|
||||||
heading.setText(R.string.image_description);
|
heading.setText(R.string.sk_image_description);
|
||||||
heading.setAllCaps(true);
|
heading.setAllCaps(true);
|
||||||
heading.setTypeface(null, Typeface.BOLD);
|
heading.setTypeface(null, Typeface.BOLD);
|
||||||
heading.setPadding(0, V.dp(24), 0, V.dp(8));
|
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)
|
if(id==R.id.favorite_btn)
|
||||||
return R.string.button_favorite;
|
return R.string.button_favorite;
|
||||||
if(id==R.id.bookmark_btn)
|
if(id==R.id.bookmark_btn)
|
||||||
return R.string.button_bookmark;
|
return R.string.add_bookmark;
|
||||||
if(id==R.id.share_btn)
|
if(id==R.id.share_btn)
|
||||||
return R.string.button_share;
|
return R.string.button_share;
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import org.joinmastodon.android.GlobalUserPreferences;
|
|||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.requests.accounts.GetAccountRelationships;
|
import org.joinmastodon.android.api.requests.accounts.GetAccountRelationships;
|
||||||
import org.joinmastodon.android.api.requests.statuses.GetStatusSourceText;
|
import org.joinmastodon.android.api.requests.statuses.GetStatusSourceText;
|
||||||
|
import org.joinmastodon.android.api.requests.statuses.GetStatusTranslation;
|
||||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||||
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
||||||
import org.joinmastodon.android.fragments.ComposeFragment;
|
import org.joinmastodon.android.fragments.ComposeFragment;
|
||||||
@@ -33,6 +34,7 @@ import org.joinmastodon.android.model.Account;
|
|||||||
import org.joinmastodon.android.model.Attachment;
|
import org.joinmastodon.android.model.Attachment;
|
||||||
import org.joinmastodon.android.model.Relationship;
|
import org.joinmastodon.android.model.Relationship;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
|
import org.joinmastodon.android.model.StatusTranslation;
|
||||||
import org.joinmastodon.android.ui.text.HtmlParser;
|
import org.joinmastodon.android.ui.text.HtmlParser;
|
||||||
import org.joinmastodon.android.ui.utils.CustomEmojiHelper;
|
import org.joinmastodon.android.ui.utils.CustomEmojiHelper;
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
@@ -60,6 +62,7 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
|
|||||||
private SpannableStringBuilder parsedName;
|
private SpannableStringBuilder parsedName;
|
||||||
public final Status status;
|
public final Status status;
|
||||||
private boolean hasVisibilityToggle;
|
private boolean hasVisibilityToggle;
|
||||||
|
private boolean hasTranslateToggle;
|
||||||
boolean needBottomPadding;
|
boolean needBottomPadding;
|
||||||
private String extraText;
|
private String extraText;
|
||||||
|
|
||||||
@@ -74,6 +77,7 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
|
|||||||
HtmlParser.parseCustomEmoji(parsedName, user.emojis);
|
HtmlParser.parseCustomEmoji(parsedName, user.emojis);
|
||||||
emojiHelper.setText(parsedName);
|
emojiHelper.setText(parsedName);
|
||||||
if(status!=null){
|
if(status!=null){
|
||||||
|
hasTranslateToggle=true;
|
||||||
hasVisibilityToggle=status.sensitive || !TextUtils.isEmpty(status.spoilerText);
|
hasVisibilityToggle=status.sensitive || !TextUtils.isEmpty(status.spoilerText);
|
||||||
if(!hasVisibilityToggle && !status.mediaAttachments.isEmpty()){
|
if(!hasVisibilityToggle && !status.mediaAttachments.isEmpty()){
|
||||||
for(Attachment att:status.mediaAttachments){
|
for(Attachment att:status.mediaAttachments){
|
||||||
@@ -107,7 +111,7 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
|
|||||||
|
|
||||||
public static class Holder extends StatusDisplayItem.Holder<HeaderStatusDisplayItem> implements ImageLoaderViewHolder{
|
public static class Holder extends StatusDisplayItem.Holder<HeaderStatusDisplayItem> implements ImageLoaderViewHolder{
|
||||||
private final TextView name, username, timestamp, extraText;
|
private final TextView name, username, timestamp, extraText;
|
||||||
private final ImageView avatar, more, visibility;
|
private final ImageView avatar, more, visibility, translate;
|
||||||
private final PopupMenu optionsMenu;
|
private final PopupMenu optionsMenu;
|
||||||
private Relationship relationship;
|
private Relationship relationship;
|
||||||
private APIRequest<?> currentRelationshipRequest;
|
private APIRequest<?> currentRelationshipRequest;
|
||||||
@@ -121,6 +125,7 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
|
|||||||
|
|
||||||
public Holder(Activity activity, ViewGroup parent){
|
public Holder(Activity activity, ViewGroup parent){
|
||||||
super(activity, R.layout.display_item_header, parent);
|
super(activity, R.layout.display_item_header, parent);
|
||||||
|
translate=findViewById(R.id.translate);
|
||||||
name=findViewById(R.id.name);
|
name=findViewById(R.id.name);
|
||||||
username=findViewById(R.id.username);
|
username=findViewById(R.id.username);
|
||||||
timestamp=findViewById(R.id.timestamp);
|
timestamp=findViewById(R.id.timestamp);
|
||||||
@@ -133,6 +138,7 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
|
|||||||
avatar.setClipToOutline(true);
|
avatar.setClipToOutline(true);
|
||||||
more.setOnClickListener(this::onMoreClick);
|
more.setOnClickListener(this::onMoreClick);
|
||||||
visibility.setOnClickListener(v->item.parentFragment.onVisibilityIconClick(this));
|
visibility.setOnClickListener(v->item.parentFragment.onVisibilityIconClick(this));
|
||||||
|
translate.setOnClickListener(v->item.parentFragment.onRevealTranslationClick(this));
|
||||||
|
|
||||||
optionsMenu=new PopupMenu(activity, more);
|
optionsMenu=new PopupMenu(activity, more);
|
||||||
optionsMenu.inflate(R.menu.post);
|
optionsMenu.inflate(R.menu.post);
|
||||||
@@ -226,6 +232,7 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
|
|||||||
else
|
else
|
||||||
timestamp.setText(item.parentFragment.getString(R.string.edited_timestamp, UiUtils.formatRelativeTimestamp(itemView.getContext(), item.status.editedAt)));
|
timestamp.setText(item.parentFragment.getString(R.string.edited_timestamp, UiUtils.formatRelativeTimestamp(itemView.getContext(), item.status.editedAt)));
|
||||||
visibility.setVisibility(item.hasVisibilityToggle && !item.inset ? View.VISIBLE : View.GONE);
|
visibility.setVisibility(item.hasVisibilityToggle && !item.inset ? View.VISIBLE : View.GONE);
|
||||||
|
translate.setVisibility(item.hasTranslateToggle ? View.VISIBLE : View.GONE);
|
||||||
if(item.hasVisibilityToggle){
|
if(item.hasVisibilityToggle){
|
||||||
visibility.setImageResource(item.status.spoilerRevealed ? R.drawable.ic_visibility_off : R.drawable.ic_visibility);
|
visibility.setImageResource(item.status.spoilerRevealed ? R.drawable.ic_visibility_off : R.drawable.ic_visibility);
|
||||||
visibility.setContentDescription(item.parentFragment.getString(item.status.spoilerRevealed ? R.string.hide_content : R.string.reveal_content));
|
visibility.setContentDescription(item.parentFragment.getString(item.status.spoilerRevealed ? R.string.hide_content : R.string.reveal_content));
|
||||||
@@ -233,6 +240,9 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
|
|||||||
visibility.setTooltipText(visibility.getContentDescription());
|
visibility.setTooltipText(visibility.getContentDescription());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if(item.hasTranslateToggle){
|
||||||
|
translate.setImageResource(item.status.wantsTranslation ? R.drawable.ic_translate_on : R.drawable.ic_translate_off);
|
||||||
|
}
|
||||||
itemView.setPadding(itemView.getPaddingLeft(), itemView.getPaddingTop(), itemView.getPaddingRight(), item.needBottomPadding ? V.dp(16) : 0);
|
itemView.setPadding(itemView.getPaddingLeft(), itemView.getPaddingTop(), itemView.getPaddingRight(), item.needBottomPadding ? V.dp(16) : 0);
|
||||||
if(TextUtils.isEmpty(item.extraText)){
|
if(TextUtils.isEmpty(item.extraText)){
|
||||||
extraText.setVisibility(View.GONE);
|
extraText.setVisibility(View.GONE);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import android.view.ViewGroup;
|
|||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.GlobalUserPreferences;
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
||||||
import org.joinmastodon.android.model.Poll;
|
import org.joinmastodon.android.model.Poll;
|
||||||
@@ -44,7 +45,7 @@ public class PollFooterStatusDisplayItem extends StatusDisplayItem{
|
|||||||
text+=" · "+item.parentFragment.getString(R.string.poll_closed);
|
text+=" · "+item.parentFragment.getString(R.string.poll_closed);
|
||||||
}
|
}
|
||||||
this.text.setText(text);
|
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());
|
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.graphics.drawable.Drawable;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.ImageView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
@@ -60,7 +61,8 @@ public class PollOptionStatusDisplayItem extends StatusDisplayItem{
|
|||||||
|
|
||||||
public static class Holder extends StatusDisplayItem.Holder<PollOptionStatusDisplayItem> implements ImageLoaderViewHolder{
|
public static class Holder extends StatusDisplayItem.Holder<PollOptionStatusDisplayItem> implements ImageLoaderViewHolder{
|
||||||
private final TextView text, percent;
|
private final TextView text, percent;
|
||||||
private final View icon, button;
|
private final View button;
|
||||||
|
private final ImageView icon;
|
||||||
private final Drawable progressBg;
|
private final Drawable progressBg;
|
||||||
|
|
||||||
public Holder(Activity activity, ViewGroup parent){
|
public Holder(Activity activity, ViewGroup parent){
|
||||||
@@ -76,13 +78,17 @@ public class PollOptionStatusDisplayItem extends StatusDisplayItem{
|
|||||||
@Override
|
@Override
|
||||||
public void onBind(PollOptionStatusDisplayItem item){
|
public void onBind(PollOptionStatusDisplayItem item){
|
||||||
text.setText(item.text);
|
text.setText(item.text);
|
||||||
icon.setVisibility(item.showResults ? View.GONE : View.VISIBLE);
|
|
||||||
percent.setVisibility(item.showResults ? View.VISIBLE : View.GONE);
|
percent.setVisibility(item.showResults ? View.VISIBLE : View.GONE);
|
||||||
itemView.setClickable(!item.showResults);
|
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){
|
if(item.showResults){
|
||||||
progressBg.setLevel(Math.round(10000f*item.votesFraction));
|
progressBg.setLevel(Math.round(10000f*item.votesFraction));
|
||||||
button.setBackground(progressBg);
|
button.setBackground(progressBg);
|
||||||
itemView.setSelected(item.isMostVoted);
|
itemView.setSelected(item.isMostVoted);
|
||||||
|
icon.setSelected(item.poll.ownVotes.contains(item.poll.options.indexOf(item.option)));
|
||||||
percent.setText(String.format(Locale.getDefault(), "%d%%", Math.round(item.votesFraction*100f)));
|
percent.setText(String.format(Locale.getDefault(), "%d%%", Math.round(item.votesFraction*100f)));
|
||||||
}else{
|
}else{
|
||||||
itemView.setSelected(item.poll.selectedOptions!=null && item.poll.selectedOptions.contains(item.option));
|
itemView.setSelected(item.poll.selectedOptions!=null && item.poll.selectedOptions.contains(item.option));
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
package org.joinmastodon.android.ui.displayitems;
|
package org.joinmastodon.android.ui.displayitems;
|
||||||
|
|
||||||
|
import static org.joinmastodon.android.MastodonApp.context;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.text.SpannableStringBuilder;
|
import android.text.SpannableStringBuilder;
|
||||||
|
import android.util.TypedValue;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
@@ -38,6 +41,8 @@ public class ReblogOrReplyLineStatusDisplayItem extends StatusDisplayItem{
|
|||||||
emojiHelper.setText(ssb);
|
emojiHelper.setText(ssb);
|
||||||
this.icon=icon;
|
this.icon=icon;
|
||||||
this.handleClick=handleClick;
|
this.handleClick=handleClick;
|
||||||
|
TypedValue outValue = new TypedValue();
|
||||||
|
context.getTheme().resolveAttribute(android.R.attr.selectableItemBackground, outValue, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -67,6 +72,8 @@ public class ReblogOrReplyLineStatusDisplayItem extends StatusDisplayItem{
|
|||||||
text.setText(item.text);
|
text.setText(item.text);
|
||||||
text.setCompoundDrawablesRelativeWithIntrinsicBounds(item.icon, 0, 0, 0);
|
text.setCompoundDrawablesRelativeWithIntrinsicBounds(item.icon, 0, 0, 0);
|
||||||
if(item.handleClick!=null) text.setOnClickListener(item.handleClick);
|
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)
|
if(Build.VERSION.SDK_INT<Build.VERSION_CODES.N)
|
||||||
UiUtils.fixCompoundDrawableTintOnAndroid6(text);
|
UiUtils.fixCompoundDrawableTintOnAndroid6(text);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,17 +8,22 @@ import android.text.TextUtils;
|
|||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.Button;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
|
import org.joinmastodon.android.api.requests.statuses.GetStatusTranslation;
|
||||||
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
|
import org.joinmastodon.android.model.StatusTranslation;
|
||||||
import org.joinmastodon.android.ui.drawables.SpoilerStripesDrawable;
|
import org.joinmastodon.android.ui.drawables.SpoilerStripesDrawable;
|
||||||
import org.joinmastodon.android.ui.text.HtmlParser;
|
import org.joinmastodon.android.ui.text.HtmlParser;
|
||||||
import org.joinmastodon.android.ui.utils.CustomEmojiHelper;
|
import org.joinmastodon.android.ui.utils.CustomEmojiHelper;
|
||||||
import org.joinmastodon.android.ui.views.LinkedTextView;
|
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.ImageLoaderViewHolder;
|
||||||
import me.grishka.appkit.imageloader.MovieDrawable;
|
import me.grishka.appkit.imageloader.MovieDrawable;
|
||||||
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
|
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
|
||||||
@@ -35,6 +40,7 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
|
|||||||
super(parentID, parentFragment);
|
super(parentID, parentFragment);
|
||||||
this.text=text;
|
this.text=text;
|
||||||
this.status=status;
|
this.status=status;
|
||||||
|
// this.wantsTranslation=wantsTranslation;
|
||||||
emojiHelper.setText(text);
|
emojiHelper.setText(text);
|
||||||
if(!TextUtils.isEmpty(status.spoilerText)){
|
if(!TextUtils.isEmpty(status.spoilerText)){
|
||||||
parsedSpoilerText=HtmlParser.parseCustomEmoji(status.spoilerText, status.emojis);
|
parsedSpoilerText=HtmlParser.parseCustomEmoji(status.spoilerText, status.emojis);
|
||||||
@@ -91,7 +97,26 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBind(TextStatusDisplayItem item){
|
public void onBind(TextStatusDisplayItem item){
|
||||||
text.setText(item.text);
|
if(item.status.wantsTranslation){
|
||||||
|
new GetStatusTranslation(item.status.id)
|
||||||
|
.setCallback(new Callback<StatusTranslation>(){
|
||||||
|
@Override
|
||||||
|
public void onSuccess(StatusTranslation status){
|
||||||
|
text.setText(status.getStrippedText());
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void onError(ErrorResponse error){
|
||||||
|
item.status.wantsTranslation=false;
|
||||||
|
text.setText(item.text);
|
||||||
|
error.showToast(item.parentFragment.getActivity());
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
.wrapProgress(item.parentFragment.getActivity(), R.string.loading, true)
|
||||||
|
.exec(item.parentFragment.getAccountID());
|
||||||
|
}else{
|
||||||
|
text.setText(item.text);
|
||||||
|
}
|
||||||
text.setTextIsSelectable(item.textSelectable);
|
text.setTextIsSelectable(item.textSelectable);
|
||||||
spoilerTitleInline.setTextIsSelectable(item.textSelectable);
|
spoilerTitleInline.setTextIsSelectable(item.textSelectable);
|
||||||
text.setInvalidateOnEveryFrame(false);
|
text.setInvalidateOnEveryFrame(false);
|
||||||
|
|||||||
@@ -178,7 +178,7 @@ public class PhotoViewer implements ZoomPanView.Listener{
|
|||||||
toolbar=uiOverlay.findViewById(R.id.toolbar);
|
toolbar=uiOverlay.findViewById(R.id.toolbar);
|
||||||
toolbar.setNavigationOnClickListener(v->onStartSwipeToDismissTransition(0));
|
toolbar.setNavigationOnClickListener(v->onStartSwipeToDismissTransition(0));
|
||||||
imageDescriptionButton = toolbar.getMenu()
|
imageDescriptionButton = toolbar.getMenu()
|
||||||
.add(R.string.image_description)
|
.add(R.string.sk_image_description)
|
||||||
.setIcon(R.drawable.ic_fluent_image_alt_text_24_regular)
|
.setIcon(R.drawable.ic_fluent_image_alt_text_24_regular)
|
||||||
.setVisible(attachments.get(pager.getCurrentItem()).description != null
|
.setVisible(attachments.get(pager.getCurrentItem()).description != null
|
||||||
&& !attachments.get(pager.getCurrentItem()).description.isEmpty())
|
&& !attachments.get(pager.getCurrentItem()).description.isEmpty())
|
||||||
|
|||||||
@@ -1,12 +1,29 @@
|
|||||||
package org.joinmastodon.android.ui.text;
|
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.SpannableStringBuilder;
|
||||||
import android.text.Spanned;
|
import android.text.Spanned;
|
||||||
import android.text.TextUtils;
|
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 android.widget.TextView;
|
||||||
|
|
||||||
import com.twitter.twittertext.Regex;
|
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.Emoji;
|
||||||
import org.joinmastodon.android.model.Hashtag;
|
import org.joinmastodon.android.model.Hashtag;
|
||||||
import org.joinmastodon.android.model.Mention;
|
import org.joinmastodon.android.model.Mention;
|
||||||
@@ -15,11 +32,11 @@ import org.jsoup.Jsoup;
|
|||||||
import org.jsoup.nodes.Element;
|
import org.jsoup.nodes.Element;
|
||||||
import org.jsoup.nodes.Node;
|
import org.jsoup.nodes.Node;
|
||||||
import org.jsoup.nodes.TextNode;
|
import org.jsoup.nodes.TextNode;
|
||||||
import org.jsoup.safety.Cleaner;
|
|
||||||
import org.jsoup.safety.Safelist;
|
import org.jsoup.safety.Safelist;
|
||||||
import org.jsoup.select.NodeVisitor;
|
import org.jsoup.select.NodeVisitor;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
@@ -29,6 +46,8 @@ import java.util.stream.Collectors;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import me.grishka.appkit.utils.V;
|
||||||
|
|
||||||
public class HtmlParser{
|
public class HtmlParser{
|
||||||
private static final String TAG="HtmlParser";
|
private static final String TAG="HtmlParser";
|
||||||
private static final String VALID_URL_PATTERN_STRING =
|
private static final String VALID_URL_PATTERN_STRING =
|
||||||
@@ -67,11 +86,17 @@ public class HtmlParser{
|
|||||||
public Object span;
|
public Object span;
|
||||||
public int start;
|
public int start;
|
||||||
public Element element;
|
public Element element;
|
||||||
|
public boolean more;
|
||||||
|
|
||||||
public SpanInfo(Object span, int start, Element element){
|
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.span=span;
|
||||||
this.start=start;
|
this.start=start;
|
||||||
this.element=element;
|
this.element=element;
|
||||||
|
this.more=more;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,24 +144,59 @@ public class HtmlParser{
|
|||||||
openSpans.add(new SpanInfo(new InvisibleSpan(), ssb.length(), el));
|
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
|
@Override
|
||||||
public void tail(@NonNull Node node, int depth){
|
public void tail(@NonNull Node node, int depth){
|
||||||
if(node instanceof Element el){
|
if(node instanceof Element el){
|
||||||
|
processOpenSpan(el);
|
||||||
if("span".equals(el.nodeName()) && el.hasClass("ellipsis")){
|
if("span".equals(el.nodeName()) && el.hasClass("ellipsis")){
|
||||||
ssb.append("…", new DeleteWhenCopiedSpan(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
ssb.append("…", new DeleteWhenCopiedSpan(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
}else if("p".equals(el.nodeName())){
|
}else if(blockElements.contains(el.nodeName()) && node.nextSibling()!=null){
|
||||||
if(node.nextSibling()!=null)
|
ssb.append("\n"); // line end
|
||||||
ssb.append("\n\n");
|
ssb.append("\n", new RelativeSizeSpan(0.75f), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); // margin after block
|
||||||
}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);
|
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_HASHTAGS -> R.string.trending_hashtags_info_banner;
|
||||||
case TRENDING_LINKS -> R.string.trending_links_info_banner;
|
case TRENDING_LINKS -> R.string.trending_links_info_banner;
|
||||||
case LOCAL_TIMELINE -> R.string.local_timeline_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;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -411,7 +411,7 @@ public class UiUtils{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void confirmDeletePost(Activity activity, String accountID, Status status, Consumer<Status> resultCallback, boolean forRedraft){
|
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)
|
new DeleteStatus(status.id)
|
||||||
.setCallback(new Callback<>(){
|
.setCallback(new Callback<>(){
|
||||||
@Override
|
@Override
|
||||||
@@ -433,9 +433,9 @@ public class UiUtils{
|
|||||||
|
|
||||||
public static void confirmPinPost(Activity activity, String accountID, Status status, boolean pinned, Consumer<Status> resultCallback){
|
public static void confirmPinPost(Activity activity, String accountID, Status status, boolean pinned, Consumer<Status> resultCallback){
|
||||||
showConfirmationAlert(activity,
|
showConfirmationAlert(activity,
|
||||||
pinned ? R.string.confirm_pin_post_title : R.string.confirm_unpin_post_title,
|
pinned ? R.string.sk_confirm_pin_post_title : R.string.sk_confirm_unpin_post_title,
|
||||||
pinned ? R.string.confirm_pin_post : R.string.confirm_unpin_post,
|
pinned ? R.string.sk_confirm_pin_post : R.string.sk_confirm_unpin_post,
|
||||||
pinned ? R.string.pin_post : R.string.unpin_post,
|
pinned ? R.string.sk_pin_post : R.string.sk_unpin_post,
|
||||||
()->{
|
()->{
|
||||||
new SetStatusPinned(status.id, pinned)
|
new SetStatusPinned(status.id, pinned)
|
||||||
.setCallback(new Callback<>() {
|
.setCallback(new Callback<>() {
|
||||||
@@ -452,7 +452,7 @@ public class UiUtils{
|
|||||||
error.showToast(activity);
|
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);
|
.exec(accountID);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -508,7 +508,7 @@ public class UiUtils{
|
|||||||
public void onSuccess(Relationship result) {
|
public void onSuccess(Relationship result) {
|
||||||
resultCallback.accept(result);
|
resultCallback.accept(result);
|
||||||
progressCallback.accept(false);
|
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
|
@Override
|
||||||
@@ -718,6 +718,16 @@ public class UiUtils{
|
|||||||
GlobalUserPreferences.trueBlackTheme ? R.style.Theme_Mastodon_Dark_TrueBlack_Yellow : R.style.Theme_Mastodon_Dark_Yellow;
|
GlobalUserPreferences.trueBlackTheme ? R.style.Theme_Mastodon_Dark_TrueBlack_Yellow : R.style.Theme_Mastodon_Dark_Yellow;
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
case MATERIAL3:
|
||||||
|
context.setTheme(switch(GlobalUserPreferences.theme){
|
||||||
|
case AUTO ->
|
||||||
|
GlobalUserPreferences.trueBlackTheme ? R.style.Theme_Mastodon_AutoLightDark_TrueBlack_Material3 : R.style.Theme_Mastodon_AutoLightDark_Material3;
|
||||||
|
case LIGHT ->
|
||||||
|
R.style.Theme_Mastodon_Light_Material3;
|
||||||
|
case DARK ->
|
||||||
|
GlobalUserPreferences.trueBlackTheme ? R.style.Theme_Mastodon_Dark_TrueBlack_Material3 : R.style.Theme_Mastodon_Dark_Material3;
|
||||||
|
});
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
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>
|
||||||
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>
|
||||||
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>
|
||||||
@@ -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,5 @@
|
|||||||
|
<vector android:height="28dp" android:tint="#000000"
|
||||||
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
|
android:width="28dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M3,5v14h17L20,5L3,5zM7,7v2L5,9L5,7h2zM5,13v-2h2v2L5,13zM5,15h2v2L5,17v-2zM18,17L9,17v-2h9v2zM18,13L9,13v-2h9v2zM18,9L9,9L9,7h9v2z"/>
|
||||||
|
</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>
|
||||||
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>
|
||||||
5
mastodon/src/main/res/drawable/ic_translate_off.xml
Normal file
5
mastodon/src/main/res/drawable/ic_translate_off.xml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="24dp" android:tint="#000000"
|
||||||
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
|
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="?colorSecondary" android:pathData="M12.87,15.07l-2.54,-2.51 0.03,-0.03c1.74,-1.94 2.98,-4.17 3.71,-6.53L17,6L17,4h-7L10,2L8,2v2L1,4v1.99h11.17C11.5,7.92 10.44,9.75 9,11.35 8.07,10.32 7.3,9.19 6.69,8h-2c0.73,1.63 1.73,3.17 2.98,4.56l-5.09,5.02L4,19l5,-5 3.11,3.11 0.76,-2.04zM18.5,10h-2L12,22h2l1.12,-3h4.75L21,22h2l-4.5,-12zM15.88,17l1.62,-4.33L19.12,17h-3.24z"/>
|
||||||
|
</vector>
|
||||||
5
mastodon/src/main/res/drawable/ic_translate_on.xml
Normal file
5
mastodon/src/main/res/drawable/ic_translate_on.xml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="24dp" android:tint="#000000"
|
||||||
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
|
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="?colorSecondary" android:pathData="M12.87,15.07l-2.54,-2.51 0.03,-0.03c1.74,-1.94 2.98,-4.17 3.71,-6.53L17,6L17,4h-7L10,2L8,2v2L1,4v1.99h11.17C11.5,7.92 10.44,9.75 9,11.35 8.07,10.32 7.3,9.19 6.69,8h-2c0.73,1.63 1.73,3.17 2.98,4.56l-5.09,5.02L4,19l5,-5 3.11,3.11 0.76,-2.04zM18.5,10h-2L12,22h2l1.12,-3h4.75L21,22h2l-4.5,-12zM15.88,17l1.62,-4.33L19.12,17h-3.24z"/>
|
||||||
|
</vector>
|
||||||
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>
|
||||||
@@ -15,6 +15,7 @@
|
|||||||
android:outlineProvider="background"
|
android:outlineProvider="background"
|
||||||
android:elevation="2dp">
|
android:elevation="2dp">
|
||||||
<ImageView
|
<ImageView
|
||||||
|
android:id="@+id/icon"
|
||||||
android:layout_width="24dp"
|
android:layout_width="24dp"
|
||||||
android:layout_height="24dp"
|
android:layout_height="24dp"
|
||||||
android:layout_margin="16dp"
|
android:layout_margin="16dp"
|
||||||
|
|||||||
@@ -26,8 +26,20 @@
|
|||||||
android:layout_width="36dp"
|
android:layout_width="36dp"
|
||||||
android:layout_height="36dp"
|
android:layout_height="36dp"
|
||||||
android:layout_marginTop="-6dp"
|
android:layout_marginTop="-6dp"
|
||||||
android:layout_marginRight="6dp"
|
android:layout_marginEnd="6dp"
|
||||||
android:layout_toLeftOf="@id/more"
|
android:layout_toStartOf="@id/more"
|
||||||
|
android:background="?android:selectableItemBackgroundBorderless"
|
||||||
|
android:scaleType="center"
|
||||||
|
android:src="@drawable/ic_visibility"
|
||||||
|
android:tint="?android:textColorSecondary" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/translate"
|
||||||
|
android:layout_width="36dp"
|
||||||
|
android:layout_height="36dp"
|
||||||
|
android:layout_marginTop="-6dp"
|
||||||
|
android:layout_marginEnd="6dp"
|
||||||
|
android:layout_toStartOf="@id/visibility"
|
||||||
android:background="?android:selectableItemBackgroundBorderless"
|
android:background="?android:selectableItemBackgroundBorderless"
|
||||||
android:scaleType="center"
|
android:scaleType="center"
|
||||||
android:src="@drawable/ic_visibility"
|
android:src="@drawable/ic_visibility"
|
||||||
@@ -46,7 +58,7 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="24dp"
|
android:layout_height="24dp"
|
||||||
android:layout_marginEnd="8dp"
|
android:layout_marginEnd="8dp"
|
||||||
android:layout_toStartOf="@id/visibility"
|
android:layout_toStartOf="@id/translate"
|
||||||
android:layout_toEndOf="@id/avatar">
|
android:layout_toEndOf="@id/avatar">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
@@ -78,7 +90,7 @@
|
|||||||
android:layout_height="20dp"
|
android:layout_height="20dp"
|
||||||
android:layout_below="@id/name_wrap"
|
android:layout_below="@id/name_wrap"
|
||||||
android:layout_marginEnd="8dp"
|
android:layout_marginEnd="8dp"
|
||||||
android:layout_toStartOf="@id/visibility"
|
android:layout_toStartOf="@id/translate"
|
||||||
android:layout_toEndOf="@id/avatar"
|
android:layout_toEndOf="@id/avatar"
|
||||||
android:layoutDirection="locale"
|
android:layoutDirection="locale"
|
||||||
android:orientation="horizontal">
|
android:orientation="horizontal">
|
||||||
|
|||||||
@@ -12,7 +12,8 @@
|
|||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/button"
|
android:id="@+id/button"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="48dp"
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingVertical="12dp"
|
||||||
android:outlineProvider="background"
|
android:outlineProvider="background"
|
||||||
android:elevation="2dp"
|
android:elevation="2dp"
|
||||||
android:background="@drawable/bg_poll_option_clickable"
|
android:background="@drawable/bg_poll_option_clickable"
|
||||||
@@ -25,7 +26,6 @@
|
|||||||
android:layout_height="24dp"
|
android:layout_height="24dp"
|
||||||
android:layout_marginStart="12dp"
|
android:layout_marginStart="12dp"
|
||||||
android:layout_gravity="center_vertical"
|
android:layout_gravity="center_vertical"
|
||||||
android:duplicateParentState="true"
|
|
||||||
android:tint="?colorDarkIcon"
|
android:tint="?colorDarkIcon"
|
||||||
android:src="@drawable/ic_poll_option_button"/>
|
android:src="@drawable/ic_poll_option_button"/>
|
||||||
|
|
||||||
@@ -48,9 +48,7 @@
|
|||||||
android:layout_marginLeft="16dp"
|
android:layout_marginLeft="16dp"
|
||||||
android:layout_marginRight="16dp"
|
android:layout_marginRight="16dp"
|
||||||
android:textAppearance="@style/m3_title_medium"
|
android:textAppearance="@style/m3_title_medium"
|
||||||
android:singleLine="true"
|
tools:text="scream into void. like this: aaaaaaaaaaaaaaaaaaaa"/>
|
||||||
android:ellipsize="end"
|
|
||||||
tools:text="scream into void"/>
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,8 @@
|
|||||||
android:layout_marginTop="6dp"
|
android:layout_marginTop="6dp"
|
||||||
android:layout_marginBottom="8dp"
|
android:layout_marginBottom="8dp"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content"
|
||||||
|
android:visibility="gone">
|
||||||
<View
|
<View
|
||||||
android:id="@+id/border_top"
|
android:id="@+id/border_top"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@@ -31,9 +32,10 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:textAppearance="@style/m3_title_medium"
|
android:textAppearance="@style/m3_title_medium"
|
||||||
android:background="?colorBackgroundLight"
|
android:background="@color/error_900"
|
||||||
tools:text="CW title"/>
|
tools:text="CW title"/>
|
||||||
|
|
||||||
|
|
||||||
<View
|
<View
|
||||||
android:id="@+id/border_bottom"
|
android:id="@+id/border_bottom"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@@ -49,6 +51,22 @@
|
|||||||
android:textSize="16sp"
|
android:textSize="16sp"
|
||||||
android:textAppearance="@style/m3_body_large"
|
android:textAppearance="@style/m3_body_large"
|
||||||
tools:text="setting up my mstdn"/>
|
tools:text="setting up my mstdn"/>
|
||||||
|
|
||||||
|
<!-- <Button-->
|
||||||
|
<!-- android:id="@+id/translate"-->
|
||||||
|
<!-- 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/pink_color" />-->
|
||||||
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -132,6 +132,7 @@
|
|||||||
android:outlineProvider="background"
|
android:outlineProvider="background"
|
||||||
android:elevation="2dp">
|
android:elevation="2dp">
|
||||||
<ImageView
|
<ImageView
|
||||||
|
android:id="@+id/add_poll_option_icon"
|
||||||
android:layout_width="24dp"
|
android:layout_width="24dp"
|
||||||
android:layout_height="24dp"
|
android:layout_height="24dp"
|
||||||
android:layout_margin="16dp"
|
android:layout_margin="16dp"
|
||||||
@@ -148,6 +149,39 @@
|
|||||||
android:textAppearance="@style/m3_label_large"
|
android:textAppearance="@style/m3_label_large"
|
||||||
android:textColor="?android:textColorPrimary"
|
android:textColor="?android:textColorPrimary"
|
||||||
tools:text="Duration: 7 days"/>
|
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/poll_allow_multiple" />
|
||||||
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<org.joinmastodon.android.ui.views.ComposeMediaLayout
|
<org.joinmastodon.android.ui.views.ComposeMediaLayout
|
||||||
@@ -187,7 +221,7 @@
|
|||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:textSize="16sp"
|
android:textSize="16sp"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:text="@string/mark_media_as_sensitive" />
|
android:text="@string/sk_mark_media_as_sensitive" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|||||||
18
mastodon/src/main/res/layout/fragment_lists.xml
Normal file
18
mastodon/src/main/res/layout/fragment_lists.xml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<?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"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<org.joinmastodon.android.ui.tabs.TabLayout
|
||||||
|
android:id="@+id/tabbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
app:tabGravity="fill"
|
||||||
|
app:tabIndicator="@drawable/mtrl_tabs_default_indicator"
|
||||||
|
app:tabIndicatorAnimationMode="elastic"
|
||||||
|
app:tabIndicatorColor="?android:textColorPrimary"
|
||||||
|
app:tabMode="fixed"
|
||||||
|
android:background="@drawable/bg_discover_tabs"/>
|
||||||
|
</LinearLayout>
|
||||||
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_moshidon_welcome.xml
Normal file
60
mastodon/src/main/res/layout/fragment_moshidon_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>
|
||||||
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"
|
style="?secondaryButtonStyle"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="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:drawableStart="@drawable/ic_fluent_dismiss_24_filled"
|
||||||
android:singleLine="true" />
|
android:singleLine="true" />
|
||||||
|
|
||||||
@@ -204,7 +204,7 @@
|
|||||||
android:id="@+id/accept_btn"
|
android:id="@+id/accept_btn"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="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:drawableStart="@drawable/ic_fluent_checkmark_24_filled"
|
||||||
android:singleLine="true" />
|
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>
|
||||||
@@ -28,4 +28,29 @@
|
|||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:paddingRight="16dp"/>
|
android:paddingRight="16dp"/>
|
||||||
|
|
||||||
|
<!-- <ImageView-->
|
||||||
|
<!-- android:id="@+id/edit"-->
|
||||||
|
<!-- android:layout_width="36dp"-->
|
||||||
|
<!-- android:layout_height="36dp"-->
|
||||||
|
<!-- android:layout_marginTop="0dp"-->
|
||||||
|
<!-- android:layout_marginEnd="6dp"-->
|
||||||
|
<!-- android:layout_toStartOf="@id/list_toggle"-->
|
||||||
|
<!-- android:background="?android:selectableItemBackgroundBorderless"-->
|
||||||
|
<!-- android:scaleType="center"-->
|
||||||
|
<!-- android:src="@drawable/ic_fluent_edit_24_regular"-->
|
||||||
|
<!-- android:tint="?android:textColorSecondary" />-->
|
||||||
|
|
||||||
|
<!-- <ImageView-->
|
||||||
|
<!-- android:id="@+id/delete"-->
|
||||||
|
<!-- android:layout_width="36dp"-->
|
||||||
|
<!-- android:layout_height="36dp"-->
|
||||||
|
<!-- android:layout_marginTop="0dp"-->
|
||||||
|
<!-- android:layout_marginEnd="6dp"-->
|
||||||
|
<!-- android:layout_toStartOf="@id/edit"-->
|
||||||
|
<!-- android:background="?android:selectableItemBackgroundBorderless"-->
|
||||||
|
<!-- android:scaleType="center"-->
|
||||||
|
<!-- android:src="@drawable/ic_fluent_delete_24_regular"-->
|
||||||
|
<!-- android:tint="?android:textColorSecondary" />-->
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
|||||||
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>
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
android:textSize="16sp"
|
android:textSize="16sp"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:text="@string/settings_color_picker"/>
|
android:text="@string/sk_settings_color_picker"/>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/color_picker_button"
|
android:id="@+id/color_picker_button"
|
||||||
@@ -41,6 +41,6 @@
|
|||||||
android:stateListAnimator="@null"
|
android:stateListAnimator="@null"
|
||||||
android:textColor="?android:textColorPrimary"
|
android:textColor="?android:textColorPrimary"
|
||||||
android:textSize="16sp"
|
android:textSize="16sp"
|
||||||
tools:text="@string/pink_color" />
|
tools:text="@string/sk_color_theme_pink" />
|
||||||
|
|
||||||
</org.joinmastodon.android.ui.views.AutoOrientationLinearLayout>
|
</org.joinmastodon.android.ui.views.AutoOrientationLinearLayout>
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="16dp"
|
||||||
android:layout_marginBottom="16dp"
|
android:layout_marginBottom="16dp"
|
||||||
android:textAppearance="@style/m3_body_medium"
|
android:textAppearance="@style/m3_body_medium"
|
||||||
tools:text="@string/update_available"/>
|
tools:text="@string/sk_update_available"/>
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<item android:id="@+id/purple_color" android:title="@string/purple_color"/>
|
<item android:id="@+id/purple_color" android:title="@string/sk_color_theme_purple"/>
|
||||||
<item android:id="@+id/pink_color" android:title="@string/pink_color"/>
|
<item android:id="@+id/pink_color" android:title="@string/sk_color_theme_pink"/>
|
||||||
<item android:id="@+id/green_color" android:title="@string/green_color"/>
|
<item android:id="@+id/green_color" android:title="@string/sk_color_theme_green"/>
|
||||||
<item android:id="@+id/blue_color" android:title="@string/blue_color"/>
|
<item android:id="@+id/blue_color" android:title="@string/sk_color_theme_blue"/>
|
||||||
<item android:id="@+id/orange_color" android:title="@string/orange_color"/>
|
<item android:id="@+id/orange_color" android:title="@string/sk_color_theme_brown"/>
|
||||||
<item android:id="@+id/yellow_color" android:title="@string/yellow_color"/>
|
<item android:id="@+id/yellow_color" android:title="@string/sk_color_theme_yellow"/>
|
||||||
|
<item android:id="@+id/m3_color" android:title="@string/sk_color_theme_material_you"/>
|
||||||
</menu>
|
</menu>
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
android:title="@string/visibility_public"/>
|
android:title="@string/visibility_public"/>
|
||||||
<item android:id="@+id/vis_unlisted"
|
<item android:id="@+id/vis_unlisted"
|
||||||
android:icon="@drawable/ic_fluent_people_community_24_regular"
|
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"
|
<item android:id="@+id/vis_followers"
|
||||||
android:icon="@drawable/ic_fluent_people_checkmark_24_regular"
|
android:icon="@drawable/ic_fluent_people_checkmark_24_regular"
|
||||||
android:title="@string/visibility_followers_only"/>
|
android:title="@string/visibility_followers_only"/>
|
||||||
|
|||||||
@@ -4,5 +4,6 @@
|
|||||||
android:id="@+id/follow_requests"
|
android:id="@+id/follow_requests"
|
||||||
android:icon="@drawable/ic_follow_requests_24_badged"
|
android:icon="@drawable/ic_follow_requests_24_badged"
|
||||||
android:showAsAction="always"
|
android:showAsAction="always"
|
||||||
android:title="@string/follow_requests" />
|
android:visible="false"
|
||||||
|
android:title="@string/sk_follow_requests" />
|
||||||
</menu>
|
</menu>
|
||||||
@@ -2,9 +2,9 @@
|
|||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<item android:id="@+id/edit" android:title="@string/edit"/>
|
<item android:id="@+id/edit" android:title="@string/edit"/>
|
||||||
<item android:id="@+id/delete" android:title="@string/delete"/>
|
<item android:id="@+id/delete" android:title="@string/delete"/>
|
||||||
<item android:id="@+id/delete_and_redraft" android:title="@string/delete_and_redraft"/>
|
<item android:id="@+id/delete_and_redraft" android:title="@string/sk_delete_and_redraft"/>
|
||||||
<item android:id="@+id/pin" android:title="@string/pin_post"/>
|
<item android:id="@+id/pin" android:title="@string/sk_pin_post"/>
|
||||||
<item android:id="@+id/unpin" android:title="@string/unpin_post"/>
|
<item android:id="@+id/unpin" android:title="@string/sk_unpin_post"/>
|
||||||
<item android:id="@+id/mute" android:title="@string/mute_user"/>
|
<item android:id="@+id/mute" android:title="@string/mute_user"/>
|
||||||
<item android:id="@+id/block" android:title="@string/block_user"/>
|
<item android:id="@+id/block" android:title="@string/block_user"/>
|
||||||
<item android:id="@+id/block_domain" android:title="@string/block_domain"/>
|
<item android:id="@+id/block_domain" android:title="@string/block_domain"/>
|
||||||
|
|||||||
@@ -6,6 +6,6 @@
|
|||||||
<item android:id="@+id/report" android:title="@string/report_user"/>
|
<item android:id="@+id/report" android:title="@string/report_user"/>
|
||||||
<item android:id="@+id/block_domain" android:title="@string/block_domain"/>
|
<item android:id="@+id/block_domain" android:title="@string/block_domain"/>
|
||||||
<item android:id="@+id/hide_boosts" android:title="@string/hide_boosts_from_user"/>
|
<item android:id="@+id/hide_boosts" android:title="@string/hide_boosts_from_user"/>
|
||||||
<item android:id="@+id/manage_user_lists" android:title="@string/lists_with_user"/>
|
<item android:id="@+id/manage_user_lists" android:title="@string/sk_lists_with_user"/>
|
||||||
<item android:id="@+id/open_in_browser" android:title="@string/open_in_browser"/>
|
<item android:id="@+id/open_in_browser" android:title="@string/open_in_browser"/>
|
||||||
</menu>
|
</menu>
|
||||||
@@ -3,9 +3,7 @@
|
|||||||
<string name="get_started">الخطوات الأولى</string>
|
<string name="get_started">الخطوات الأولى</string>
|
||||||
<string name="log_in">تسجيلُ الدخول</string>
|
<string name="log_in">تسجيلُ الدخول</string>
|
||||||
<string name="next">التالي</string>
|
<string name="next">التالي</string>
|
||||||
<string name="loading_instance">يَجري الحُصُول على معلومات المَثيل…</string>
|
|
||||||
<string name="error">خطأ</string>
|
<string name="error">خطأ</string>
|
||||||
<string name="not_a_mastodon_instance">%s لا يبدو كمثيل ماستدون.</string>
|
|
||||||
<string name="ok">حسنًا</string>
|
<string name="ok">حسنًا</string>
|
||||||
<string name="preparing_auth">جَارٍ الإعدَادُ لِلمُصادَقَة…</string>
|
<string name="preparing_auth">جَارٍ الإعدَادُ لِلمُصادَقَة…</string>
|
||||||
<string name="finishing_auth">يُنهي المصادقة…</string>
|
<string name="finishing_auth">يُنهي المصادقة…</string>
|
||||||
|
|||||||
3
mastodon/src/main/res/values-ar-rSA/strings_sk.xml
Normal file
3
mastodon/src/main/res/values-ar-rSA/strings_sk.xml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
</resources>
|
||||||
@@ -1,5 +1,14 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
|
<string name="get_started">Пачаць</string>
|
||||||
|
<string name="log_in">Увайсці</string>
|
||||||
|
<string name="next">Далей</string>
|
||||||
|
<string name="error">Памылка</string>
|
||||||
|
<string name="ok">Добра</string>
|
||||||
|
<string name="notifications">Апавяшчэнні</string>
|
||||||
|
<string name="share_toot_title">Абагуліць</string>
|
||||||
|
<string name="settings">Налады</string>
|
||||||
|
<string name="cancel">Скасаваць</string>
|
||||||
<!-- translators: %,d is a valid placeholder, it formats the number with locale-dependent grouping separators -->
|
<!-- translators: %,d is a valid placeholder, it formats the number with locale-dependent grouping separators -->
|
||||||
<!-- %s is version like 1.2.3 -->
|
<!-- %s is version like 1.2.3 -->
|
||||||
<!-- %s is version like 1.2.3 -->
|
<!-- %s is version like 1.2.3 -->
|
||||||
|
|||||||
3
mastodon/src/main/res/values-be-rBY/strings_sk.xml
Normal file
3
mastodon/src/main/res/values-be-rBY/strings_sk.xml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
</resources>
|
||||||
3
mastodon/src/main/res/values-bn-rBD/strings_sk.xml
Normal file
3
mastodon/src/main/res/values-bn-rBD/strings_sk.xml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
</resources>
|
||||||
@@ -3,9 +3,7 @@
|
|||||||
<string name="get_started">Kreni</string>
|
<string name="get_started">Kreni</string>
|
||||||
<string name="log_in">Loguj se</string>
|
<string name="log_in">Loguj se</string>
|
||||||
<string name="next">Dalje</string>
|
<string name="next">Dalje</string>
|
||||||
<string name="loading_instance">Čekamo potrebne informacije…</string>
|
|
||||||
<string name="error">Greška</string>
|
<string name="error">Greška</string>
|
||||||
<string name="not_a_mastodon_instance">%s ne izgleda kao Mastodon platforma.</string>
|
|
||||||
<string name="ok">OK</string>
|
<string name="ok">OK</string>
|
||||||
<string name="preparing_auth">Pripremamo autorizaciju…</string>
|
<string name="preparing_auth">Pripremamo autorizaciju…</string>
|
||||||
<string name="finishing_auth">Završavamo autorizaciju…</string>
|
<string name="finishing_auth">Završavamo autorizaciju…</string>
|
||||||
|
|||||||
3
mastodon/src/main/res/values-bs-rBA/strings_sk.xml
Normal file
3
mastodon/src/main/res/values-bs-rBA/strings_sk.xml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
</resources>
|
||||||
@@ -3,17 +3,15 @@
|
|||||||
<string name="get_started">Comença</string>
|
<string name="get_started">Comença</string>
|
||||||
<string name="log_in">Inicia sessió</string>
|
<string name="log_in">Inicia sessió</string>
|
||||||
<string name="next">Següent</string>
|
<string name="next">Següent</string>
|
||||||
<string name="loading_instance">Obtenint informació sobre la instància…</string>
|
|
||||||
<string name="error">Error</string>
|
<string name="error">Error</string>
|
||||||
<string name="not_a_mastodon_instance">Sembla que %s no és una instància Mastodon.</string>
|
|
||||||
<string name="ok">D\'acord</string>
|
<string name="ok">D\'acord</string>
|
||||||
<string name="preparing_auth">Preparant a l\'autenticació…</string>
|
<string name="preparing_auth">Preparant l\'autenticació…</string>
|
||||||
<string name="finishing_auth">Finalitzant autentificació…</string>
|
<string name="finishing_auth">Finalitzant l\'autenticació…</string>
|
||||||
<string name="user_boosted">%s ha impulsat</string>
|
<string name="user_boosted">%s ha impulsat</string>
|
||||||
<string name="in_reply_to">En resposta a %s</string>
|
<string name="in_reply_to">En resposta a %s</string>
|
||||||
<string name="notifications">Notificacions</string>
|
<string name="notifications">Notificacions</string>
|
||||||
<string name="user_followed_you">t\'ha seguit</string>
|
<string name="user_followed_you">t\'ha seguit</string>
|
||||||
<string name="user_sent_follow_request">t\'ha enviat una sol·licitud de seguiment</string>
|
<string name="user_sent_follow_request">ha sol·licitat seguir-te</string>
|
||||||
<string name="user_favorited">ha afavorit la teva publicació</string>
|
<string name="user_favorited">ha afavorit la teva publicació</string>
|
||||||
<string name="notification_boosted">ha impulsat la teva publicació</string>
|
<string name="notification_boosted">ha impulsat la teva publicació</string>
|
||||||
<string name="poll_ended">l\'enquesta ha finalitzat</string>
|
<string name="poll_ended">l\'enquesta ha finalitzat</string>
|
||||||
@@ -24,7 +22,7 @@
|
|||||||
<string name="share_toot_title">Comparteix</string>
|
<string name="share_toot_title">Comparteix</string>
|
||||||
<string name="settings">Configuració</string>
|
<string name="settings">Configuració</string>
|
||||||
<string name="publish">Publica</string>
|
<string name="publish">Publica</string>
|
||||||
<string name="discard_draft">Voleu descartar l\'esborrany?</string>
|
<string name="discard_draft">Vols descartar l\'esborrany?</string>
|
||||||
<string name="discard">Descarta</string>
|
<string name="discard">Descarta</string>
|
||||||
<string name="cancel">Cancel·la</string>
|
<string name="cancel">Cancel·la</string>
|
||||||
<plurals name="followers">
|
<plurals name="followers">
|
||||||
@@ -49,14 +47,14 @@
|
|||||||
<string name="mention_user">Menciona %s</string>
|
<string name="mention_user">Menciona %s</string>
|
||||||
<string name="share_user">Comparteix %s</string>
|
<string name="share_user">Comparteix %s</string>
|
||||||
<string name="mute_user">Silencia %s</string>
|
<string name="mute_user">Silencia %s</string>
|
||||||
<string name="unmute_user">Deixar de silenciar %s</string>
|
<string name="unmute_user">Deixa de silenciar %s</string>
|
||||||
<string name="block_user">Bloca %s</string>
|
<string name="block_user">Bloca %s</string>
|
||||||
<string name="unblock_user">Desbloca %s</string>
|
<string name="unblock_user">Desbloca %s</string>
|
||||||
<string name="report_user">Denuncia %s</string>
|
<string name="report_user">Denuncia %s</string>
|
||||||
<string name="block_domain">Bloca %s</string>
|
<string name="block_domain">Bloca %s</string>
|
||||||
<string name="unblock_domain">Desbloca %s</string>
|
<string name="unblock_domain">Desbloca %s</string>
|
||||||
<plurals name="x_posts">
|
<plurals name="x_posts">
|
||||||
<item quantity="one">%,d entrada</item>
|
<item quantity="one">%,d publicació</item>
|
||||||
<item quantity="other">%,d publicacions</item>
|
<item quantity="other">%,d publicacions</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
<string name="profile_joined">S\'ha unit</string>
|
<string name="profile_joined">S\'ha unit</string>
|
||||||
@@ -101,12 +99,12 @@
|
|||||||
<item quantity="other">%,d votants</item>
|
<item quantity="other">%,d votants</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
<string name="poll_closed">Finalitzada</string>
|
<string name="poll_closed">Finalitzada</string>
|
||||||
<string name="confirm_mute_title">Silenciar el compte</string>
|
<string name="confirm_mute_title">Silencia el compte</string>
|
||||||
<string name="confirm_mute">Confirma per silenciar %s</string>
|
<string name="confirm_mute">Confirma per a silenciar %s</string>
|
||||||
<string name="do_mute">Silenciar</string>
|
<string name="do_mute">Silencia</string>
|
||||||
<string name="confirm_unmute_title">Deixar de silenciar el compte</string>
|
<string name="confirm_unmute_title">Deixar de silenciar el compte</string>
|
||||||
<string name="confirm_unmute">Confirma per deixar de silenciar %s</string>
|
<string name="confirm_unmute">Confirma per deixar de silenciar %s</string>
|
||||||
<string name="do_unmute">Deixar de silenciar</string>
|
<string name="do_unmute">Deixa de silenciar</string>
|
||||||
<string name="confirm_block_title">Bloca el compte</string>
|
<string name="confirm_block_title">Bloca el compte</string>
|
||||||
<string name="confirm_block_domain_title">Bloca el domini</string>
|
<string name="confirm_block_domain_title">Bloca el domini</string>
|
||||||
<string name="confirm_block">Confirma per blocar %s</string>
|
<string name="confirm_block">Confirma per blocar %s</string>
|
||||||
@@ -135,12 +133,12 @@
|
|||||||
<string name="all_notifications">Totes</string>
|
<string name="all_notifications">Totes</string>
|
||||||
<string name="mentions">Mencions</string>
|
<string name="mentions">Mencions</string>
|
||||||
<plurals name="x_people_talking">
|
<plurals name="x_people_talking">
|
||||||
<item quantity="one">%d persona parla</item>
|
<item quantity="one">%d persona està parlant-ne</item>
|
||||||
<item quantity="other">%d persones estan parlant</item>
|
<item quantity="other">%d persones estan parlant-ne</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
<plurals name="discussed_x_times">
|
<plurals name="discussed_x_times">
|
||||||
<item quantity="one">S\'ha comentat %d cop</item>
|
<item quantity="one">S\'ha comentat %d vegada</item>
|
||||||
<item quantity="other">S\'ha comentat %d cops</item>
|
<item quantity="other">S\'ha comentat %d vegades</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
<string name="report_title">Denuncia %s</string>
|
<string name="report_title">Denuncia %s</string>
|
||||||
<string name="report_choose_reason">Quin és el problema amb aquesta publicació?</string>
|
<string name="report_choose_reason">Quin és el problema amb aquesta publicació?</string>
|
||||||
@@ -160,7 +158,7 @@
|
|||||||
<string name="report_choose_posts_subtitle">Selecciona tots els aplicables</string>
|
<string name="report_choose_posts_subtitle">Selecciona tots els aplicables</string>
|
||||||
<string name="report_comment_title">Hi ha res més que hauríem de saber?</string>
|
<string name="report_comment_title">Hi ha res més que hauríem de saber?</string>
|
||||||
<string name="report_comment_hint">Comentaris addicionals</string>
|
<string name="report_comment_hint">Comentaris addicionals</string>
|
||||||
<string name="sending_report">Enviant informe…</string>
|
<string name="sending_report">S\'està enviant l\'informe…</string>
|
||||||
<string name="report_sent_title">Gràcies per informar, ho investigarem.</string>
|
<string name="report_sent_title">Gràcies per informar, ho investigarem.</string>
|
||||||
<string name="report_sent_subtitle">Mentre ho revisem, pots prendre mesures contra %s.</string>
|
<string name="report_sent_subtitle">Mentre ho revisem, pots prendre mesures contra %s.</string>
|
||||||
<string name="unfollow_user">Deixar de seguir %s</string>
|
<string name="unfollow_user">Deixar de seguir %s</string>
|
||||||
@@ -176,7 +174,7 @@
|
|||||||
<string name="instance_rules_title">Algunes normes bàsiques</string>
|
<string name="instance_rules_title">Algunes normes bàsiques</string>
|
||||||
<string name="instance_rules_subtitle">Pren un minut per revisar les normes establertes i aplicades pels administradors de %s.</string>
|
<string name="instance_rules_subtitle">Pren un minut per revisar les normes establertes i aplicades pels administradors de %s.</string>
|
||||||
<string name="signup_title">Deixa que et posem en marxa a %s</string>
|
<string name="signup_title">Deixa que et posem en marxa a %s</string>
|
||||||
<string name="edit_photo">editar</string>
|
<string name="edit_photo">edita</string>
|
||||||
<string name="display_name">nom visible</string>
|
<string name="display_name">nom visible</string>
|
||||||
<string name="username">nom d\'usuari</string>
|
<string name="username">nom d\'usuari</string>
|
||||||
<string name="email">correu electrònic</string>
|
<string name="email">correu electrònic</string>
|
||||||
@@ -223,9 +221,9 @@
|
|||||||
<string name="notification_type_mention">Mencions</string>
|
<string name="notification_type_mention">Mencions</string>
|
||||||
<string name="notification_type_poll">Enquestes</string>
|
<string name="notification_type_poll">Enquestes</string>
|
||||||
<string name="choose_account">Selecciona un compte</string>
|
<string name="choose_account">Selecciona un compte</string>
|
||||||
<string name="err_not_logged_in">Si us plau, inicia sessió primer a Mastodon</string>
|
<string name="err_not_logged_in">Primer inicia sessió a Mastodon</string>
|
||||||
<plurals name="cant_add_more_than_x_attachments">
|
<plurals name="cant_add_more_than_x_attachments">
|
||||||
<item quantity="one">No pots afegir més de %d fitxer multimèdia</item>
|
<item quantity="one">No pots afegir més d\'%d fitxer multimèdia</item>
|
||||||
<item quantity="other">No pots afegir més de %d fitxers multimèdia</item>
|
<item quantity="other">No pots afegir més de %d fitxers multimèdia</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
<string name="media_attachment_unsupported_type">El tipus de fitxer %s no és compatible</string>
|
<string name="media_attachment_unsupported_type">El tipus de fitxer %s no és compatible</string>
|
||||||
@@ -236,8 +234,8 @@
|
|||||||
<string name="theme_dark">Fosc</string>
|
<string name="theme_dark">Fosc</string>
|
||||||
<string name="theme_true_black">Mode negre pur</string>
|
<string name="theme_true_black">Mode negre pur</string>
|
||||||
<string name="settings_behavior">Comportament</string>
|
<string name="settings_behavior">Comportament</string>
|
||||||
<string name="settings_gif">Reproduir emojis i avatar animats</string>
|
<string name="settings_gif">Reprodueix emojis i avatar animats</string>
|
||||||
<string name="settings_custom_tabs">Utilitzar el navegador intern</string>
|
<string name="settings_custom_tabs">Utilitza el navegador intern</string>
|
||||||
<string name="settings_notifications">Notificacions</string>
|
<string name="settings_notifications">Notificacions</string>
|
||||||
<string name="notify_me_when">Notifica\'m quan</string>
|
<string name="notify_me_when">Notifica\'m quan</string>
|
||||||
<string name="notify_anyone">qualsevol</string>
|
<string name="notify_anyone">qualsevol</string>
|
||||||
@@ -259,7 +257,7 @@
|
|||||||
<string name="media_cache_cleared">S\'ha esborrat la memòria cau multimèdia</string>
|
<string name="media_cache_cleared">S\'ha esborrat la memòria cau multimèdia</string>
|
||||||
<string name="confirm_log_out">Segur que vols tancar la sessió?</string>
|
<string name="confirm_log_out">Segur que vols tancar la sessió?</string>
|
||||||
<string name="sensitive_content">Contingut sensible</string>
|
<string name="sensitive_content">Contingut sensible</string>
|
||||||
<string name="sensitive_content_explain">L\'autor va marcar aquest mitjà com a sensible. Toca per a mostrar-lo.</string>
|
<string name="sensitive_content_explain">L\'autor ha marcat aquest mitjà com a sensible. Toca per a mostrar-lo.</string>
|
||||||
<string name="media_hidden">Toca per a mostrar</string>
|
<string name="media_hidden">Toca per a mostrar</string>
|
||||||
<string name="avatar_description">Vés al perfil de: %s</string>
|
<string name="avatar_description">Vés al perfil de: %s</string>
|
||||||
<string name="more_options">Més opcions</string>
|
<string name="more_options">Més opcions</string>
|
||||||
@@ -368,7 +366,7 @@
|
|||||||
<string name="file_size_gb">%.2f GB</string>
|
<string name="file_size_gb">%.2f GB</string>
|
||||||
<string name="file_upload_progress">%1$s de %2$s</string>
|
<string name="file_upload_progress">%1$s de %2$s</string>
|
||||||
<string name="file_upload_time_remaining">%s restants</string>
|
<string name="file_upload_time_remaining">%s restants</string>
|
||||||
<string name="upload_error_connection_lost">El teu dispositiu ha perdut la connexió a internet</string>
|
<string name="upload_error_connection_lost">El dispositiu ha perdut la connexió a Internet</string>
|
||||||
<string name="upload_processing">S\'està processant…</string>
|
<string name="upload_processing">S\'està processant…</string>
|
||||||
<!-- %s is version like 1.2.3 -->
|
<!-- %s is version like 1.2.3 -->
|
||||||
<string name="update_available">Mastodon per a Android %s està preparat per a baixar-se.</string>
|
<string name="update_available">Mastodon per a Android %s està preparat per a baixar-se.</string>
|
||||||
@@ -380,9 +378,10 @@
|
|||||||
<string name="privacy_policy_title">Mastodon i la teva privacitat</string>
|
<string name="privacy_policy_title">Mastodon i la teva privacitat</string>
|
||||||
<string name="privacy_policy_subtitle">Tot i que l\'aplicació Mastodon no recull cap dada, el servidor mitjançant el qual et registres pot tenir una política diferent. Pren un minut per revisar i acceptar la política de privadesa de l\'aplicació Mastodon i la política de privadesa del teu servidor.</string>
|
<string name="privacy_policy_subtitle">Tot i que l\'aplicació Mastodon no recull cap dada, el servidor mitjançant el qual et registres pot tenir una política diferent. Pren un minut per revisar i acceptar la política de privadesa de l\'aplicació Mastodon i la política de privadesa del teu servidor.</string>
|
||||||
<string name="i_agree">D’acord</string>
|
<string name="i_agree">D’acord</string>
|
||||||
<!-- Missing strings -->
|
|
||||||
<string name="bookmarks">Marcadors</string>
|
|
||||||
<string name="pinned_posts">Fixat</string>
|
|
||||||
<string name="empty_list">Aquesta llista està buida</string>
|
<string name="empty_list">Aquesta llista està buida</string>
|
||||||
<string name="instance_signup_closed">Aquest servidor no accepta nous registres.</string>
|
<string name="instance_signup_closed">Aquest servidor no accepta nous registres.</string>
|
||||||
|
<string name="text_copied">Copiat al porta-retalls</string>
|
||||||
|
<string name="add_bookmark">Marca</string>
|
||||||
|
<string name="remove_bookmark">Elimina el marcador</string>
|
||||||
|
<string name="bookmarks">Marcadors</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
50
mastodon/src/main/res/values-ca-rES/strings_sk.xml
Normal file
50
mastodon/src/main/res/values-ca-rES/strings_sk.xml
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="sk_pinned_posts">Fixat</string>
|
||||||
|
<string name="sk_delete_and_redraft">Elimina i torna a escriure</string>
|
||||||
|
<string name="sk_confirm_delete_and_redraft_title">Eliminar i tornar a escriure</string>
|
||||||
|
<string name="sk_confirm_pin_post">Vols fixar aquesta publicació al teu perfil\?</string>
|
||||||
|
<string name="sk_pinning">S\'està fixant…</string>
|
||||||
|
<string name="sk_unpin_post">Deixa de fixar</string>
|
||||||
|
<string name="sk_confirm_unpin_post_title">Deixar de fixar al perfil</string>
|
||||||
|
<string name="sk_settings_show_replies">Mostra les respostes</string>
|
||||||
|
<string name="sk_settings_load_new_posts">Carrega automàticament les publicacions noves</string>
|
||||||
|
<string name="sk_federated_timeline">Federació</string>
|
||||||
|
<string name="sk_federated_timeline_info_banner">Aquestes són les publicacions més recents de gent de la teua federació.</string>
|
||||||
|
<string name="sk_update_ready">Moshidon %s s\'ha baixat i està preparat per a instal·lar-se.</string>
|
||||||
|
<string name="sk_accept_follow_request">Accepta la sol·licitud</string>
|
||||||
|
<string name="sk_disable_marquee">Desactiva el desplaçament de text a les barres de títol</string>
|
||||||
|
<string name="sk_user_post_notifications_on">Notificacions activades per a publicacions de: %s</string>
|
||||||
|
<string name="sk_app_name">Moshidon</string>
|
||||||
|
<string name="sk_pin_post">Fixa al perfil</string>
|
||||||
|
<string name="sk_confirm_pin_post_title">Fixar al perfil</string>
|
||||||
|
<string name="sk_confirm_delete_and_redraft">Segur que vols eliminar i tornar a escriure aquesta publicació\?</string>
|
||||||
|
<string name="sk_confirm_unpin_post">Segur que vols deixar de fixar aquesta publicació\?</string>
|
||||||
|
<string name="sk_unpinning">S\'està deixant de fixar…</string>
|
||||||
|
<string name="sk_image_description">Descripció de la imatge</string>
|
||||||
|
<string name="sk_visibility_unlisted">No llistat</string>
|
||||||
|
<string name="sk_settings_show_boosts">Mostra els impulsos</string>
|
||||||
|
<string name="sk_settings_show_interaction_counts">Mostra el recompte d\'interaccions</string>
|
||||||
|
<string name="sk_settings_app_version">Moshidon v. %1$s (%2$d)</string>
|
||||||
|
<string name="sk_user_post_notifications_off">Notificacions desactivades per a publicacions de: %s</string>
|
||||||
|
<string name="sk_mark_media_as_sensitive">Marca el contingut com a sensible</string>
|
||||||
|
<string name="sk_update_available">Moshidon %s està preparat per a baixar-se.</string>
|
||||||
|
<string name="sk_check_for_update">Comprova actualitzacions</string>
|
||||||
|
<string name="sk_no_update_available">No hi ha cap actualització disponible</string>
|
||||||
|
<string name="sk_list_timelines">Llistes</string>
|
||||||
|
<string name="sk_follow_requests">Sol·licituds de seguiment</string>
|
||||||
|
<string name="sk_reject_follow_request">Rebutja la sol·licitud</string>
|
||||||
|
<string name="sk_lists_with_user">Llistes amb %s</string>
|
||||||
|
<string name="sk_settings_always_reveal_content_warnings">Mostra sempre els avisos de contingut</string>
|
||||||
|
<string name="sk_settings_contribute">Contribueix a Moshidon</string>
|
||||||
|
<string name="sk_settings_show_federated_timeline">Mostra la línia de temps federada</string>
|
||||||
|
<string name="sk_notification_type_status">Publicacions</string>
|
||||||
|
<string name="sk_notify_posts">Notificacions de publicacions</string>
|
||||||
|
<string name="sk_settings_color_picker">Color de tema</string>
|
||||||
|
<string name="sk_color_theme_pink">Rosa</string>
|
||||||
|
<string name="sk_color_theme_purple">Lila</string>
|
||||||
|
<string name="sk_color_theme_green">Verd</string>
|
||||||
|
<string name="sk_color_theme_blue">Blau</string>
|
||||||
|
<string name="sk_color_theme_brown">Marró</string>
|
||||||
|
<string name="sk_color_theme_yellow">Groc</string>
|
||||||
|
</resources>
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user