Compare commits
298 Commits
feature/re
...
documentat
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
be5ed6e224 | ||
|
|
3b32fe3663 | ||
|
|
e15b752bad | ||
|
|
5928cf675a | ||
|
|
7655a5a3ae | ||
|
|
b5e0fca9c0 | ||
|
|
bac6f58115 | ||
|
|
e2a20f9599 | ||
|
|
900823e0dd | ||
|
|
918bda54d6 | ||
|
|
cc1d1180e8 | ||
|
|
006c0d00f2 | ||
|
|
741a55110a | ||
|
|
f7dcb754ed | ||
|
|
f5e50bf668 | ||
|
|
4ad895ab71 | ||
|
|
0fccd0ab37 | ||
|
|
4fc6a8a2a5 | ||
|
|
397d768f3a | ||
|
|
f76eba894a | ||
|
|
42ae74f1a7 | ||
|
|
9d09a904ab | ||
|
|
5366c92b4d | ||
|
|
c047c53aac | ||
|
|
489a49ca40 | ||
|
|
ed44a4dac4 | ||
|
|
dc5c2dd907 | ||
|
|
cd86d04c9f | ||
|
|
d0c64fcdf5 | ||
|
|
dfff3b8bcf | ||
|
|
4e13f868fd | ||
|
|
251ffbba8d | ||
|
|
01ae9ba7f5 | ||
|
|
df1df28e23 | ||
|
|
23b2603a5f | ||
|
|
1e6dadd7ab | ||
|
|
26ab5a7f55 | ||
|
|
d7b76ed70a | ||
|
|
bc7946dc23 | ||
|
|
3a34599c82 | ||
|
|
0a6dd8a754 | ||
|
|
a61ece13af | ||
|
|
89e58fa947 | ||
|
|
4c47bb0768 | ||
|
|
82005bf3bd | ||
|
|
03d89ae93c | ||
|
|
2e84faa505 | ||
|
|
e7e8d13d9e | ||
|
|
a683c2cb11 | ||
|
|
22febe019b | ||
|
|
addf7de316 | ||
|
|
e0f4f87086 | ||
|
|
7917e34568 | ||
|
|
44d4eada51 | ||
|
|
40bfdea5b1 | ||
|
|
d7287441ca | ||
|
|
55138c1e86 | ||
|
|
0aef680572 | ||
|
|
6dc37d6bde | ||
|
|
60ea7cedf6 | ||
|
|
c986b10e14 | ||
|
|
d52174bd9e | ||
|
|
c65d138911 | ||
|
|
ad9bb8ad58 | ||
|
|
5b391b44d3 | ||
|
|
fe20fe4254 | ||
|
|
63e536c66c | ||
|
|
b5a08b1b98 | ||
|
|
226e2a7cdc | ||
|
|
4d7c4aed4c | ||
|
|
c9bcd000c3 | ||
|
|
de42145f30 | ||
|
|
7bcdd6070a | ||
|
|
8a215e90d0 | ||
|
|
b736fa18bb | ||
|
|
43c19e4942 | ||
|
|
ffc18029bb | ||
|
|
b88b3d15f8 | ||
|
|
c817886a2d | ||
|
|
aae239494e | ||
|
|
b0b2daa5d5 | ||
|
|
eea2e38f1b | ||
|
|
f894ecd25b | ||
|
|
e0b6ed7103 | ||
|
|
a78e75747a | ||
|
|
3b25e367bb | ||
|
|
08b29dff3d | ||
|
|
2f2e053d26 | ||
|
|
191d582c30 | ||
|
|
8d3380ff6e | ||
|
|
ba85d18574 | ||
|
|
0f53b17515 | ||
|
|
cb9c869712 | ||
|
|
aa3d9e7b8f | ||
|
|
b3a9b5824d | ||
|
|
218503a739 | ||
|
|
13480ce575 | ||
|
|
581ee53f12 | ||
|
|
02545f3dd2 | ||
|
|
37fe0a1145 | ||
|
|
e753eea23f | ||
|
|
9d8cf2dc02 | ||
|
|
de589d1fdf | ||
|
|
573872291c | ||
|
|
e4232fb3de | ||
|
|
04f805e846 | ||
|
|
ae2769a1b9 | ||
|
|
d8d6cd8258 | ||
|
|
8bc395cb16 | ||
|
|
6d06c4d740 | ||
|
|
fc8784393b | ||
|
|
ce92ffe6dd | ||
|
|
1953cab173 | ||
|
|
2525714bc9 | ||
|
|
debdda5fed | ||
|
|
c4c17e3aea | ||
|
|
04699c3684 | ||
|
|
d78fce92c6 | ||
|
|
73a9073ee4 | ||
|
|
ef541058c4 | ||
|
|
bf4c905674 | ||
|
|
d256c759ee | ||
|
|
6b8418845e | ||
|
|
47a3834716 | ||
|
|
b540729f6b | ||
|
|
98e24a5285 | ||
|
|
ec1e850549 | ||
|
|
b6186a349f | ||
|
|
730aa28979 | ||
|
|
d15d8a0169 | ||
|
|
6077bf4b55 | ||
|
|
b568dac138 | ||
|
|
2c7df11e84 | ||
|
|
29777c2513 | ||
|
|
f70a9cbe3f | ||
|
|
7bc49aa21c | ||
|
|
4d0790dcec | ||
|
|
bb7b18e148 | ||
|
|
b7b646f03a | ||
|
|
7c59b700da | ||
|
|
cf64e75e2f | ||
|
|
e121fccfc1 | ||
|
|
5a1a2ac9f5 | ||
|
|
00d6f29285 | ||
|
|
a649ea0a00 | ||
|
|
180cf3c902 | ||
|
|
ba997903b6 | ||
|
|
8a4231686b | ||
|
|
c1252638c6 | ||
|
|
5515b12fd9 | ||
|
|
9fe7ebf3bf | ||
|
|
09733f3343 | ||
|
|
249dd5c0e5 | ||
|
|
181fa1383b | ||
|
|
ee1a4edbe1 | ||
|
|
f3081f3f6c | ||
|
|
6a12708905 | ||
|
|
04a8e33385 | ||
|
|
d79c183685 | ||
|
|
0c6efac46a | ||
|
|
6a5a506abf | ||
|
|
746717a875 | ||
|
|
e55dd0eaca | ||
|
|
6af22b1f0e | ||
|
|
7d840502d4 | ||
|
|
665a0176b9 | ||
|
|
ef4422828b | ||
|
|
002fc0897c | ||
|
|
0a21c90efa | ||
|
|
3df62d2516 | ||
|
|
ee18236ec8 | ||
|
|
15fcb0e25d | ||
|
|
2dae662333 | ||
|
|
3ad46926f1 | ||
|
|
2385d102ae | ||
|
|
0a17ceb984 | ||
|
|
4ef18f1f4a | ||
|
|
0de227ab9c | ||
|
|
19cb8703a6 | ||
|
|
e18567dd82 | ||
|
|
bfb3bcdbfb | ||
|
|
565cd14d88 | ||
|
|
ebc37eac75 | ||
|
|
c3702db577 | ||
|
|
e15c4fa342 | ||
|
|
8330b9f1c5 | ||
|
|
f759150982 | ||
|
|
6af177b596 | ||
|
|
657bb94975 | ||
|
|
3c946212b1 | ||
|
|
b4e80f7fca | ||
|
|
c9efc2cb2b | ||
|
|
0d62e33dc7 | ||
|
|
ac88b9e19c | ||
|
|
871dfda79e | ||
|
|
e0c2c208ae | ||
|
|
22ac112bdb | ||
|
|
afd0cca176 | ||
|
|
c083c8bce5 | ||
|
|
63bde032b3 | ||
|
|
49492c0788 | ||
|
|
b439c64add | ||
|
|
1868bfe8e3 | ||
|
|
f240a3d996 | ||
|
|
788e5bd12e | ||
|
|
a55fed4502 | ||
|
|
a8fdaf1a47 | ||
|
|
4a758bd488 | ||
|
|
2c9731ec2a | ||
|
|
eef33266fc | ||
|
|
58d2c3e5a6 | ||
|
|
9e6a355db0 | ||
|
|
0d10e09fd6 | ||
|
|
f85bb995ba | ||
|
|
268e5639f6 | ||
|
|
51809df8ca | ||
|
|
94fb676b0c | ||
|
|
a47106594b | ||
|
|
d93d66f702 | ||
|
|
b2f9f7ae54 | ||
|
|
de7b908c78 | ||
|
|
75d3c2fdce | ||
|
|
ea1b6c5835 | ||
|
|
cb7887da41 | ||
|
|
a80313ee6b | ||
|
|
e1a821bc43 | ||
|
|
924ea2d03a | ||
|
|
55270fe654 | ||
|
|
a125fab57b | ||
|
|
395ee0aa99 | ||
|
|
0f50fa6ba1 | ||
|
|
adb7df3c71 | ||
|
|
5d7bcb629b | ||
|
|
a00f1417d2 | ||
|
|
8efd7e8ebf | ||
|
|
b016d277e0 | ||
|
|
fdb39617d1 | ||
|
|
89f83fbf62 | ||
|
|
ecee9e01a6 | ||
|
|
20dc9bb8b9 | ||
|
|
2c47d0e9ed | ||
|
|
8e13d52e51 | ||
|
|
cc40198c9e | ||
|
|
290897ea41 | ||
|
|
b9e1c84304 | ||
|
|
3c44c80e2e | ||
|
|
dffa4e4594 | ||
|
|
fa2d9fec58 | ||
|
|
09c1a2cfa0 | ||
|
|
d1f90eb231 | ||
|
|
1f7d97134b | ||
|
|
79be91784d | ||
|
|
de2654def3 | ||
|
|
56343dacff | ||
|
|
0e677f8ce7 | ||
|
|
aa911896d6 | ||
|
|
c62a8635b9 | ||
|
|
4e900247c5 | ||
|
|
b3bd62bc6c | ||
|
|
8e5fd48ecd | ||
|
|
b2bca9dd2c | ||
|
|
b8c0dc3181 | ||
|
|
cccdc5292e | ||
|
|
76d77a0e7a | ||
|
|
e737f4bf9a | ||
|
|
391db2f1c9 | ||
|
|
359d61183c | ||
|
|
46fd05d88e | ||
|
|
cde22a0945 | ||
|
|
111b7e25c5 | ||
|
|
4f8d8f0c8d | ||
|
|
915b0603d0 | ||
|
|
075aab8074 | ||
|
|
6ebe4c86af | ||
|
|
0925c8c582 | ||
|
|
a683fdce62 | ||
|
|
b958299446 | ||
|
|
3f80be8377 | ||
|
|
ced0accde5 | ||
|
|
b454ff5ec7 | ||
|
|
45af198f32 | ||
|
|
ff374f8899 | ||
|
|
faecb3bc4b | ||
|
|
6b893fadef | ||
|
|
c328467a41 | ||
|
|
182325470b | ||
|
|
f330ad71ac | ||
|
|
ba0c064f36 | ||
|
|
8d7aaee5b9 | ||
|
|
68cba2de63 | ||
|
|
5a914f9c0e | ||
|
|
b0e6805a20 | ||
|
|
21e7e44c01 | ||
|
|
f7df4abdae | ||
|
|
7674ceefe9 | ||
|
|
4be575c534 | ||
|
|
dd0f0a7d5a | ||
|
|
759b44c224 |
2
.github/workflows/nightly-builds.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
|||||||
uses: actions/setup-java@v3
|
uses: actions/setup-java@v3
|
||||||
with:
|
with:
|
||||||
java-version: '17'
|
java-version: '17'
|
||||||
distribution: 'temurin'
|
distribution: 'corretto'
|
||||||
cache: gradle
|
cache: gradle
|
||||||
|
|
||||||
- name: Get current date
|
- name: Get current date
|
||||||
|
|||||||
11
.github/workflows/validate-gradle-wrapper.yml
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
name: Validate Gradle Wrapper
|
||||||
|
|
||||||
|
on: [pull_request, push]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
validation:
|
||||||
|
name: Validation
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: gradle/wrapper-validation-action@v1
|
||||||
5
FAQ.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
## F.A.Q
|
||||||
|
|
||||||
|
Q: What are the main differences between Moshidon and Megalodon?
|
||||||
|
|
||||||
|
A: There are many, but the most outstanding differences are: the ability to have other server's local timeline inside the app. It can be acessed in the "Add community" option in the top right corner of the Edit timelines screen. Other outstanding features that Moshidon has are some quality of life improvements, such as notification actions and allowing for unlisted replies by default. Most other features are pretty minor, such as profile notes directly available in the person's profile. Other features are quite minor usability and visibility improvements. All of which can be found in the settings page.
|
||||||
71
README.md
@@ -7,38 +7,38 @@
|
|||||||
|
|
||||||
[](https://github.com/LucasGGamerM/moshidon/releases/latest/download/moshidon.apk)
|
[](https://github.com/LucasGGamerM/moshidon/releases/latest/download/moshidon.apk)
|
||||||
|
|
||||||
[](https://nightly.link/LucasGGamerM/moshidon/workflows/nightly-builds/master/moshidon-nightly.apk.zip)
|
[](https://github.com/LucasGGamerM/moshidon-nightly/releases/latest/download/moshidon-nightly.apk)
|
||||||
|
|
||||||
|
|
||||||
[](https://translate.codeberg.org/engage/moshidon/)
|
[](https://translate.codeberg.org/engage/moshidon/)
|
||||||
|
|
||||||
[](https://github.com/LucasGGamerM/moshidon/actions/workflows/android.yml)
|
[](https://github.com/LucasGGamerM/moshidon/actions/workflows/nightly-builds.yml)
|
||||||
|
|
||||||
<a href="https://play.google.com/store/apps/details?id=org.joinmastodon.android.moshinda"><img height="50" alt="Get it on Google Play" src="img/google-play-badge.png"></a>
|
<a href="https://play.google.com/store/apps/details?id=org.joinmastodon.android.moshinda"><img height="50" alt="Get it on Google Play" src="img/google-play-badge.png"></a>
|
||||||
|
|
||||||
<a href="https://apt.izzysoft.de/fdroid/index/apk/org.joinmastodon.android.moshinda"><img height="50" alt="Get it on IzzyOnDroid" src="img/izzy-badge.png"></a>
|
<a href="https://apt.izzysoft.de/fdroid/index/apk/org.joinmastodon.android.moshinda"><img height="50" alt="Get it on IzzyOnDroid" src="img/izzy-badge.png"></a>
|
||||||
|
|
||||||
|
## Help out the project by donating at: https://github.com/sponsors/LucasGGamerM!
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## F.A.Q
|
|
||||||
|
|
||||||
### Q: What are the main differences between Moshidon and Megalodon?
|
|
||||||
|
|
||||||
### A: There are many, but the most outstanding differences are: the ability to have other server's local timeline inside the app. It can be acessed in the "Add community" option in the top right corner of the Edit timelines screen. Other outstanding features that Moshidon has are some quality of life improvements, such as notification actions and allowing for unlisted replies by default. Most other features are pretty minor, such as profile notes directly available in the person's profile. Other features are quite minor usability and visibility improvements. All of which can be found in the settings page.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Key features
|
## Key features
|
||||||
|
|
||||||
### **The ability to add new custom local timelines!**
|
### **The ability to add other server's local timeline to your timelines**
|
||||||
|
|
||||||
#### It can be accessed in the "Edit timelines" menu, where you can add a new "Community" to see other server's local posts!
|
It can be accessed in the "Edit timelines" menu, where you can add a new "Community" to see other server's local posts!
|
||||||
|
|
||||||
### **Material you theme support on Android 12+ devices!**
|
### **View remote profiles**
|
||||||
|
|
||||||
### **Show posts filtered with a warning!**
|
You can now see all of a profile follows and followers, by directly loading them from the profile's home instance. In case of a failed lookup, the app will automatically fall back to the older method.
|
||||||
|
|
||||||
**Allows you to have filtered posts collapsed with a warning! As shown in the screenshots:**
|
### **Translate posts easily**
|
||||||
|
|
||||||
|
Allows you to easily translate posts in another language with a translate button! Your instance must support translation, otherwise it will not work.
|
||||||
|
|
||||||
|
### **Show posts filtered with a warning**
|
||||||
|
|
||||||
|
Allows you to have filtered posts collapsed with a warning! As shown in the screenshots:
|
||||||
|
|
||||||
Before | After
|
Before | After
|
||||||
:-------------------------:|:-------------------------:
|
:-------------------------:|:-------------------------:
|
||||||
@@ -47,7 +47,7 @@ Before | After
|
|||||||
|
|
||||||
### **Color themes**
|
### **Color themes**
|
||||||
|
|
||||||
**Allows you to change theme within the app. Supports Purple, pink, green, blue, red, orange, yellow and Nord!**
|
**Allows you to change theme within the app. Supports Material You, purple, pink, green, blue, red, orange, yellow and Nord!**
|
||||||
|
|
||||||
### **Unlisted posting**
|
### **Unlisted posting**
|
||||||
|
|
||||||
@@ -71,6 +71,10 @@ That’s one of the reasons why choosing a small, **well-moderated instance is i
|
|||||||
|
|
||||||
This is important to **ensure the content you’re sharing is as accessible as possible** to people who can’t see the images and rely on software to read back the provided content descriptions. Thankfully, it’s quite common for people on the Fediverse to provide such alt texts, and hopefully things stay this way!
|
This is important to **ensure the content you’re sharing is as accessible as possible** to people who can’t see the images and rely on software to read back the provided content descriptions. Thankfully, it’s quite common for people on the Fediverse to provide such alt texts, and hopefully things stay this way!
|
||||||
|
|
||||||
|
### **Reminder to add alt text to attached media**
|
||||||
|
|
||||||
|
By default, Moshidon will show a warning to add alt text if your post has any attachments without any alt text. This is for better accessibility, and it can easily be bypassed and disabled in settings.
|
||||||
|
|
||||||
### **Pinning posts**
|
### **Pinning posts**
|
||||||
|
|
||||||
**This lets you can highlight important posts on your profile. A dedicated “Pinned” tab in people’s profiles shows all the posts they pinned.**
|
**This lets you can highlight important posts on your profile. A dedicated “Pinned” tab in people’s profiles shows all the posts they pinned.**
|
||||||
@@ -95,12 +99,22 @@ Moshidon is also available in [IzzyOnDroid repo](https://apt.izzysoft.de/fdroid/
|
|||||||
|
|
||||||
## Release variants
|
## Release variants
|
||||||
|
|
||||||
All downloads can be found on the [Releases](https://github.com/LucasGGamerM/moshidon/releases) page.
|
### Stable variant
|
||||||
|
|
||||||
|
All stable version downloads can be found on the [Releases](https://github.com/LucasGGamerM/moshidon/releases) page.
|
||||||
|
|
||||||
**`moshidon.apk`**
|
**`moshidon.apk`**
|
||||||
|
|
||||||
Variant with an integrated updater. If you download Moshidon from here (and not from an app store), just download the regular `moshidon.apk`.
|
Variant with an integrated updater. If you download Moshidon from here (and not from an app store), just download the regular `moshidon.apk`.
|
||||||
|
|
||||||
|
### Nightly variant
|
||||||
|
|
||||||
|
All nightly builds can be downloaded at [Nightly Releases](https://github.com/LucasGGamerM/moshidon-nightly/releases) page.
|
||||||
|
|
||||||
|
**`moshidon-nightly.apk`**
|
||||||
|
|
||||||
|
Unstable variant with an integrated updater. It's for development and testing purposes. If you find any bugs with it, please file a bug report at our [issues](https://github.com/LucasGGamerM/moshidon/issues) page.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
@@ -108,16 +122,18 @@ Variant with an integrated updater. If you download Moshidon from here (and not
|
|||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
|
* [Adding the ability to view other server's local timelines](https://github.com/LucasGGamerM/moshidon/tree/feature/local-timelines)
|
||||||
|
* [Adding the ability to load followers and following from remote instance](https://github.com/LucasGGamerM/moshidon/tree/feature/remote-followers)
|
||||||
* [Adding the ability to have filtered posts show with a warning](https://github.com/LucasGGamerM/moshidon/tree/feature/filters_again)
|
* [Adding the ability to have filtered posts show with a warning](https://github.com/LucasGGamerM/moshidon/tree/feature/filters_again)
|
||||||
* [Add “Unlisted” as a post visibility option](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/enable-unlisted)
|
* [Add “Unlisted” as a post visibility option](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/enable-unlisted)
|
||||||
([Pull request](https://github.com/mastodon/mastodon-android/pull/103))
|
([Pull request](https://github.com/mastodon/mastodon-android/pull/103))
|
||||||
* Adding a useful private profile note box!*
|
* Adding a useful private profile note box
|
||||||
* Auto hiding the compose button on scroll!*
|
* Auto hiding the compose button on scroll
|
||||||
* Adding the ability to remind yourself to add alt text to images!*
|
* Adding the ability to remind yourself to add alt text to images
|
||||||
* An indicator for if an image has alt text or not*
|
* An indicator for if an image has alt text or not
|
||||||
* Adding the ability to have drafts!*
|
* Adding the ability to have drafts
|
||||||
* Also adding the ability to view announcements from your instance!*
|
* Also adding the ability to view announcements from your instance
|
||||||
* Adding the ability to post for local timeline only (Only on instances that support it!)*
|
* Adding the ability to post for local timeline only (Only on instances that support it!)
|
||||||
* [Add image description button and viewer](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/display-alt-text) ([Pull request](https://github.com/mastodon/mastodon-android/pull/129))
|
* [Add image description button and viewer](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/display-alt-text) ([Pull request](https://github.com/mastodon/mastodon-android/pull/129))
|
||||||
* [Implement pinning posts and displaying pinned posts](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/pin-posts) ([Pull request](https://github.com/mastodon/mastodon-android/pull/140))
|
* [Implement pinning posts and displaying pinned posts](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/pin-posts) ([Pull request](https://github.com/mastodon/mastodon-android/pull/140))
|
||||||
* [Implement deleting and re-drafting](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/delete-redraft) ([Closes issue](https://github.com/mastodon/mastodon-android/issues/21))
|
* [Implement deleting and re-drafting](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/delete-redraft) ([Closes issue](https://github.com/mastodon/mastodon-android/issues/21))
|
||||||
@@ -139,6 +155,7 @@ Variant with an integrated updater. If you download Moshidon from here (and not
|
|||||||
|
|
||||||
### Behavior
|
### Behavior
|
||||||
|
|
||||||
|
* Allow for confirmation before reblogging
|
||||||
* Adding a bottom option for the publish button, allowing for easier use on larger screens!
|
* Adding a bottom option for the publish button, allowing for easier use on larger screens!
|
||||||
* [Make back button return to the home tab before exiting the app](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/back-returns-home) ([Closes issue](https://github.com/mastodon/mastodon-android/issues/118))
|
* [Make back button return to the home tab before exiting the app](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/back-returns-home) ([Closes issue](https://github.com/mastodon/mastodon-android/issues/118))
|
||||||
* [Always preserve content warnings when replying](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/always-preserve-cw) ([Closes issue](https://github.com/mastodon/mastodon-android/issues/113))
|
* [Always preserve content warnings when replying](https://github.com/mastodon/mastodon-android/compare/master...sk22:megalodon:feature/always-preserve-cw) ([Closes issue](https://github.com/mastodon/mastodon-android/issues/113))
|
||||||
@@ -170,6 +187,10 @@ This project is released under the [GPL-3 License](./LICENSE).
|
|||||||
|
|
||||||
## Links
|
## Links
|
||||||
|
|
||||||
[Official matrix chatroom:](https://matrix.to/#/#moshidon:matrix.org) https://matrix.to/#/#moshidon:matrix.org
|
[F.A.Q](FAQ.md)
|
||||||
|
|
||||||
|
[Official matrix chatroom:](https://matrix.to/#/#moshidon:floss.social) https://matrix.to/#/#moshidon:floss.social
|
||||||
|
|
||||||
<a rel="me" href="https://floss.social/@moshidon">@moshidon<wbr>@floss.social</a>
|
<a rel="me" href="https://floss.social/@moshidon">@moshidon<wbr>@floss.social</a>
|
||||||
|
|
||||||
|
---
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ buildscript {
|
|||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:7.3.1'
|
classpath 'com.android.tools.build:gradle:8.0.0'
|
||||||
// NOTE: Do not place your application dependencies here; they belong
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
// in the individual module build.gradle files
|
// in the individual module build.gradle files
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,4 +16,7 @@ org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
|
|||||||
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
||||||
android.useAndroidX=true
|
android.useAndroidX=true
|
||||||
# Automatically convert third-party libraries to use AndroidX
|
# Automatically convert third-party libraries to use AndroidX
|
||||||
android.enableJetifier=false
|
android.enableJetifier=false
|
||||||
|
android.defaults.buildfeatures.buildconfig=true
|
||||||
|
android.nonTransitiveRClass=true
|
||||||
|
android.nonFinalResIds=false
|
||||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
7
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,7 @@
|
|||||||
#Thu Jan 13 11:33:43 MSK 2022
|
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
|
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStorePath=wrapper/dists
|
distributionSha256Sum=e111cb9948407e26351227dabce49822fb88c37ee72f1d1582a69c68af2e702f
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
|
||||||
|
networkTimeout=10000
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
|
|||||||
288
gradlew
vendored
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env sh
|
#!/bin/sh
|
||||||
|
|
||||||
#
|
#
|
||||||
# Copyright 2015 the original author or authors.
|
# Copyright © 2015-2021 the original authors.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
@@ -17,67 +17,98 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
##
|
#
|
||||||
## Gradle start up script for UN*X
|
# Gradle start up script for POSIX generated by Gradle.
|
||||||
##
|
#
|
||||||
|
# Important for running:
|
||||||
|
#
|
||||||
|
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||||
|
# noncompliant, but you have some other compliant shell such as ksh or
|
||||||
|
# bash, then to run this script, type that shell name before the whole
|
||||||
|
# command line, like:
|
||||||
|
#
|
||||||
|
# ksh Gradle
|
||||||
|
#
|
||||||
|
# Busybox and similar reduced shells will NOT work, because this script
|
||||||
|
# requires all of these POSIX shell features:
|
||||||
|
# * functions;
|
||||||
|
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||||
|
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||||
|
# * compound commands having a testable exit status, especially «case»;
|
||||||
|
# * various built-in commands including «command», «set», and «ulimit».
|
||||||
|
#
|
||||||
|
# Important for patching:
|
||||||
|
#
|
||||||
|
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||||
|
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||||
|
#
|
||||||
|
# The "traditional" practice of packing multiple parameters into a
|
||||||
|
# space-separated string is a well documented source of bugs and security
|
||||||
|
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||||
|
# options in "$@", and eventually passing that to Java.
|
||||||
|
#
|
||||||
|
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||||
|
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||||
|
# see the in-line comments for details.
|
||||||
|
#
|
||||||
|
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||||
|
# Darwin, MinGW, and NonStop.
|
||||||
|
#
|
||||||
|
# (3) This script is generated from the Groovy template
|
||||||
|
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||||
|
# within the Gradle project.
|
||||||
|
#
|
||||||
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
|
#
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
|
||||||
# Attempt to set APP_HOME
|
# Attempt to set APP_HOME
|
||||||
|
|
||||||
# Resolve links: $0 may be a link
|
# Resolve links: $0 may be a link
|
||||||
PRG="$0"
|
app_path=$0
|
||||||
# Need this for relative symlinks.
|
|
||||||
while [ -h "$PRG" ] ; do
|
# Need this for daisy-chained symlinks.
|
||||||
ls=`ls -ld "$PRG"`
|
while
|
||||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||||
if expr "$link" : '/.*' > /dev/null; then
|
[ -h "$app_path" ]
|
||||||
PRG="$link"
|
do
|
||||||
else
|
ls=$( ls -ld "$app_path" )
|
||||||
PRG=`dirname "$PRG"`"/$link"
|
link=${ls#*' -> '}
|
||||||
fi
|
case $link in #(
|
||||||
|
/*) app_path=$link ;; #(
|
||||||
|
*) app_path=$APP_HOME$link ;;
|
||||||
|
esac
|
||||||
done
|
done
|
||||||
SAVED="`pwd`"
|
|
||||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
|
||||||
APP_HOME="`pwd -P`"
|
|
||||||
cd "$SAVED" >/dev/null
|
|
||||||
|
|
||||||
APP_NAME="Gradle"
|
# This is normally unused
|
||||||
APP_BASE_NAME=`basename "$0"`
|
# shellcheck disable=SC2034
|
||||||
|
APP_BASE_NAME=${0##*/}
|
||||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
|
||||||
|
|
||||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
MAX_FD="maximum"
|
MAX_FD=maximum
|
||||||
|
|
||||||
warn () {
|
warn () {
|
||||||
echo "$*"
|
echo "$*"
|
||||||
}
|
} >&2
|
||||||
|
|
||||||
die () {
|
die () {
|
||||||
echo
|
echo
|
||||||
echo "$*"
|
echo "$*"
|
||||||
echo
|
echo
|
||||||
exit 1
|
exit 1
|
||||||
}
|
} >&2
|
||||||
|
|
||||||
# OS specific support (must be 'true' or 'false').
|
# OS specific support (must be 'true' or 'false').
|
||||||
cygwin=false
|
cygwin=false
|
||||||
msys=false
|
msys=false
|
||||||
darwin=false
|
darwin=false
|
||||||
nonstop=false
|
nonstop=false
|
||||||
case "`uname`" in
|
case "$( uname )" in #(
|
||||||
CYGWIN* )
|
CYGWIN* ) cygwin=true ;; #(
|
||||||
cygwin=true
|
Darwin* ) darwin=true ;; #(
|
||||||
;;
|
MSYS* | MINGW* ) msys=true ;; #(
|
||||||
Darwin* )
|
NONSTOP* ) nonstop=true ;;
|
||||||
darwin=true
|
|
||||||
;;
|
|
||||||
MINGW* )
|
|
||||||
msys=true
|
|
||||||
;;
|
|
||||||
NONSTOP* )
|
|
||||||
nonstop=true
|
|
||||||
;;
|
|
||||||
esac
|
esac
|
||||||
|
|
||||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
@@ -87,9 +118,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
|||||||
if [ -n "$JAVA_HOME" ] ; then
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
# IBM's JDK on AIX uses strange locations for the executables
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||||
else
|
else
|
||||||
JAVACMD="$JAVA_HOME/bin/java"
|
JAVACMD=$JAVA_HOME/bin/java
|
||||||
fi
|
fi
|
||||||
if [ ! -x "$JAVACMD" ] ; then
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
@@ -98,7 +129,7 @@ Please set the JAVA_HOME variable in your environment to match the
|
|||||||
location of your Java installation."
|
location of your Java installation."
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
JAVACMD="java"
|
JAVACMD=java
|
||||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
Please set the JAVA_HOME variable in your environment to match the
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
@@ -106,80 +137,109 @@ location of your Java installation."
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Increase the maximum file descriptors if we can.
|
# Increase the maximum file descriptors if we can.
|
||||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||||
MAX_FD_LIMIT=`ulimit -H -n`
|
case $MAX_FD in #(
|
||||||
if [ $? -eq 0 ] ; then
|
max*)
|
||||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||||
MAX_FD="$MAX_FD_LIMIT"
|
# shellcheck disable=SC3045
|
||||||
fi
|
MAX_FD=$( ulimit -H -n ) ||
|
||||||
ulimit -n $MAX_FD
|
warn "Could not query maximum file descriptor limit"
|
||||||
if [ $? -ne 0 ] ; then
|
esac
|
||||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
case $MAX_FD in #(
|
||||||
fi
|
'' | soft) :;; #(
|
||||||
else
|
*)
|
||||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||||
fi
|
# shellcheck disable=SC3045
|
||||||
fi
|
ulimit -n "$MAX_FD" ||
|
||||||
|
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||||
# For Darwin, add options to specify how the application appears in the dock
|
|
||||||
if $darwin; then
|
|
||||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
|
||||||
fi
|
|
||||||
|
|
||||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
|
||||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
|
||||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
|
||||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
|
||||||
|
|
||||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
|
||||||
|
|
||||||
# We build the pattern for arguments to be converted via cygpath
|
|
||||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
|
||||||
SEP=""
|
|
||||||
for dir in $ROOTDIRSRAW ; do
|
|
||||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
|
||||||
SEP="|"
|
|
||||||
done
|
|
||||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
|
||||||
# Add a user-defined pattern to the cygpath arguments
|
|
||||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
|
||||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
|
||||||
fi
|
|
||||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
|
||||||
i=0
|
|
||||||
for arg in "$@" ; do
|
|
||||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
|
||||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
|
||||||
|
|
||||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
|
||||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
|
||||||
else
|
|
||||||
eval `echo args$i`="\"$arg\""
|
|
||||||
fi
|
|
||||||
i=`expr $i + 1`
|
|
||||||
done
|
|
||||||
case $i in
|
|
||||||
0) set -- ;;
|
|
||||||
1) set -- "$args0" ;;
|
|
||||||
2) set -- "$args0" "$args1" ;;
|
|
||||||
3) set -- "$args0" "$args1" "$args2" ;;
|
|
||||||
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
|
||||||
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
|
||||||
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
|
||||||
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
|
||||||
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
|
||||||
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
|
||||||
esac
|
esac
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Escape application args
|
# Collect all arguments for the java command, stacking in reverse order:
|
||||||
save () {
|
# * args from the command line
|
||||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
# * the main class name
|
||||||
echo " "
|
# * -classpath
|
||||||
}
|
# * -D...appname settings
|
||||||
APP_ARGS=`save "$@"`
|
# * --module-path (only if needed)
|
||||||
|
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||||
|
|
||||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
if "$cygwin" || "$msys" ; then
|
||||||
|
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||||
|
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||||
|
|
||||||
|
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||||
|
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
for arg do
|
||||||
|
if
|
||||||
|
case $arg in #(
|
||||||
|
-*) false ;; # don't mess with options #(
|
||||||
|
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||||
|
[ -e "$t" ] ;; #(
|
||||||
|
*) false ;;
|
||||||
|
esac
|
||||||
|
then
|
||||||
|
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||||
|
fi
|
||||||
|
# Roll the args list around exactly as many times as the number of
|
||||||
|
# args, so each arg winds up back in the position where it started, but
|
||||||
|
# possibly modified.
|
||||||
|
#
|
||||||
|
# NB: a `for` loop captures its iteration list before it begins, so
|
||||||
|
# changing the positional parameters here affects neither the number of
|
||||||
|
# iterations, nor the values presented in `arg`.
|
||||||
|
shift # remove old arg
|
||||||
|
set -- "$@" "$arg" # push replacement arg
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
|
# Collect all arguments for the java command;
|
||||||
|
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||||
|
# shell script including quotes and variable substitutions, so put them in
|
||||||
|
# double quotes to make sure that they get re-expanded; and
|
||||||
|
# * put everything else in single quotes, so that it's not re-expanded.
|
||||||
|
|
||||||
|
set -- \
|
||||||
|
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||||
|
-classpath "$CLASSPATH" \
|
||||||
|
org.gradle.wrapper.GradleWrapperMain \
|
||||||
|
"$@"
|
||||||
|
|
||||||
|
# Stop when "xargs" is not available.
|
||||||
|
if ! command -v xargs >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
die "xargs is not available"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Use "xargs" to parse quoted args.
|
||||||
|
#
|
||||||
|
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||||
|
#
|
||||||
|
# In Bash we could simply go:
|
||||||
|
#
|
||||||
|
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||||
|
# set -- "${ARGS[@]}" "$@"
|
||||||
|
#
|
||||||
|
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||||
|
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||||
|
# character that might be a shell metacharacter, then use eval to reverse
|
||||||
|
# that process (while maintaining the separation between arguments), and wrap
|
||||||
|
# the whole thing up as a single "set" statement.
|
||||||
|
#
|
||||||
|
# This will of course break if any of these variables contains a newline or
|
||||||
|
# an unmatched quote.
|
||||||
|
#
|
||||||
|
|
||||||
|
eval "set -- $(
|
||||||
|
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||||
|
xargs -n1 |
|
||||||
|
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||||
|
tr '\n' ' '
|
||||||
|
)" '"$@"'
|
||||||
|
|
||||||
exec "$JAVACMD" "$@"
|
exec "$JAVACMD" "$@"
|
||||||
|
|||||||
15
gradlew.bat
vendored
@@ -14,7 +14,7 @@
|
|||||||
@rem limitations under the License.
|
@rem limitations under the License.
|
||||||
@rem
|
@rem
|
||||||
|
|
||||||
@if "%DEBUG%" == "" @echo off
|
@if "%DEBUG%"=="" @echo off
|
||||||
@rem ##########################################################################
|
@rem ##########################################################################
|
||||||
@rem
|
@rem
|
||||||
@rem Gradle startup script for Windows
|
@rem Gradle startup script for Windows
|
||||||
@@ -25,7 +25,8 @@
|
|||||||
if "%OS%"=="Windows_NT" setlocal
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
set DIRNAME=%~dp0
|
set DIRNAME=%~dp0
|
||||||
if "%DIRNAME%" == "" set DIRNAME=.
|
if "%DIRNAME%"=="" set DIRNAME=.
|
||||||
|
@rem This is normally unused
|
||||||
set APP_BASE_NAME=%~n0
|
set APP_BASE_NAME=%~n0
|
||||||
set APP_HOME=%DIRNAME%
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
@@ -40,7 +41,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
|
|||||||
|
|
||||||
set JAVA_EXE=java.exe
|
set JAVA_EXE=java.exe
|
||||||
%JAVA_EXE% -version >NUL 2>&1
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
if "%ERRORLEVEL%" == "0" goto execute
|
if %ERRORLEVEL% equ 0 goto execute
|
||||||
|
|
||||||
echo.
|
echo.
|
||||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
@@ -75,13 +76,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
|||||||
|
|
||||||
:end
|
:end
|
||||||
@rem End local scope for the variables with windows NT shell
|
@rem End local scope for the variables with windows NT shell
|
||||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||||
|
|
||||||
:fail
|
:fail
|
||||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
rem the _cmd.exe /c_ return code!
|
rem the _cmd.exe /c_ return code!
|
||||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
set EXIT_CODE=%ERRORLEVEL%
|
||||||
exit /b 1
|
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||||
|
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||||
|
exit /b %EXIT_CODE%
|
||||||
|
|
||||||
:mainEnd
|
:mainEnd
|
||||||
if "%OS%"=="Windows_NT" endlocal
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|||||||
@@ -2,6 +2,12 @@ plugins {
|
|||||||
id 'com.android.application'
|
id 'com.android.application'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
java {
|
||||||
|
toolchain {
|
||||||
|
languageVersion = JavaLanguageVersion.of(17)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdk 33
|
compileSdk 33
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
@@ -10,11 +16,11 @@ android {
|
|||||||
applicationId "org.joinmastodon.android.moshinda"
|
applicationId "org.joinmastodon.android.moshinda"
|
||||||
minSdk 23
|
minSdk 23
|
||||||
targetSdk 33
|
targetSdk 33
|
||||||
versionCode 99
|
versionCode 100
|
||||||
versionName "1.2.0+fork.99.moshinda"
|
versionName "1.3.0+fork.100.moshinda"
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
resConfigs "ar-rSA", "be-rBY", "bn-rBD", "bs-rBA", "ca-rES", "cs-rCZ", "de-rDE", "el-rGR", "es-rES", "eu-rES", "fi-rFI", "fil-rPH", "fr-rFR", "ga-rIE", "gd-rGB", "gl-rES", "hi-rIN", "hr-rHR", "hu-rHU", "hy-rAM", "in-rID", "is-rIS", "it-rIT", "iw-rIL", "ja-rJP", "kab", "ko-rKR", "nl-rNL", "oc-rFR", "pl-rPL", "pt-rBR", "pt-rPT", "ro-rRO", "ru-rRU", "si-rLK", "sl-rSI", "sv-rSE", "th-rTH", "tr-rTR", "uk-rUA", "vi-rVN", "zh-rCN", "zh-rTW"
|
resourceConfigurations += ['ar-rSA', 'ar-rDZ', 'be-rBY', 'bn-rBD', 'bs-rBA', 'ca-rES', 'cs-rCZ', 'da-rDK', 'de-rDE', 'el-rGR', 'es-rES', 'eu-rES', 'fa-rIR', 'fi-rFI', 'fil-rPH', 'fr-rFR', 'ga-rIE', 'gd-rGB', 'gl-rES', 'hi-rIN', 'hr-rHR', 'hu-rHU', 'hy-rAM', 'ig-rNG', 'in-rID', 'is-rIS', 'it-rIT', 'iw-rIL', 'ja-rJP', 'kab', 'ko-rKR', 'my-rMM', 'nl-rNL', 'no-rNO', 'oc-rFR', 'pl-rPL', 'pt-rBR', 'pt-rPT', 'ro-rRO', 'ru-rRU', 'si-rLK', 'sl-rSI', 'sv-rSE', 'th-rTH', 'tr-rTR', 'uk-rUA', 'ur-rIN', 'vi-rVN', 'zh-rCN', 'zh-rTW']
|
||||||
}
|
}
|
||||||
|
|
||||||
signingConfigs {
|
signingConfigs {
|
||||||
nightly{
|
nightly{
|
||||||
@@ -42,8 +48,8 @@ android {
|
|||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
// minifyEnabled true
|
minifyEnabled true
|
||||||
// shrinkResources true
|
shrinkResources true
|
||||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||||
}
|
}
|
||||||
debug{
|
debug{
|
||||||
@@ -85,17 +91,22 @@ android {
|
|||||||
setRoot "src/github"
|
setRoot "src/github"
|
||||||
}
|
}
|
||||||
debug {
|
debug {
|
||||||
setRoot "src/github"
|
setRoot "src/debug"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lintOptions{
|
namespace 'org.joinmastodon.android'
|
||||||
checkReleaseBuilds false
|
lint {
|
||||||
abortOnError false
|
abortOnError false
|
||||||
|
checkReleaseBuilds false
|
||||||
|
}
|
||||||
|
|
||||||
|
buildFeatures {
|
||||||
|
buildConfig true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
api 'androidx.annotation:annotation:1.3.0'
|
api 'androidx.annotation:annotation:1.6.0'
|
||||||
implementation 'com.squareup.okhttp3:okhttp:3.14.9'
|
implementation 'com.squareup.okhttp3:okhttp:3.14.9'
|
||||||
implementation 'me.grishka.litex:recyclerview:1.2.1.1'
|
implementation 'me.grishka.litex:recyclerview:1.2.1.1'
|
||||||
implementation 'me.grishka.litex:swiperefreshlayout:1.1.0.1'
|
implementation 'me.grishka.litex:swiperefreshlayout:1.1.0.1'
|
||||||
@@ -104,7 +115,7 @@ dependencies {
|
|||||||
implementation 'me.grishka.litex:viewpager:1.0.0'
|
implementation 'me.grishka.litex:viewpager:1.0.0'
|
||||||
implementation 'me.grishka.litex:viewpager2:1.0.0'
|
implementation 'me.grishka.litex:viewpager2:1.0.0'
|
||||||
implementation 'me.grishka.appkit:appkit:1.2.7'
|
implementation 'me.grishka.appkit:appkit:1.2.7'
|
||||||
implementation 'com.google.code.gson:gson:2.8.9'
|
implementation 'com.google.code.gson:gson:2.9.0'
|
||||||
implementation 'org.jsoup:jsoup:1.14.3'
|
implementation 'org.jsoup:jsoup:1.14.3'
|
||||||
implementation 'com.squareup:otto:1.3.8'
|
implementation 'com.squareup:otto:1.3.8'
|
||||||
implementation 'de.psdev:async-otto:1.0.3'
|
implementation 'de.psdev:async-otto:1.0.3'
|
||||||
|
|||||||
7
mastodon/proguard-rules.pro
vendored
@@ -42,6 +42,13 @@
|
|||||||
|
|
||||||
-keepattributes LineNumberTable
|
-keepattributes LineNumberTable
|
||||||
|
|
||||||
|
-keepattributes *
|
||||||
|
-keep,allowobfuscation,allowshrinking class com.google.gson.reflect.TypeToken
|
||||||
|
-keep,allowobfuscation,allowshrinking class * extends com.google.gson.reflect.TypeToken
|
||||||
|
|
||||||
|
#-keep class javax.** { *; }
|
||||||
|
-keep class org.joinmastodon.android.** { *; }
|
||||||
|
|
||||||
# Parceler library
|
# Parceler library
|
||||||
-keep interface org.parceler.Parcel
|
-keep interface org.parceler.Parcel
|
||||||
-keep @org.parceler.Parcel class * { *; }
|
-keep @org.parceler.Parcel class * { *; }
|
||||||
|
|||||||
@@ -0,0 +1,369 @@
|
|||||||
|
package org.joinmastodon.android.updater;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.DownloadManager;
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.IntentFilter;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.content.pm.PackageInstaller;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonParser;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.BuildConfig;
|
||||||
|
import org.joinmastodon.android.E;
|
||||||
|
import org.joinmastodon.android.GlobalUserPreferences;
|
||||||
|
import org.joinmastodon.android.MastodonApp;
|
||||||
|
import org.joinmastodon.android.R;
|
||||||
|
import org.joinmastodon.android.api.MastodonAPIController;
|
||||||
|
import org.joinmastodon.android.events.SelfUpdateStateChangedEvent;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import androidx.annotation.Keep;
|
||||||
|
import okhttp3.Call;
|
||||||
|
import okhttp3.Request;
|
||||||
|
import okhttp3.Response;
|
||||||
|
|
||||||
|
@Keep
|
||||||
|
public class GithubSelfUpdaterImpl extends GithubSelfUpdater{
|
||||||
|
private static final long CHECK_PERIOD=6*3600*1000L;
|
||||||
|
private static final String TAG="GithubSelfUpdater";
|
||||||
|
|
||||||
|
private UpdateState state=UpdateState.NO_UPDATE;
|
||||||
|
private UpdateInfo info;
|
||||||
|
private long downloadID;
|
||||||
|
private BroadcastReceiver downloadCompletionReceiver=new BroadcastReceiver(){
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent){
|
||||||
|
if(downloadID!=0 && intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, 0)==downloadID){
|
||||||
|
MastodonApp.context.unregisterReceiver(this);
|
||||||
|
setState(UpdateState.DOWNLOADED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public GithubSelfUpdaterImpl(){
|
||||||
|
SharedPreferences prefs=getPrefs();
|
||||||
|
int checkedByBuild=prefs.getInt("checkedByBuild", 0);
|
||||||
|
if(prefs.contains("version") && checkedByBuild==BuildConfig.VERSION_CODE){
|
||||||
|
info=new UpdateInfo();
|
||||||
|
info.version=prefs.getString("version", null);
|
||||||
|
info.size=prefs.getLong("apkSize", 0);
|
||||||
|
info.changelog=prefs.getString("changelog", null);
|
||||||
|
downloadID=prefs.getLong("downloadID", 0);
|
||||||
|
if(downloadID==0 || !getUpdateApkFile().exists()){
|
||||||
|
state=UpdateState.UPDATE_AVAILABLE;
|
||||||
|
}else{
|
||||||
|
DownloadManager dm=MastodonApp.context.getSystemService(DownloadManager.class);
|
||||||
|
state=dm.getUriForDownloadedFile(downloadID)==null ? UpdateState.DOWNLOADING : UpdateState.DOWNLOADED;
|
||||||
|
if(state==UpdateState.DOWNLOADING){
|
||||||
|
MastodonApp.context.registerReceiver(downloadCompletionReceiver, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}else if(checkedByBuild!=BuildConfig.VERSION_CODE && checkedByBuild>0){
|
||||||
|
// We are in a new version, running for the first time after update. Gotta clean things up.
|
||||||
|
long id=getPrefs().getLong("downloadID", 0);
|
||||||
|
if(id!=0){
|
||||||
|
MastodonApp.context.getSystemService(DownloadManager.class).remove(id);
|
||||||
|
}
|
||||||
|
getUpdateApkFile().delete();
|
||||||
|
getPrefs().edit()
|
||||||
|
.remove("apkSize")
|
||||||
|
.remove("version")
|
||||||
|
.remove("apkURL")
|
||||||
|
.remove("checkedByBuild")
|
||||||
|
.remove("downloadID")
|
||||||
|
.remove("changelog")
|
||||||
|
.apply();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private SharedPreferences getPrefs(){
|
||||||
|
return MastodonApp.context.getSharedPreferences("githubUpdater", Context.MODE_PRIVATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void maybeCheckForUpdates(){
|
||||||
|
if(state!=UpdateState.NO_UPDATE && state!=UpdateState.UPDATE_AVAILABLE)
|
||||||
|
return;
|
||||||
|
long timeSinceLastCheck=System.currentTimeMillis()-getPrefs().getLong("lastCheck", CHECK_PERIOD);
|
||||||
|
if(timeSinceLastCheck>=CHECK_PERIOD){
|
||||||
|
setState(UpdateState.CHECKING);
|
||||||
|
MastodonAPIController.runInBackground(this::actuallyCheckForUpdates);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void checkForUpdates() {
|
||||||
|
setState(UpdateState.CHECKING);
|
||||||
|
MastodonAPIController.runInBackground(this::actuallyCheckForUpdates);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void actuallyCheckForUpdates(){
|
||||||
|
Request req=new Request.Builder()
|
||||||
|
.url("https://api.github.com/repos/LucasGGamerM/moshidon/releases")
|
||||||
|
.build();
|
||||||
|
Call call=MastodonAPIController.getHttpClient().newCall(req);
|
||||||
|
try(Response resp=call.execute()){
|
||||||
|
JsonArray arr=JsonParser.parseReader(resp.body().charStream()).getAsJsonArray();
|
||||||
|
for (JsonElement jsonElement : arr) {
|
||||||
|
JsonObject obj = jsonElement.getAsJsonObject();
|
||||||
|
if (obj.get("prerelease").getAsBoolean() && !GlobalUserPreferences.enablePreReleases) continue;
|
||||||
|
|
||||||
|
String tag=obj.get("tag_name").getAsString();
|
||||||
|
String changelog=obj.get("body").getAsString();
|
||||||
|
Pattern pattern=Pattern.compile("v?(\\d+)\\.(\\d+)\\.(\\d+)\\+fork\\.(\\d+)");
|
||||||
|
Matcher matcher=pattern.matcher(tag);
|
||||||
|
if(!matcher.find()){
|
||||||
|
Log.w(TAG, "actuallyCheckForUpdates: release tag has wrong format: "+tag);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int newMajor=Integer.parseInt(matcher.group(1)),
|
||||||
|
newMinor=Integer.parseInt(matcher.group(2)),
|
||||||
|
newRevision=Integer.parseInt(matcher.group(3)),
|
||||||
|
newForkNumber=Integer.parseInt(matcher.group(4));
|
||||||
|
matcher=pattern.matcher(BuildConfig.VERSION_NAME);
|
||||||
|
String[] currentParts=BuildConfig.VERSION_NAME.split("[.+]");
|
||||||
|
if(!matcher.find()){
|
||||||
|
Log.w(TAG, "actuallyCheckForUpdates: current version has wrong format: "+BuildConfig.VERSION_NAME);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int curMajor=Integer.parseInt(matcher.group(1)),
|
||||||
|
curMinor=Integer.parseInt(matcher.group(2)),
|
||||||
|
curRevision=Integer.parseInt(matcher.group(3)),
|
||||||
|
curForkNumber=Integer.parseInt(matcher.group(4));
|
||||||
|
long newVersion=((long)newMajor << 32) | ((long)newMinor << 16) | newRevision;
|
||||||
|
long curVersion=((long)curMajor << 32) | ((long)curMinor << 16) | curRevision;
|
||||||
|
if(newVersion>curVersion || newForkNumber>curForkNumber){
|
||||||
|
String version=newMajor+"."+newMinor+"."+newRevision+"+fork."+newForkNumber;
|
||||||
|
Log.d(TAG, "actuallyCheckForUpdates: new version: "+version);
|
||||||
|
for(JsonElement el:obj.getAsJsonArray("assets")){
|
||||||
|
JsonObject asset=el.getAsJsonObject();
|
||||||
|
if("moshidon.apk".equals(asset.get("name").getAsString()) && "application/vnd.android.package-archive".equals(asset.get("content_type").getAsString()) && "uploaded".equals(asset.get("state").getAsString())){
|
||||||
|
long size=asset.get("size").getAsLong();
|
||||||
|
String url=asset.get("browser_download_url").getAsString();
|
||||||
|
|
||||||
|
UpdateInfo info=new UpdateInfo();
|
||||||
|
info.size=size;
|
||||||
|
info.version=version;
|
||||||
|
info.changelog=changelog;
|
||||||
|
this.info=info;
|
||||||
|
|
||||||
|
getPrefs().edit()
|
||||||
|
.putLong("apkSize", size)
|
||||||
|
.putString("version", version)
|
||||||
|
.putString("apkURL", url)
|
||||||
|
.putString("changelog", changelog)
|
||||||
|
.putInt("checkedByBuild", BuildConfig.VERSION_CODE)
|
||||||
|
.remove("downloadID")
|
||||||
|
.apply();
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getPrefs().edit().putLong("lastCheck", System.currentTimeMillis()).apply();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}catch(Exception x){
|
||||||
|
Log.w(TAG, "actuallyCheckForUpdates", x);
|
||||||
|
}finally{
|
||||||
|
setState(info==null ? UpdateState.NO_UPDATE : UpdateState.UPDATE_AVAILABLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setState(UpdateState state){
|
||||||
|
this.state=state;
|
||||||
|
E.post(new SelfUpdateStateChangedEvent(state));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UpdateState getState(){
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UpdateInfo getUpdateInfo(){
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
public File getUpdateApkFile(){
|
||||||
|
return new File(MastodonApp.context.getExternalCacheDir(), "update.apk");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void downloadUpdate(){
|
||||||
|
if(state==UpdateState.DOWNLOADING)
|
||||||
|
throw new IllegalStateException();
|
||||||
|
DownloadManager dm=MastodonApp.context.getSystemService(DownloadManager.class);
|
||||||
|
MastodonApp.context.registerReceiver(downloadCompletionReceiver, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
|
||||||
|
downloadID=dm.enqueue(
|
||||||
|
new DownloadManager.Request(Uri.parse(getPrefs().getString("apkURL", null)))
|
||||||
|
.setDestinationUri(Uri.fromFile(getUpdateApkFile()))
|
||||||
|
);
|
||||||
|
getPrefs().edit().putLong("downloadID", downloadID).apply();
|
||||||
|
setState(UpdateState.DOWNLOADING);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void installUpdate(Activity activity){
|
||||||
|
if(state!=UpdateState.DOWNLOADED)
|
||||||
|
throw new IllegalStateException();
|
||||||
|
Uri uri;
|
||||||
|
Intent intent=new Intent(Intent.ACTION_INSTALL_PACKAGE);
|
||||||
|
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.N){
|
||||||
|
uri=new Uri.Builder().scheme("content").authority(activity.getPackageName()+".self_update_provider").path("update.apk").build();
|
||||||
|
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||||
|
}else{
|
||||||
|
uri=Uri.fromFile(getUpdateApkFile());
|
||||||
|
}
|
||||||
|
intent.setDataAndType(uri, "application/vnd.android.package-archive");
|
||||||
|
activity.startActivity(intent);
|
||||||
|
|
||||||
|
// TODO figure out how to restart the app when updating via this new API
|
||||||
|
/*
|
||||||
|
PackageInstaller installer=activity.getPackageManager().getPackageInstaller();
|
||||||
|
try{
|
||||||
|
final int sid=installer.createSession(new PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL));
|
||||||
|
installer.registerSessionCallback(new PackageInstaller.SessionCallback(){
|
||||||
|
@Override
|
||||||
|
public void onCreated(int i){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBadgingChanged(int i){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActiveChanged(int i, boolean b){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onProgressChanged(int id, float progress){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFinished(int id, boolean success){
|
||||||
|
activity.getPackageManager().setComponentEnabledSetting(new ComponentName(activity, AfterUpdateRestartReceiver.class), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
activity.getPackageManager().setComponentEnabledSetting(new ComponentName(activity, AfterUpdateRestartReceiver.class), PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
|
||||||
|
PackageInstaller.Session session=installer.openSession(sid);
|
||||||
|
try(OutputStream out=session.openWrite("mastodon.apk", 0, info.size); InputStream in=new FileInputStream(getUpdateApkFile())){
|
||||||
|
byte[] buffer=new byte[16384];
|
||||||
|
int read;
|
||||||
|
while((read=in.read(buffer))>0){
|
||||||
|
out.write(buffer, 0, read);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// PendingIntent intent=PendingIntent.getBroadcast(activity, 1, new Intent(activity, InstallerStatusReceiver.class), PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_MUTABLE);
|
||||||
|
PendingIntent intent=PendingIntent.getActivity(activity, 1, new Intent(activity, MainActivity.class), PendingIntent.FLAG_UPDATE_CURRENT);
|
||||||
|
session.commit(intent.getIntentSender());
|
||||||
|
}catch(IOException x){
|
||||||
|
Log.w(TAG, "installUpdate", x);
|
||||||
|
Toast.makeText(activity, x.getMessage(), Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getDownloadProgress(){
|
||||||
|
if(state!=UpdateState.DOWNLOADING)
|
||||||
|
throw new IllegalStateException();
|
||||||
|
DownloadManager dm=MastodonApp.context.getSystemService(DownloadManager.class);
|
||||||
|
try(Cursor cursor=dm.query(new DownloadManager.Query().setFilterById(downloadID))){
|
||||||
|
if(cursor.moveToFirst()){
|
||||||
|
long loaded=cursor.getLong(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
|
||||||
|
long total=cursor.getLong(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_TOTAL_SIZE_BYTES));
|
||||||
|
// Log.d(TAG, "getDownloadProgress: "+loaded+" of "+total);
|
||||||
|
return total>0 ? (float)loaded/total : 0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cancelDownload(){
|
||||||
|
if(state!=UpdateState.DOWNLOADING)
|
||||||
|
throw new IllegalStateException();
|
||||||
|
DownloadManager dm=MastodonApp.context.getSystemService(DownloadManager.class);
|
||||||
|
dm.remove(downloadID);
|
||||||
|
downloadID=0;
|
||||||
|
getPrefs().edit().remove("downloadID").apply();
|
||||||
|
setState(UpdateState.UPDATE_AVAILABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleIntentFromInstaller(Intent intent, Activity activity){
|
||||||
|
int status=intent.getIntExtra(PackageInstaller.EXTRA_STATUS, 0);
|
||||||
|
if(status==PackageInstaller.STATUS_PENDING_USER_ACTION){
|
||||||
|
Intent confirmIntent=intent.getParcelableExtra(Intent.EXTRA_INTENT);
|
||||||
|
activity.startActivity(confirmIntent);
|
||||||
|
}else if(status!=PackageInstaller.STATUS_SUCCESS){
|
||||||
|
String msg=intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE);
|
||||||
|
Toast.makeText(activity, activity.getString(R.string.error)+":\n"+msg, Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*public static class InstallerStatusReceiver extends BroadcastReceiver{
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent){
|
||||||
|
int status=intent.getIntExtra(PackageInstaller.EXTRA_STATUS, 0);
|
||||||
|
if(status==PackageInstaller.STATUS_PENDING_USER_ACTION){
|
||||||
|
Intent confirmIntent=intent.getParcelableExtra(Intent.EXTRA_INTENT);
|
||||||
|
context.startActivity(confirmIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
|
||||||
|
}else if(status!=PackageInstaller.STATUS_SUCCESS){
|
||||||
|
String msg=intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE);
|
||||||
|
Toast.makeText(context, context.getString(R.string.error)+":\n"+msg, Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class AfterUpdateRestartReceiver extends BroadcastReceiver{
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent){
|
||||||
|
if(Intent.ACTION_MY_PACKAGE_REPLACED.equals(intent.getAction())){
|
||||||
|
context.getPackageManager().setComponentEnabledSetting(new ComponentName(context, AfterUpdateRestartReceiver.class), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
|
||||||
|
Toast.makeText(context, R.string.update_installed, Toast.LENGTH_SHORT).show();
|
||||||
|
Intent restartIntent=new Intent(context, MainActivity.class)
|
||||||
|
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
|
.setPackage(context.getPackageName());
|
||||||
|
if(Build.VERSION.SDK_INT<Build.VERSION_CODES.P){
|
||||||
|
context.startActivity(restartIntent);
|
||||||
|
}else{
|
||||||
|
// Bypass activity starting restrictions by starting it from a notification
|
||||||
|
NotificationManager nm=context.getSystemService(NotificationManager.class);
|
||||||
|
NotificationChannel chan=new NotificationChannel("selfUpdateRestart", context.getString(R.string.update_installed), NotificationManager.IMPORTANCE_HIGH);
|
||||||
|
nm.createNotificationChannel(chan);
|
||||||
|
Notification n=new Notification.Builder(context, "selfUpdateRestart")
|
||||||
|
.setContentTitle(context.getString(R.string.update_installed))
|
||||||
|
.setContentIntent(PendingIntent.getActivity(context, 1, restartIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE))
|
||||||
|
.setFullScreenIntent(PendingIntent.getActivity(context, 1, restartIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE), true)
|
||||||
|
.setSmallIcon(R.drawable.ic_ntf_logo)
|
||||||
|
.build();
|
||||||
|
nm.notify(1, n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
package org.joinmastodon.android.updater;
|
||||||
|
|
||||||
|
import android.content.ContentProvider;
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.ParcelFileDescriptor;
|
||||||
|
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
public class SelfUpdateContentProvider extends ContentProvider{
|
||||||
|
@Override
|
||||||
|
public boolean onCreate(){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public String getType(@NonNull Uri uri){
|
||||||
|
if(isCorrectUri(uri))
|
||||||
|
return "application/vnd.android.package-archive";
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs){
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs){
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode) throws FileNotFoundException{
|
||||||
|
if(isCorrectUri(uri)){
|
||||||
|
return ParcelFileDescriptor.open(((GithubSelfUpdaterImpl)GithubSelfUpdater.getInstance()).getUpdateApkFile(), ParcelFileDescriptor.MODE_READ_ONLY);
|
||||||
|
}
|
||||||
|
throw new FileNotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isCorrectUri(Uri uri){
|
||||||
|
return "/update.apk".equals(uri.getPath());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 988 B After Width: | Height: | Size: 988 B |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 6.1 KiB |
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 8.5 KiB After Width: | Height: | Size: 8.5 KiB |
|
Before Width: | Height: | Size: 11 KiB |
@@ -31,6 +31,7 @@
|
|||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:theme="@style/Theme.Mastodon.AutoLightDark"
|
android:theme="@style/Theme.Mastodon.AutoLightDark"
|
||||||
|
android:windowSoftInputMode="adjustPan"
|
||||||
android:largeHeap="true">
|
android:largeHeap="true">
|
||||||
|
|
||||||
<activity android:name=".MainActivity" android:exported="true" android:configChanges="orientation|screenSize" android:windowSoftInputMode="adjustResize" android:launchMode="singleTask">
|
<activity android:name=".MainActivity" android:exported="true" android:configChanges="orientation|screenSize" android:windowSoftInputMode="adjustResize" android:launchMode="singleTask">
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ gab.protohype.net
|
|||||||
social.unzensiert.to
|
social.unzensiert.to
|
||||||
freeatlantis.com
|
freeatlantis.com
|
||||||
|
|
||||||
# reactionary bigotry and hatespeech against magrinalized groups
|
# reactionary bigotry and hatespeech against marginalized groups
|
||||||
poa.st
|
poa.st
|
||||||
freespeechextremist.com
|
freespeechextremist.com
|
||||||
rdrama.cc
|
rdrama.cc
|
||||||
|
|||||||
|
@@ -54,6 +54,7 @@ public class GlobalUserPreferences{
|
|||||||
public static boolean replyLineAboveHeader;
|
public static boolean replyLineAboveHeader;
|
||||||
public static boolean swapBookmarkWithBoostAction;
|
public static boolean swapBookmarkWithBoostAction;
|
||||||
public static boolean loadRemoteAccountFollowers;
|
public static boolean loadRemoteAccountFollowers;
|
||||||
|
public static boolean mentionRebloggerAutomatically;
|
||||||
public static String publishButtonText;
|
public static String publishButtonText;
|
||||||
public static ThemePreference theme;
|
public static ThemePreference theme;
|
||||||
public static ColorPreference color;
|
public static ColorPreference color;
|
||||||
@@ -122,6 +123,7 @@ public class GlobalUserPreferences{
|
|||||||
confirmBeforeReblog=prefs.getBoolean("confirmBeforeReblog", false);
|
confirmBeforeReblog=prefs.getBoolean("confirmBeforeReblog", false);
|
||||||
swapBookmarkWithBoostAction=prefs.getBoolean("swapBookmarkWithBoostAction", false);
|
swapBookmarkWithBoostAction=prefs.getBoolean("swapBookmarkWithBoostAction", false);
|
||||||
loadRemoteAccountFollowers=prefs.getBoolean("loadRemoteAccountFollowers", true);
|
loadRemoteAccountFollowers=prefs.getBoolean("loadRemoteAccountFollowers", true);
|
||||||
|
mentionRebloggerAutomatically=prefs.getBoolean("mentionRebloggerAutomatically", false);
|
||||||
publishButtonText=prefs.getString("publishButtonText", "");
|
publishButtonText=prefs.getString("publishButtonText", "");
|
||||||
theme=ThemePreference.values()[prefs.getInt("theme", 0)];
|
theme=ThemePreference.values()[prefs.getInt("theme", 0)];
|
||||||
recentLanguages=fromJson(prefs.getString("recentLanguages", "{}"), recentLanguagesType, new HashMap<>());
|
recentLanguages=fromJson(prefs.getString("recentLanguages", "{}"), recentLanguagesType, new HashMap<>());
|
||||||
@@ -181,6 +183,7 @@ public class GlobalUserPreferences{
|
|||||||
.putBoolean("confirmBeforeReblog", confirmBeforeReblog)
|
.putBoolean("confirmBeforeReblog", confirmBeforeReblog)
|
||||||
.putBoolean("swapBookmarkWithBoostAction", swapBookmarkWithBoostAction)
|
.putBoolean("swapBookmarkWithBoostAction", swapBookmarkWithBoostAction)
|
||||||
.putBoolean("loadRemoteAccountFollowers", loadRemoteAccountFollowers)
|
.putBoolean("loadRemoteAccountFollowers", loadRemoteAccountFollowers)
|
||||||
|
.putBoolean("mentionRebloggerAutomatically", mentionRebloggerAutomatically)
|
||||||
.putInt("theme", theme.ordinal())
|
.putInt("theme", theme.ordinal())
|
||||||
.putString("color", color.name())
|
.putString("color", color.name())
|
||||||
.putString("recentLanguages", gson.toJson(recentLanguages))
|
.putString("recentLanguages", gson.toJson(recentLanguages))
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import android.text.TextUtils;
|
|||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import org.joinmastodon.android.api.MastodonAPIController;
|
import org.joinmastodon.android.api.MastodonAPIController;
|
||||||
|
import org.joinmastodon.android.api.requests.accounts.SetAccountFollowed;
|
||||||
import org.joinmastodon.android.api.requests.notifications.GetNotificationByID;
|
import org.joinmastodon.android.api.requests.notifications.GetNotificationByID;
|
||||||
import org.joinmastodon.android.api.requests.statuses.CreateStatus;
|
import org.joinmastodon.android.api.requests.statuses.CreateStatus;
|
||||||
import org.joinmastodon.android.api.requests.statuses.SetStatusBookmarked;
|
import org.joinmastodon.android.api.requests.statuses.SetStatusBookmarked;
|
||||||
@@ -123,8 +124,16 @@ public class PushNotificationReceiver extends BroadcastReceiver{
|
|||||||
|
|
||||||
if(intent.hasExtra("notification")){
|
if(intent.hasExtra("notification")){
|
||||||
org.joinmastodon.android.model.Notification notification=Parcels.unwrap(intent.getParcelableExtra("notification"));
|
org.joinmastodon.android.model.Notification notification=Parcels.unwrap(intent.getParcelableExtra("notification"));
|
||||||
String statusID=notification.status.id;
|
String statusID = null;
|
||||||
if (statusID != null) {
|
String targetAccountID = null;
|
||||||
|
|
||||||
|
if(notification.status != null){
|
||||||
|
statusID = notification.status.id;
|
||||||
|
}
|
||||||
|
if(notification.account != null){
|
||||||
|
targetAccountID = notification.account.id;
|
||||||
|
}
|
||||||
|
if (statusID != null || targetAccountID != null) {
|
||||||
AccountSessionManager accountSessionManager = AccountSessionManager.getInstance();
|
AccountSessionManager accountSessionManager = AccountSessionManager.getInstance();
|
||||||
Preferences preferences = accountSessionManager.getAccount(accountID).preferences;
|
Preferences preferences = accountSessionManager.getAccount(accountID).preferences;
|
||||||
|
|
||||||
@@ -134,6 +143,7 @@ public class PushNotificationReceiver extends BroadcastReceiver{
|
|||||||
case BOOST -> new SetStatusReblogged(notification.status.id, true, preferences.postingDefaultVisibility).exec(accountID);
|
case BOOST -> new SetStatusReblogged(notification.status.id, true, preferences.postingDefaultVisibility).exec(accountID);
|
||||||
case UNBOOST -> new SetStatusReblogged(notification.status.id, false, preferences.postingDefaultVisibility).exec(accountID);
|
case UNBOOST -> new SetStatusReblogged(notification.status.id, false, preferences.postingDefaultVisibility).exec(accountID);
|
||||||
case REPLY -> handleReplyAction(context, accountID, intent, notification, notificationId, preferences);
|
case REPLY -> handleReplyAction(context, accountID, intent, notification, notificationId, preferences);
|
||||||
|
case FOLLOW_BACK -> new SetAccountFollowed(notification.account.id, true, true, false).exec(accountID);
|
||||||
default -> Log.w(TAG, "onReceive: Failed to get NotificationAction");
|
default -> Log.w(TAG, "onReceive: Failed to get NotificationAction");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -241,6 +251,9 @@ public class PushNotificationReceiver extends BroadcastReceiver{
|
|||||||
if(notification.status.reblogged)
|
if(notification.status.reblogged)
|
||||||
builder.addAction(buildNotificationAction(context, id, accountID, notification, context.getString(R.string.sk_undo_reblog), NotificationAction.UNBOOST));
|
builder.addAction(buildNotificationAction(context, id, accountID, notification, context.getString(R.string.sk_undo_reblog), NotificationAction.UNBOOST));
|
||||||
}
|
}
|
||||||
|
case FOLLOW -> {
|
||||||
|
builder.addAction(buildNotificationAction(context, id, accountID, notification, context.getString(R.string.follow_back), NotificationAction.FOLLOW_BACK));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ import android.view.View;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import org.joinmastodon.android.R;
|
|
||||||
|
|
||||||
import me.grishka.appkit.api.ErrorResponse;
|
import me.grishka.appkit.api.ErrorResponse;
|
||||||
|
|
||||||
public class MastodonErrorResponse extends ErrorResponse{
|
public class MastodonErrorResponse extends ErrorResponse{
|
||||||
@@ -22,7 +20,7 @@ public class MastodonErrorResponse extends ErrorResponse{
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bindErrorView(View view){
|
public void bindErrorView(View view){
|
||||||
TextView text=view.findViewById(R.id.error_text);
|
TextView text=view.findViewById(me.grishka.appkit.R.id.error_text);
|
||||||
text.setText(error);
|
text.setText(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -49,7 +49,6 @@ import org.joinmastodon.android.ui.photoviewer.PhotoViewer;
|
|||||||
import org.joinmastodon.android.ui.photoviewer.PhotoViewerHost;
|
import org.joinmastodon.android.ui.photoviewer.PhotoViewerHost;
|
||||||
import org.joinmastodon.android.ui.utils.MediaAttachmentViewController;
|
import org.joinmastodon.android.ui.utils.MediaAttachmentViewController;
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
import org.joinmastodon.android.ui.views.MediaGridLayout;
|
|
||||||
import org.joinmastodon.android.utils.TypedObjectPool;
|
import org.joinmastodon.android.utils.TypedObjectPool;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -90,10 +89,10 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
|||||||
|
|
||||||
public BaseStatusListFragment(){
|
public BaseStatusListFragment(){
|
||||||
super(20);
|
super(20);
|
||||||
if (withComposeButton()) setListLayoutId(R.layout.recycler_fragment_with_fab);
|
if (wantsComposeButton()) setListLayoutId(R.layout.recycler_fragment_with_fab);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean withComposeButton() {
|
protected boolean wantsComposeButton() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,8 +108,6 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
|||||||
setRetainInstance(true);
|
setRetainInstance(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected RecyclerView.Adapter getAdapter(){
|
protected RecyclerView.Adapter getAdapter(){
|
||||||
return adapter=new DisplayItemsAdapter();
|
return adapter=new DisplayItemsAdapter();
|
||||||
@@ -278,6 +275,36 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public @Nullable View getFab() {
|
||||||
|
if (getParentFragment() instanceof HasFab l) return l.getFab();
|
||||||
|
else return fab;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void animateFab(boolean show) {
|
||||||
|
View fab = getFab();
|
||||||
|
if (fab == null) return;
|
||||||
|
if (show && fab.getVisibility() != View.VISIBLE) {
|
||||||
|
fab.setVisibility(View.VISIBLE);
|
||||||
|
TranslateAnimation animate = new TranslateAnimation(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
fab.getHeight() * 2,
|
||||||
|
0);
|
||||||
|
animate.setDuration(300);
|
||||||
|
fab.startAnimation(animate);
|
||||||
|
} else if (!show && fab.getVisibility() == View.VISIBLE) {
|
||||||
|
TranslateAnimation animate = new TranslateAnimation(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
fab.getHeight() * 2);
|
||||||
|
animate.setDuration(300);
|
||||||
|
fab.startAnimation(animate);
|
||||||
|
fab.setVisibility(View.INVISIBLE);
|
||||||
|
scrollDiff = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onViewCreated(View view, Bundle savedInstanceState){
|
public void onViewCreated(View view, Bundle savedInstanceState){
|
||||||
super.onViewCreated(view, savedInstanceState);
|
super.onViewCreated(view, savedInstanceState);
|
||||||
@@ -289,47 +316,21 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
|||||||
if(currentPhotoViewer!=null)
|
if(currentPhotoViewer!=null)
|
||||||
currentPhotoViewer.offsetView(-dx, -dy);
|
currentPhotoViewer.offsetView(-dx, -dy);
|
||||||
|
|
||||||
|
View fab = getFab();
|
||||||
if (fab!=null && GlobalUserPreferences.enableFabAutoHide) {
|
if (fab!=null && GlobalUserPreferences.enableFabAutoHide) {
|
||||||
// This piece of code should make it so that the fab is always visible if the status list scroll view is at the item at the top
|
|
||||||
if(list.getChildAt(0).getTop() == 0){
|
|
||||||
scrollDiff= THRESHOLD +1;
|
|
||||||
}else{
|
|
||||||
if(dy > 0){
|
|
||||||
scrollDiff=0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dy > 0 && fab.getVisibility() == View.VISIBLE) {
|
if (dy > 0 && fab.getVisibility() == View.VISIBLE) {
|
||||||
TranslateAnimation animate = new TranslateAnimation(
|
animateFab(false);
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
fab.getHeight() * 2);
|
|
||||||
animate.setDuration(300);
|
|
||||||
// animate.setFillAfter(true);
|
|
||||||
fab.startAnimation(animate);
|
|
||||||
fab.setEnabled(false);
|
|
||||||
fab.setVisibility(View.INVISIBLE);
|
|
||||||
scrollDiff = 0;
|
|
||||||
} else if (dy < 0 && fab.getVisibility() != View.VISIBLE) {
|
} else if (dy < 0 && fab.getVisibility() != View.VISIBLE) {
|
||||||
if (scrollDiff > THRESHOLD) {
|
if (list.getChildAt(0).getTop() == 0 || scrollDiff > THRESHOLD) {
|
||||||
TranslateAnimation animate = new TranslateAnimation(
|
animateFab(true);
|
||||||
0,
|
|
||||||
0,
|
|
||||||
fab.getHeight() * 2,
|
|
||||||
0);
|
|
||||||
animate.setDuration(300);
|
|
||||||
// animate.setFillAfter(true);
|
|
||||||
fab.startAnimation(animate);
|
|
||||||
fab.setEnabled(true);
|
|
||||||
fab.setVisibility(View.VISIBLE);
|
|
||||||
scrollDiff = 0;
|
scrollDiff = 0;
|
||||||
} else {
|
} else {
|
||||||
scrollDiff += Math.abs(dy);
|
scrollDiff += Math.abs(dy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}});
|
}
|
||||||
|
});
|
||||||
list.addItemDecoration(new StatusListItemDecoration());
|
list.addItemDecoration(new StatusListItemDecoration());
|
||||||
((UsableRecyclerView)list).setSelectorBoundsProvider(new UsableRecyclerView.SelectorBoundsProvider(){
|
((UsableRecyclerView)list).setSelectorBoundsProvider(new UsableRecyclerView.SelectorBoundsProvider(){
|
||||||
private Rect tmpRect=new Rect();
|
private Rect tmpRect=new Rect();
|
||||||
@@ -364,11 +365,12 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
|||||||
((UsableRecyclerView) list).setIncludeMarginsInItemHitbox(true);
|
((UsableRecyclerView) list).setIncludeMarginsInItemHitbox(true);
|
||||||
updateToolbar();
|
updateToolbar();
|
||||||
|
|
||||||
if (withComposeButton()) {
|
if (wantsComposeButton() && !getArguments().getBoolean("__disable_fab", false)) {
|
||||||
fab = view.findViewById(R.id.fab);
|
|
||||||
fab.setVisibility(View.VISIBLE);
|
fab.setVisibility(View.VISIBLE);
|
||||||
fab.setOnClickListener(this::onFabClick);
|
fab.setOnClickListener(this::onFabClick);
|
||||||
fab.setOnLongClickListener(this::onFabLongClick);
|
fab.setOnLongClickListener(this::onFabLongClick);
|
||||||
|
} else if (fab != null) {
|
||||||
|
fab.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -696,13 +698,13 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
|||||||
currentPhotoViewer.onPause();
|
currentPhotoViewer.onPause();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void onFabClick(View v){
|
public void onFabClick(View v){
|
||||||
Bundle args=new Bundle();
|
Bundle args=new Bundle();
|
||||||
args.putString("account", accountID);
|
args.putString("account", accountID);
|
||||||
Nav.go(getActivity(), ComposeFragment.class, args);
|
Nav.go(getActivity(), ComposeFragment.class, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean onFabLongClick(View v) {
|
public boolean onFabLongClick(View v) {
|
||||||
return UiUtils.pickAccountForCompose(getActivity(), accountID);
|
return UiUtils.pickAccountForCompose(getActivity(), accountID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -731,6 +731,8 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
String ownID=AccountSessionManager.getInstance().getAccount(accountID).self.id;
|
String ownID=AccountSessionManager.getInstance().getAccount(accountID).self.id;
|
||||||
if(!status.account.id.equals(ownID))
|
if(!status.account.id.equals(ownID))
|
||||||
mentions.add('@'+status.account.acct);
|
mentions.add('@'+status.account.acct);
|
||||||
|
if(status.rebloggedBy != null && GlobalUserPreferences.mentionRebloggerAutomatically)
|
||||||
|
mentions.add('@'+status.rebloggedBy.acct);
|
||||||
for(Mention mention:status.mentions){
|
for(Mention mention:status.mentions){
|
||||||
if(mention.id.equals(ownID))
|
if(mention.id.equals(ownID))
|
||||||
continue;
|
continue;
|
||||||
@@ -1602,7 +1604,8 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
if(att.isUploadingOrProcessing())
|
if(att.isUploadingOrProcessing())
|
||||||
att.cancelUpload();
|
att.cancelUpload();
|
||||||
attachments.remove(att);
|
attachments.remove(att);
|
||||||
uploadNextQueuedAttachment();
|
if(!areThereAnyUploadingAttachments())
|
||||||
|
uploadNextQueuedAttachment();
|
||||||
attachmentsView.removeView(att.view);
|
attachmentsView.removeView(att.view);
|
||||||
if(getMediaAttachmentsCount()==0)
|
if(getMediaAttachmentsCount()==0)
|
||||||
attachmentsView.setVisibility(View.GONE);
|
attachmentsView.setVisibility(View.GONE);
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ public class CustomLocalTimelineFragment extends StatusListFragment {
|
|||||||
|
|
||||||
private String maxID;
|
private String maxID;
|
||||||
@Override
|
@Override
|
||||||
protected boolean withComposeButton() {
|
protected boolean wantsComposeButton() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package org.joinmastodon.android.fragments;
|
||||||
|
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
public interface HasFab {
|
||||||
|
View getFab();
|
||||||
|
}
|
||||||
@@ -40,7 +40,7 @@ public class HashtagTimelineFragment extends PinnableStatusListFragment {
|
|||||||
private MenuItem followButton;
|
private MenuItem followButton;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean withComposeButton() {
|
protected boolean wantsComposeButton() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,12 +146,12 @@ public class HashtagTimelineFragment extends PinnableStatusListFragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean onFabLongClick(View v) {
|
public boolean onFabLongClick(View v) {
|
||||||
return UiUtils.pickAccountForCompose(getActivity(), accountID, '#'+hashtag+' ');
|
return UiUtils.pickAccountForCompose(getActivity(), accountID, '#'+hashtag+' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onFabClick(View v){
|
public void onFabClick(View v){
|
||||||
Bundle args=new Bundle();
|
Bundle args=new Bundle();
|
||||||
args.putString("account", accountID);
|
args.putString("account", accountID);
|
||||||
args.putString("prefilledText", '#'+hashtag+' ');
|
args.putString("prefilledText", '#'+hashtag+' ');
|
||||||
|
|||||||
@@ -31,7 +31,9 @@ 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.events.AllNotificationsSeenEvent;
|
import org.joinmastodon.android.events.AllNotificationsSeenEvent;
|
||||||
import org.joinmastodon.android.events.NotificationReceivedEvent;
|
import org.joinmastodon.android.events.NotificationReceivedEvent;
|
||||||
|
import org.joinmastodon.android.fragments.discover.DiscoverAccountsFragment;
|
||||||
import org.joinmastodon.android.fragments.discover.DiscoverFragment;
|
import org.joinmastodon.android.fragments.discover.DiscoverFragment;
|
||||||
|
import org.joinmastodon.android.fragments.onboarding.OnboardingFollowSuggestionsFragment;
|
||||||
import org.joinmastodon.android.model.Account;
|
import org.joinmastodon.android.model.Account;
|
||||||
import org.joinmastodon.android.model.Instance;
|
import org.joinmastodon.android.model.Instance;
|
||||||
import org.joinmastodon.android.model.Notification;
|
import org.joinmastodon.android.model.Notification;
|
||||||
@@ -50,6 +52,7 @@ import androidx.annotation.Nullable;
|
|||||||
import com.squareup.otto.Subscribe;
|
import com.squareup.otto.Subscribe;
|
||||||
|
|
||||||
import me.grishka.appkit.FragmentStackActivity;
|
import me.grishka.appkit.FragmentStackActivity;
|
||||||
|
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.AppKitFragment;
|
import me.grishka.appkit.fragments.AppKitFragment;
|
||||||
@@ -116,7 +119,7 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
|
|||||||
content.setOrientation(LinearLayout.VERTICAL);
|
content.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
|
||||||
FrameLayout fragmentContainer=new FrameLayout(getActivity());
|
FrameLayout fragmentContainer=new FrameLayout(getActivity());
|
||||||
fragmentContainer.setId(R.id.fragment_wrap);
|
fragmentContainer.setId(me.grishka.appkit.R.id.fragment_wrap);
|
||||||
content.addView(fragmentContainer, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0, 1f));
|
content.addView(fragmentContainer, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0, 1f));
|
||||||
|
|
||||||
inflater.inflate(R.layout.tab_bar, content);
|
inflater.inflate(R.layout.tab_bar, content);
|
||||||
@@ -140,10 +143,10 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
|
|||||||
|
|
||||||
if(savedInstanceState==null){
|
if(savedInstanceState==null){
|
||||||
getChildFragmentManager().beginTransaction()
|
getChildFragmentManager().beginTransaction()
|
||||||
.add(R.id.fragment_wrap, homeTabFragment)
|
.add(me.grishka.appkit.R.id.fragment_wrap, homeTabFragment)
|
||||||
.add(R.id.fragment_wrap, searchFragment).hide(searchFragment)
|
.add(me.grishka.appkit.R.id.fragment_wrap, searchFragment).hide(searchFragment)
|
||||||
.add(R.id.fragment_wrap, notificationsFragment).hide(notificationsFragment)
|
.add(me.grishka.appkit.R.id.fragment_wrap, notificationsFragment).hide(notificationsFragment)
|
||||||
.add(R.id.fragment_wrap, profileFragment).hide(profileFragment)
|
.add(me.grishka.appkit.R.id.fragment_wrap, profileFragment).hide(profileFragment)
|
||||||
.commit();
|
.commit();
|
||||||
|
|
||||||
|
|
||||||
@@ -304,6 +307,11 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
|
|||||||
searchFragment.selectSearch();
|
searchFragment.selectSearch();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if(tab==R.id.tab_home){
|
||||||
|
Bundle args=new Bundle();
|
||||||
|
args.putString("account", accountID);
|
||||||
|
Nav.go(getActivity(), OnboardingFollowSuggestionsFragment.class, args);
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import android.view.ViewParent;
|
|||||||
import android.view.ViewTreeObserver;
|
import android.view.ViewTreeObserver;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
|
import android.widget.ImageButton;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.PopupMenu;
|
import android.widget.PopupMenu;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
@@ -72,7 +73,7 @@ import me.grishka.appkit.fragments.OnBackPressedListener;
|
|||||||
import me.grishka.appkit.utils.CubicBezierInterpolator;
|
import me.grishka.appkit.utils.CubicBezierInterpolator;
|
||||||
import me.grishka.appkit.utils.V;
|
import me.grishka.appkit.utils.V;
|
||||||
|
|
||||||
public class HomeTabFragment extends MastodonToolbarFragment implements ScrollableToTop, OnBackPressedListener, DomainDisplay {
|
public class HomeTabFragment extends MastodonToolbarFragment implements ScrollableToTop, OnBackPressedListener, DomainDisplay, HasFab {
|
||||||
private static final int ANNOUNCEMENTS_RESULT = 654;
|
private static final int ANNOUNCEMENTS_RESULT = 654;
|
||||||
|
|
||||||
private String accountID;
|
private String accountID;
|
||||||
@@ -100,6 +101,7 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
|||||||
private PopupMenu overflowPopup;
|
private PopupMenu overflowPopup;
|
||||||
private View overflowActionView = null;
|
private View overflowActionView = null;
|
||||||
private boolean announcementsBadged, settingsBadged;
|
private boolean announcementsBadged, settingsBadged;
|
||||||
|
private ImageButton fab;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
@@ -113,6 +115,10 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
|||||||
fragments = new Fragment[count];
|
fragments = new Fragment[count];
|
||||||
tabViews = new FrameLayout[count];
|
tabViews = new FrameLayout[count];
|
||||||
timelines = new TimelineDefinition[count];
|
timelines = new TimelineDefinition[count];
|
||||||
|
if(GlobalUserPreferences.disableMarquee){
|
||||||
|
setTitleMarqueeEnabled(false);
|
||||||
|
setSubtitleMarqueeEnabled(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -124,6 +130,10 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
|||||||
@Override
|
@Override
|
||||||
public View onCreateContentView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
|
public View onCreateContentView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
|
||||||
FrameLayout view = new FrameLayout(getContext());
|
FrameLayout view = new FrameLayout(getContext());
|
||||||
|
inflater.inflate(R.layout.compose_fab, view);
|
||||||
|
fab = view.findViewById(R.id.fab);
|
||||||
|
fab.setOnClickListener(this::onFabClick);
|
||||||
|
fab.setOnLongClickListener(this::onFabLongClick);
|
||||||
pager = new ViewPager2(getContext());
|
pager = new ViewPager2(getContext());
|
||||||
toolbarFrame = (FrameLayout) LayoutInflater.from(getContext()).inflate(R.layout.home_toolbar, getToolbar(), false);
|
toolbarFrame = (FrameLayout) LayoutInflater.from(getContext()).inflate(R.layout.home_toolbar, getToolbar(), false);
|
||||||
|
|
||||||
@@ -131,6 +141,7 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
|||||||
Bundle args = new Bundle();
|
Bundle args = new Bundle();
|
||||||
args.putString("account", accountID);
|
args.putString("account", accountID);
|
||||||
args.putBoolean("__is_tab", true);
|
args.putBoolean("__is_tab", true);
|
||||||
|
args.putBoolean("__disable_fab", true);
|
||||||
args.putBoolean("onlyPosts", true);
|
args.putBoolean("onlyPosts", true);
|
||||||
|
|
||||||
for (int i = 0; i < timelineDefinitions.size(); i++) {
|
for (int i = 0; i < timelineDefinitions.size(); i++) {
|
||||||
@@ -294,6 +305,20 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
|||||||
return DomainDisplay.super.getDomain();
|
return DomainDisplay.super.getDomain();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void onFabClick(View v){
|
||||||
|
if (fragments[pager.getCurrentItem()] instanceof BaseStatusListFragment<?> l) {
|
||||||
|
l.onFabClick(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean onFabLongClick(View v) {
|
||||||
|
if (fragments[pager.getCurrentItem()] instanceof BaseStatusListFragment<?> l) {
|
||||||
|
return l.onFabLongClick(v);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void addListsToOverflowMenu() {
|
private void addListsToOverflowMenu() {
|
||||||
Context ctx = getContext();
|
Context ctx = getContext();
|
||||||
listsMenu.clear();
|
listsMenu.clear();
|
||||||
@@ -444,6 +469,7 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
|||||||
private void updateSwitcherIcon(int i) {
|
private void updateSwitcherIcon(int i) {
|
||||||
timelineIcon.setImageResource(timelines[i].getIcon().iconRes);
|
timelineIcon.setImageResource(timelines[i].getIcon().iconRes);
|
||||||
timelineTitle.setText(timelines[i].getTitle(getContext()));
|
timelineTitle.setText(timelines[i].getTitle(getContext()));
|
||||||
|
if (fragments[i] instanceof BaseStatusListFragment<?> l) l.animateFab(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -682,6 +708,10 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
|||||||
return hashtagsItems.values();
|
return hashtagsItems.values();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ImageButton getFab() {
|
||||||
|
return fab;
|
||||||
|
}
|
||||||
|
|
||||||
private class HomePagerAdapter extends RecyclerView.Adapter<SimpleViewHolder> {
|
private class HomePagerAdapter extends RecyclerView.Adapter<SimpleViewHolder> {
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ public class HomeTimelineFragment extends StatusListFragment {
|
|||||||
private String lastSavedMarkerID;
|
private String lastSavedMarkerID;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean withComposeButton() {
|
protected boolean wantsComposeButton() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ public class ListTimelineFragment extends PinnableStatusListFragment {
|
|||||||
private ListTimeline.RepliesPolicy repliesPolicy;
|
private ListTimeline.RepliesPolicy repliesPolicy;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean withComposeButton() {
|
protected boolean wantsComposeButton() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,7 +152,7 @@ public class ListTimelineFragment extends PinnableStatusListFragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onFabClick(View v){
|
public void onFabClick(View v){
|
||||||
Bundle args=new Bundle();
|
Bundle args=new Bundle();
|
||||||
args.putString("account", accountID);
|
args.putString("account", accountID);
|
||||||
Nav.go(getActivity(), ComposeFragment.class, args);
|
Nav.go(getActivity(), ComposeFragment.class, args);
|
||||||
|
|||||||
@@ -16,14 +16,15 @@ import org.joinmastodon.android.events.PollUpdatedEvent;
|
|||||||
import org.joinmastodon.android.events.RemoveAccountPostsEvent;
|
import org.joinmastodon.android.events.RemoveAccountPostsEvent;
|
||||||
import org.joinmastodon.android.model.Account;
|
import org.joinmastodon.android.model.Account;
|
||||||
import org.joinmastodon.android.model.CacheablePaginatedResponse;
|
import org.joinmastodon.android.model.CacheablePaginatedResponse;
|
||||||
|
import org.joinmastodon.android.model.Emoji;
|
||||||
import org.joinmastodon.android.model.Filter;
|
import org.joinmastodon.android.model.Filter;
|
||||||
import org.joinmastodon.android.model.Notification;
|
import org.joinmastodon.android.model.Notification;
|
||||||
import org.joinmastodon.android.model.PaginatedResponse;
|
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
import org.joinmastodon.android.ui.displayitems.AccountCardStatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.AccountCardStatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.displayitems.HeaderStatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.HeaderStatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.displayitems.TextStatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.TextStatusDisplayItem;
|
||||||
|
import org.joinmastodon.android.ui.text.HtmlParser;
|
||||||
import org.joinmastodon.android.ui.utils.DiscoverInfoBannerHelper;
|
import org.joinmastodon.android.ui.utils.DiscoverInfoBannerHelper;
|
||||||
import org.joinmastodon.android.ui.utils.InsetStatusItemDecoration;
|
import org.joinmastodon.android.ui.utils.InsetStatusItemDecoration;
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
@@ -48,8 +49,8 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
|
|||||||
private final DiscoverInfoBannerHelper bannerHelper = new DiscoverInfoBannerHelper(DiscoverInfoBannerHelper.BannerType.POST_NOTIFICATIONS);
|
private final DiscoverInfoBannerHelper bannerHelper = new DiscoverInfoBannerHelper(DiscoverInfoBannerHelper.BannerType.POST_NOTIFICATIONS);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean withComposeButton() {
|
protected boolean wantsComposeButton() {
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -88,6 +89,13 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
|
|||||||
protected List<StatusDisplayItem> buildDisplayItems(Notification n){
|
protected List<StatusDisplayItem> buildDisplayItems(Notification n){
|
||||||
Account reportTarget = n.report == null ? null : n.report.targetAccount == null ? null :
|
Account reportTarget = n.report == null ? null : n.report.targetAccount == null ? null :
|
||||||
n.report.targetAccount;
|
n.report.targetAccount;
|
||||||
|
Emoji emoji = new Emoji();
|
||||||
|
if(n.emojiUrl!=null){
|
||||||
|
emoji.shortcode=n.emoji.substring(1,n.emoji.length()-1);
|
||||||
|
emoji.url=n.emojiUrl;
|
||||||
|
emoji.staticUrl=n.emojiUrl;
|
||||||
|
emoji.visibleInPicker=false;
|
||||||
|
}
|
||||||
String extraText=switch(n.type){
|
String extraText=switch(n.type){
|
||||||
case FOLLOW -> getString(R.string.user_followed_you);
|
case FOLLOW -> getString(R.string.user_followed_you);
|
||||||
case FOLLOW_REQUEST -> getString(R.string.user_sent_follow_request);
|
case FOLLOW_REQUEST -> getString(R.string.user_sent_follow_request);
|
||||||
@@ -98,8 +106,10 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
|
|||||||
case UPDATE -> getString(R.string.sk_post_edited);
|
case UPDATE -> getString(R.string.sk_post_edited);
|
||||||
case SIGN_UP -> getString(R.string.sk_signed_up);
|
case SIGN_UP -> getString(R.string.sk_signed_up);
|
||||||
case REPORT -> getString(R.string.sk_reported);
|
case REPORT -> getString(R.string.sk_reported);
|
||||||
|
case REACTION, PLEROMA_EMOJI_REACTION ->
|
||||||
|
n.emoji != null ? getString(R.string.sk_reacted_with, n.emoji) : getString(R.string.sk_reacted);
|
||||||
};
|
};
|
||||||
HeaderStatusDisplayItem titleItem=extraText!=null ? new HeaderStatusDisplayItem(n.id, n.account, n.createdAt, this, accountID, n.status, extraText, n, null) : null;
|
HeaderStatusDisplayItem titleItem=extraText!=null ? new HeaderStatusDisplayItem(n.id, n.account, n.createdAt, this, accountID, n.status, n.emojiUrl!=null ? HtmlParser.parseCustomEmoji(extraText, Collections.singletonList(emoji)) : extraText, n, null) : null;
|
||||||
if(n.status!=null){
|
if(n.status!=null){
|
||||||
ArrayList<StatusDisplayItem> items=StatusDisplayItem.buildItems(this, n.status, accountID, n, knownAccounts, titleItem!=null, titleItem==null, n, false, Filter.FilterContext.NOTIFICATIONS);
|
ArrayList<StatusDisplayItem> items=StatusDisplayItem.buildItems(this, n.status, accountID, n, knownAccounts, titleItem!=null, titleItem==null, n, false, Filter.FilterContext.NOTIFICATIONS);
|
||||||
if(titleItem!=null)
|
if(titleItem!=null)
|
||||||
@@ -146,7 +156,7 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
|
|||||||
loadRelationships(needRelationships);
|
loadRelationships(needRelationships);
|
||||||
maxID=result.maxID;
|
maxID=result.maxID;
|
||||||
|
|
||||||
if(offset==0 && !result.items.isEmpty() && !result.isFromCache()){
|
if(offset==0 && !result.items.isEmpty() && !result.isFromCache() && AccountSessionManager.getInstance().getAccount(accountID).markers.notifications != null){
|
||||||
E.post(new AllNotificationsSeenEvent());
|
E.post(new AllNotificationsSeenEvent());
|
||||||
new SaveMarkers(null, result.items.get(0).id).exec(accountID);
|
new SaveMarkers(null, result.items.get(0).id).exec(accountID);
|
||||||
AccountSessionManager.getInstance().getAccount(accountID).markers
|
AccountSessionManager.getInstance().getAccount(accountID).markers
|
||||||
@@ -204,7 +214,6 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
|
|||||||
public void onViewCreated(View view, Bundle savedInstanceState){
|
public void onViewCreated(View view, Bundle savedInstanceState){
|
||||||
super.onViewCreated(view, savedInstanceState);
|
super.onViewCreated(view, savedInstanceState);
|
||||||
list.addItemDecoration(new InsetStatusItemDecoration(this));
|
list.addItemDecoration(new InsetStatusItemDecoration(this));
|
||||||
if (getParentFragment() instanceof NotificationsFragment) fab.setVisibility(View.GONE);
|
|
||||||
if (onlyPosts) bannerHelper.maybeAddBanner(contentWrap);
|
if (onlyPosts) bannerHelper.maybeAddBanner(contentWrap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -402,7 +402,7 @@ public class ProfileAboutFragment extends Fragment implements WindowInsetsAwareF
|
|||||||
public void onSelectedChanged(@Nullable RecyclerView.ViewHolder viewHolder, int actionState){
|
public void onSelectedChanged(@Nullable RecyclerView.ViewHolder viewHolder, int actionState){
|
||||||
super.onSelectedChanged(viewHolder, actionState);
|
super.onSelectedChanged(viewHolder, actionState);
|
||||||
if(actionState==ItemTouchHelper.ACTION_STATE_DRAG){
|
if(actionState==ItemTouchHelper.ACTION_STATE_DRAG){
|
||||||
viewHolder.itemView.setTag(R.id.item_touch_helper_previous_elevation, viewHolder.itemView.getElevation()); // prevents the default behavior of changing elevation in onDraw()
|
viewHolder.itemView.setTag(me.grishka.appkit.R.id.item_touch_helper_previous_elevation, viewHolder.itemView.getElevation()); // prevents the default behavior of changing elevation in onDraw()
|
||||||
viewHolder.itemView.animate().translationZ(V.dp(1)).setDuration(200).setInterpolator(CubicBezierInterpolator.DEFAULT).start();
|
viewHolder.itemView.animate().translationZ(V.dp(1)).setDuration(200).setInterpolator(CubicBezierInterpolator.DEFAULT).start();
|
||||||
draggedViewHolder=viewHolder;
|
draggedViewHolder=viewHolder;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ import me.grishka.appkit.utils.CubicBezierInterpolator;
|
|||||||
import me.grishka.appkit.utils.V;
|
import me.grishka.appkit.utils.V;
|
||||||
import me.grishka.appkit.views.UsableRecyclerView;
|
import me.grishka.appkit.views.UsableRecyclerView;
|
||||||
|
|
||||||
public class ProfileFragment extends LoaderFragment implements OnBackPressedListener, ScrollableToTop{
|
public class ProfileFragment extends LoaderFragment implements OnBackPressedListener, ScrollableToTop, HasFab{
|
||||||
private static final int AVATAR_RESULT=722;
|
private static final int AVATAR_RESULT=722;
|
||||||
private static final int COVER_RESULT=343;
|
private static final int COVER_RESULT=343;
|
||||||
|
|
||||||
@@ -158,7 +158,6 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
private WindowInsets childInsets;
|
private WindowInsets childInsets;
|
||||||
private PhotoViewer currentPhotoViewer;
|
private PhotoViewer currentPhotoViewer;
|
||||||
private boolean editModeLoading;
|
private boolean editModeLoading;
|
||||||
protected int scrollDiff = 0;
|
|
||||||
|
|
||||||
private static final int MAX_FIELDS=4;
|
private static final int MAX_FIELDS=4;
|
||||||
|
|
||||||
@@ -232,7 +231,6 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
followingCount=content.findViewById(R.id.following_count);
|
followingCount=content.findViewById(R.id.following_count);
|
||||||
followingLabel=content.findViewById(R.id.following_label);
|
followingLabel=content.findViewById(R.id.following_label);
|
||||||
followingBtn=content.findViewById(R.id.following_btn);
|
followingBtn=content.findViewById(R.id.following_btn);
|
||||||
|
|
||||||
postsCount=content.findViewById(R.id.posts_count);
|
postsCount=content.findViewById(R.id.posts_count);
|
||||||
postsLabel=content.findViewById(R.id.posts_label);
|
postsLabel=content.findViewById(R.id.posts_label);
|
||||||
postsBtn=content.findViewById(R.id.posts_btn);
|
postsBtn=content.findViewById(R.id.posts_btn);
|
||||||
@@ -594,7 +592,6 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
|
|
||||||
boolean isSelf=AccountSessionManager.getInstance().isSelf(accountID, account);
|
boolean isSelf=AccountSessionManager.getInstance().isSelf(accountID, account);
|
||||||
|
|
||||||
|
|
||||||
if(account.locked){
|
if(account.locked){
|
||||||
ssb=new SpannableStringBuilder("@");
|
ssb=new SpannableStringBuilder("@");
|
||||||
ssb.append(account.acct);
|
ssb.append(account.acct);
|
||||||
@@ -902,36 +899,6 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
if(currentPhotoViewer!=null){
|
if(currentPhotoViewer!=null){
|
||||||
currentPhotoViewer.offsetView(0, oldScrollY-scrollY);
|
currentPhotoViewer.offsetView(0, oldScrollY-scrollY);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(GlobalUserPreferences.enableFabAutoHide){
|
|
||||||
int dy = scrollY - oldScrollY;
|
|
||||||
|
|
||||||
if (dy > 0 && fab.getVisibility() == View.VISIBLE) {
|
|
||||||
TranslateAnimation animate = new TranslateAnimation(
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
fab.getHeight() * 2);
|
|
||||||
animate.setDuration(300);
|
|
||||||
fab.startAnimation(animate);
|
|
||||||
fab.setVisibility(View.INVISIBLE);
|
|
||||||
scrollDiff = 0;
|
|
||||||
} else if (dy < 0 && fab.getVisibility() != View.VISIBLE) {
|
|
||||||
if (scrollDiff > 400) {
|
|
||||||
fab.setVisibility(View.VISIBLE);
|
|
||||||
TranslateAnimation animate = new TranslateAnimation(
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
fab.getHeight() * 2,
|
|
||||||
0);
|
|
||||||
animate.setDuration(300);
|
|
||||||
fab.startAnimation(animate);
|
|
||||||
scrollDiff = 0;
|
|
||||||
} else {
|
|
||||||
scrollDiff += Math.abs(dy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Fragment getFragmentForPage(int page){
|
private Fragment getFragmentForPage(int page){
|
||||||
@@ -1512,7 +1479,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
public void onSelectedChanged(@Nullable RecyclerView.ViewHolder viewHolder, int actionState){
|
public void onSelectedChanged(@Nullable RecyclerView.ViewHolder viewHolder, int actionState){
|
||||||
super.onSelectedChanged(viewHolder, actionState);
|
super.onSelectedChanged(viewHolder, actionState);
|
||||||
if(actionState==ItemTouchHelper.ACTION_STATE_DRAG){
|
if(actionState==ItemTouchHelper.ACTION_STATE_DRAG){
|
||||||
viewHolder.itemView.setTag(R.id.item_touch_helper_previous_elevation, viewHolder.itemView.getElevation()); // prevents the default behavior of changing elevation in onDraw()
|
viewHolder.itemView.setTag(me.grishka.appkit.R.id.item_touch_helper_previous_elevation, viewHolder.itemView.getElevation()); // prevents the default behavior of changing elevation in onDraw()
|
||||||
viewHolder.itemView.animate().translationZ(V.dp(1)).setDuration(200).setInterpolator(CubicBezierInterpolator.DEFAULT).start();
|
viewHolder.itemView.animate().translationZ(V.dp(1)).setDuration(200).setInterpolator(CubicBezierInterpolator.DEFAULT).start();
|
||||||
draggedViewHolder=viewHolder;
|
draggedViewHolder=viewHolder;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ public class ScheduledStatusListFragment extends BaseStatusListFragment<Schedule
|
|||||||
private static final int SCHEDULED_STATUS_LIST_OPENED = 161;
|
private static final int SCHEDULED_STATUS_LIST_OPENED = 161;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean withComposeButton() {
|
protected boolean wantsComposeButton() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@ public class ScheduledStatusListFragment extends BaseStatusListFragment<Schedule
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onFabClick(View v) {
|
public void onFabClick(View v) {
|
||||||
Bundle args=new Bundle();
|
Bundle args=new Bundle();
|
||||||
args.putString("account", accountID);
|
args.putString("account", accountID);
|
||||||
args.putSerializable("scheduledAt", CreateStatus.getDraftInstant());
|
args.putSerializable("scheduledAt", CreateStatus.getDraftInstant());
|
||||||
@@ -65,7 +65,7 @@ public class ScheduledStatusListFragment extends BaseStatusListFragment<Schedule
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean onFabLongClick(View v) {
|
public boolean onFabLongClick(View v) {
|
||||||
Bundle args=new Bundle();
|
Bundle args=new Bundle();
|
||||||
args.putString("account", accountID);
|
args.putString("account", accountID);
|
||||||
args.putSerializable("scheduledAt", CreateStatus.getDraftInstant());
|
args.putSerializable("scheduledAt", CreateStatus.getDraftInstant());
|
||||||
|
|||||||
@@ -7,9 +7,12 @@ import android.content.Intent;
|
|||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.provider.Settings;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
import android.util.Log;
|
||||||
import android.util.LruCache;
|
import android.util.LruCache;
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
import android.view.Gravity;
|
import android.view.Gravity;
|
||||||
@@ -42,6 +45,7 @@ import org.joinmastodon.android.GlobalUserPreferences;
|
|||||||
import org.joinmastodon.android.GlobalUserPreferences.ColorPreference;
|
import org.joinmastodon.android.GlobalUserPreferences.ColorPreference;
|
||||||
import org.joinmastodon.android.MainActivity;
|
import org.joinmastodon.android.MainActivity;
|
||||||
import org.joinmastodon.android.MastodonApp;
|
import org.joinmastodon.android.MastodonApp;
|
||||||
|
import org.joinmastodon.android.PushNotificationReceiver;
|
||||||
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.PushSubscriptionManager;
|
import org.joinmastodon.android.api.PushSubscriptionManager;
|
||||||
@@ -62,6 +66,7 @@ import org.joinmastodon.android.updater.GithubSelfUpdater;
|
|||||||
import org.parceler.Parcels;
|
import org.parceler.Parcels;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import androidx.annotation.DrawableRes;
|
import androidx.annotation.DrawableRes;
|
||||||
@@ -78,6 +83,7 @@ import me.grishka.appkit.utils.V;
|
|||||||
import me.grishka.appkit.views.UsableRecyclerView;
|
import me.grishka.appkit.views.UsableRecyclerView;
|
||||||
|
|
||||||
public class SettingsFragment extends MastodonToolbarFragment{
|
public class SettingsFragment extends MastodonToolbarFragment{
|
||||||
|
private View view;
|
||||||
private UsableRecyclerView list;
|
private UsableRecyclerView list;
|
||||||
private ArrayList<Item> items=new ArrayList<>();
|
private ArrayList<Item> items=new ArrayList<>();
|
||||||
private ThemeItem themeItem;
|
private ThemeItem themeItem;
|
||||||
@@ -109,7 +115,7 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
|||||||
if(GithubSelfUpdater.needSelfUpdating()){
|
if(GithubSelfUpdater.needSelfUpdating()){
|
||||||
GithubSelfUpdater updater=GithubSelfUpdater.getInstance();
|
GithubSelfUpdater updater=GithubSelfUpdater.getInstance();
|
||||||
GithubSelfUpdater.UpdateState state=updater.getState();
|
GithubSelfUpdater.UpdateState state=updater.getState();
|
||||||
if(state!=GithubSelfUpdater.UpdateState.NO_UPDATE && state!=GithubSelfUpdater.UpdateState.CHECKING){
|
if(state!=GithubSelfUpdater.UpdateState.NO_UPDATE && state!=GithubSelfUpdater.UpdateState.CHECKING && updater.getUpdateInfo() != null){
|
||||||
items.add(new UpdateItem());
|
items.add(new UpdateItem());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -140,6 +146,7 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
|||||||
items.add(new SwitchItem(R.string.sk_disable_marquee, R.drawable.ic_fluent_text_more_24_regular, GlobalUserPreferences.disableMarquee, i->{
|
items.add(new SwitchItem(R.string.sk_disable_marquee, R.drawable.ic_fluent_text_more_24_regular, GlobalUserPreferences.disableMarquee, i->{
|
||||||
GlobalUserPreferences.disableMarquee=i.checked;
|
GlobalUserPreferences.disableMarquee=i.checked;
|
||||||
GlobalUserPreferences.save();
|
GlobalUserPreferences.save();
|
||||||
|
needAppRestart=true;
|
||||||
}));
|
}));
|
||||||
items.add(new SwitchItem(R.string.sk_settings_uniform_icon_for_notifications, R.drawable.ic_ntf_logo, GlobalUserPreferences.uniformNotificationIcon, i->{
|
items.add(new SwitchItem(R.string.sk_settings_uniform_icon_for_notifications, R.drawable.ic_ntf_logo, GlobalUserPreferences.uniformNotificationIcon, i->{
|
||||||
GlobalUserPreferences.uniformNotificationIcon=i.checked;
|
GlobalUserPreferences.uniformNotificationIcon=i.checked;
|
||||||
@@ -161,6 +168,15 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
|||||||
GlobalUserPreferences.useCustomTabs=i.checked;
|
GlobalUserPreferences.useCustomTabs=i.checked;
|
||||||
GlobalUserPreferences.save();
|
GlobalUserPreferences.save();
|
||||||
}));
|
}));
|
||||||
|
items.add(new SwitchItem(R.string.mo_hide_compose_button_while_scrolling_setting, R.drawable.ic_fluent_edit_24_regular, GlobalUserPreferences.enableFabAutoHide, i->{
|
||||||
|
GlobalUserPreferences.enableFabAutoHide =i.checked;
|
||||||
|
GlobalUserPreferences.save();
|
||||||
|
needAppRestart=true;
|
||||||
|
}));
|
||||||
|
items.add(new SwitchItem(R.string.mo_load_remote_followers, R.drawable.ic_fluent_people_24_regular, GlobalUserPreferences.loadRemoteAccountFollowers, i -> {
|
||||||
|
GlobalUserPreferences.loadRemoteAccountFollowers=i.checked;
|
||||||
|
GlobalUserPreferences.save();
|
||||||
|
}));
|
||||||
items.add(new SwitchItem(R.string.sk_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();
|
||||||
@@ -171,11 +187,6 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
// items.add(new SwitchItem(R.string.sk_settings_show_differentiated_notification_icons, R.drawable.ic_ntf_logo, GlobalUserPreferences.showUniformPushNoticationIcons, this::onNotificationStyleChanged));
|
// items.add(new SwitchItem(R.string.sk_settings_show_differentiated_notification_icons, R.drawable.ic_ntf_logo, GlobalUserPreferences.showUniformPushNoticationIcons, this::onNotificationStyleChanged));
|
||||||
items.add(new SwitchItem(R.string.mo_hide_compose_button_while_scrolling_setting, R.drawable.ic_fluent_edit_24_regular, GlobalUserPreferences.enableFabAutoHide, i->{
|
|
||||||
GlobalUserPreferences.enableFabAutoHide =i.checked;
|
|
||||||
GlobalUserPreferences.save();
|
|
||||||
needAppRestart=true;
|
|
||||||
}));
|
|
||||||
items.add(new SwitchItem(R.string.sk_tabs_disable_swipe, R.drawable.ic_fluent_swipe_right_24_regular, GlobalUserPreferences.disableSwipe, i->{
|
items.add(new SwitchItem(R.string.sk_tabs_disable_swipe, R.drawable.ic_fluent_swipe_right_24_regular, GlobalUserPreferences.disableSwipe, i->{
|
||||||
GlobalUserPreferences.disableSwipe=i.checked;
|
GlobalUserPreferences.disableSwipe=i.checked;
|
||||||
GlobalUserPreferences.save();
|
GlobalUserPreferences.save();
|
||||||
@@ -238,6 +249,10 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
|||||||
GlobalUserPreferences.defaultToUnlistedReplies=i.checked;
|
GlobalUserPreferences.defaultToUnlistedReplies=i.checked;
|
||||||
GlobalUserPreferences.save();
|
GlobalUserPreferences.save();
|
||||||
}));
|
}));
|
||||||
|
items.add(new SwitchItem(R.string.mo_mention_reblogger_automatically, R.drawable.ic_fluent_comment_mention_24_regular, GlobalUserPreferences.mentionRebloggerAutomatically, i -> {
|
||||||
|
GlobalUserPreferences.mentionRebloggerAutomatically=i.checked;
|
||||||
|
GlobalUserPreferences.save();
|
||||||
|
}));
|
||||||
items.add(new SwitchItem(R.string.mo_disable_reminder_to_add_alt_text, R.drawable.ic_fluent_image_alt_text_24_regular, GlobalUserPreferences.disableAltTextReminder, i->{
|
items.add(new SwitchItem(R.string.mo_disable_reminder_to_add_alt_text, R.drawable.ic_fluent_image_alt_text_24_regular, GlobalUserPreferences.disableAltTextReminder, i->{
|
||||||
GlobalUserPreferences.disableAltTextReminder=i.checked;
|
GlobalUserPreferences.disableAltTextReminder=i.checked;
|
||||||
GlobalUserPreferences.save();
|
GlobalUserPreferences.save();
|
||||||
@@ -302,11 +317,6 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
|||||||
GlobalUserPreferences.collapseLongPosts=i.checked;
|
GlobalUserPreferences.collapseLongPosts=i.checked;
|
||||||
GlobalUserPreferences.save();
|
GlobalUserPreferences.save();
|
||||||
}));
|
}));
|
||||||
items.add(new SwitchItem(R.string.sk_settings_hide_interaction, R.drawable.ic_fluent_eye_24_regular, GlobalUserPreferences.spectatorMode, i->{
|
|
||||||
GlobalUserPreferences.spectatorMode=i.checked;
|
|
||||||
GlobalUserPreferences.save();
|
|
||||||
needAppRestart=true;
|
|
||||||
}));
|
|
||||||
items.add(new SwitchItem(R.string.sk_settings_hide_fab, R.drawable.ic_fluent_edit_24_regular, GlobalUserPreferences.autoHideFab, i->{
|
items.add(new SwitchItem(R.string.sk_settings_hide_fab, R.drawable.ic_fluent_edit_24_regular, GlobalUserPreferences.autoHideFab, i->{
|
||||||
GlobalUserPreferences.autoHideFab=i.checked;
|
GlobalUserPreferences.autoHideFab=i.checked;
|
||||||
GlobalUserPreferences.save();
|
GlobalUserPreferences.save();
|
||||||
@@ -327,6 +337,11 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
|||||||
needAppRestart=true;
|
needAppRestart=true;
|
||||||
}));
|
}));
|
||||||
compactReblogReplyLineItem.enabled=GlobalUserPreferences.replyLineAboveHeader;
|
compactReblogReplyLineItem.enabled=GlobalUserPreferences.replyLineAboveHeader;
|
||||||
|
items.add(new SwitchItem(R.string.sk_settings_hide_interaction, R.drawable.ic_fluent_eye_24_regular, GlobalUserPreferences.spectatorMode, i->{
|
||||||
|
GlobalUserPreferences.spectatorMode=i.checked;
|
||||||
|
GlobalUserPreferences.save();
|
||||||
|
needAppRestart=true;
|
||||||
|
}));
|
||||||
// items.add(new SwitchItem(R.string.sk_settings_translate_only_opened, R.drawable.ic_fluent_translate_24_regular, GlobalUserPreferences.translateButtonOpenedOnly, i->{
|
// items.add(new SwitchItem(R.string.sk_settings_translate_only_opened, R.drawable.ic_fluent_translate_24_regular, GlobalUserPreferences.translateButtonOpenedOnly, i->{
|
||||||
// GlobalUserPreferences.translateButtonOpenedOnly=i.checked;
|
// GlobalUserPreferences.translateButtonOpenedOnly=i.checked;
|
||||||
// GlobalUserPreferences.save();
|
// GlobalUserPreferences.save();
|
||||||
@@ -445,11 +460,11 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
|||||||
GlobalUserPreferences.recentEmojis.clear();
|
GlobalUserPreferences.recentEmojis.clear();
|
||||||
GlobalUserPreferences.save();
|
GlobalUserPreferences.save();
|
||||||
}));
|
}));
|
||||||
// items.add(new TextItem(R.string.log_out, this::confirmLogOut));
|
|
||||||
|
|
||||||
if(BuildConfig.DEBUG){
|
if(BuildConfig.DEBUG){
|
||||||
items.add(new RedHeaderItem("Debug options"));
|
items.add(new RedHeaderItem("Debug options"));
|
||||||
items.add(new TextItem("Test e-mail confirmation flow", ()->{
|
|
||||||
|
items.add(new TextItem("Test E-Mail confirmation flow", ()->{
|
||||||
AccountSession sess=AccountSessionManager.getInstance().getAccount(accountID);
|
AccountSession sess=AccountSessionManager.getInstance().getAccount(accountID);
|
||||||
sess.activated=false;
|
sess.activated=false;
|
||||||
sess.activationInfo=new AccountActivationInfo("test@email", System.currentTimeMillis());
|
sess.activationInfo=new AccountActivationInfo("test@email", System.currentTimeMillis());
|
||||||
@@ -458,9 +473,35 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
|||||||
args.putBoolean("debug", true);
|
args.putBoolean("debug", true);
|
||||||
Nav.goClearingStack(getActivity(), AccountActivationFragment.class, args);
|
Nav.goClearingStack(getActivity(), AccountActivationFragment.class, args);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
items.add(new TextItem("Copy preferences", ()->{
|
||||||
|
StringBuilder prefBuilder = new StringBuilder();
|
||||||
|
GlobalUserPreferences.load();
|
||||||
|
GlobalUserPreferences.getPrefs().getAll().forEach((key, value) -> prefBuilder.append(key).append(": ").append(value).append('\n'));
|
||||||
|
UiUtils.copyText(view, prefBuilder.toString());
|
||||||
|
}));
|
||||||
|
|
||||||
|
items.add(new TextItem("Reset preferences", ()->{
|
||||||
|
GlobalUserPreferences.load();
|
||||||
|
GlobalUserPreferences.getPrefs().edit().clear().commit();
|
||||||
|
UiUtils.restartApp();
|
||||||
|
}, R.drawable.ic_fluent_warning_24_regular));
|
||||||
|
|
||||||
|
items.add(new TextItem("Open App Info", () ->
|
||||||
|
getContext().startActivity(new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
|
||||||
|
.setData(Uri.fromParts("package", getContext().getPackageName(), null))),
|
||||||
|
R.drawable.ic_fluent_open_24_regular
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
items.add(new TextItem("Open developer settings",
|
||||||
|
()-> getContext().startActivity(new Intent(Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS)),
|
||||||
|
R.drawable.ic_fluent_open_24_regular)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
items.add(new FooterItem(getString(R.string.mo_settings_app_version, BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE)));
|
String version = getContext().getString(R.string.mo_settings_app_version, BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE);
|
||||||
|
items.add(new FooterItem(version, () -> UiUtils.copyText(view, version)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updatePublishText(Button btn) {
|
private void updatePublishText(Button btn) {
|
||||||
@@ -519,6 +560,7 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
|||||||
@Override
|
@Override
|
||||||
public void onViewCreated(View view, Bundle savedInstanceState){
|
public void onViewCreated(View view, Bundle savedInstanceState){
|
||||||
super.onViewCreated(view, savedInstanceState);
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
this.view = view;
|
||||||
if(GithubSelfUpdater.needSelfUpdating())
|
if(GithubSelfUpdater.needSelfUpdating())
|
||||||
E.register(this);
|
E.register(this);
|
||||||
}
|
}
|
||||||
@@ -887,6 +929,12 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
|||||||
this.onClick=onClick;
|
this.onClick=onClick;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TextItem(String text, Runnable onClick, @DrawableRes int icon){
|
||||||
|
this.text=text;
|
||||||
|
this.onClick=onClick;
|
||||||
|
this.icon=icon;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getViewType(){
|
public int getViewType(){
|
||||||
return 4;
|
return 4;
|
||||||
@@ -911,9 +959,11 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
|||||||
|
|
||||||
private class FooterItem extends Item{
|
private class FooterItem extends Item{
|
||||||
private String text;
|
private String text;
|
||||||
|
private Runnable onClick;
|
||||||
|
|
||||||
public FooterItem(String text){
|
public FooterItem(String text, Runnable onClick){
|
||||||
this.text=text;
|
this.text=text;
|
||||||
|
this.onClick=onClick;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -1192,7 +1242,7 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class FooterViewHolder extends BindableViewHolder<FooterItem>{
|
private class FooterViewHolder extends BindableViewHolder<FooterItem> implements UsableRecyclerView.Clickable{
|
||||||
private final TextView text;
|
private final TextView text;
|
||||||
public FooterViewHolder(){
|
public FooterViewHolder(){
|
||||||
super(getActivity(), R.layout.item_settings_footer, list);
|
super(getActivity(), R.layout.item_settings_footer, list);
|
||||||
@@ -1203,6 +1253,11 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
|||||||
public void onBind(FooterItem item){
|
public void onBind(FooterItem item){
|
||||||
text.setText(item.text);
|
text.setText(item.text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClick(){
|
||||||
|
item.onClick.run();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class UpdateViewHolder extends BindableViewHolder<UpdateItem>{
|
private class UpdateViewHolder extends BindableViewHolder<UpdateItem>{
|
||||||
|
|||||||
@@ -1,43 +1,47 @@
|
|||||||
package org.joinmastodon.android.fragments;
|
package org.joinmastodon.android.fragments;
|
||||||
|
|
||||||
import android.graphics.Canvas;
|
import android.graphics.drawable.ColorDrawable;
|
||||||
import android.graphics.Paint;
|
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.SpannableString;
|
|
||||||
import android.text.style.ReplacementSpan;
|
|
||||||
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.ViewTreeObserver;
|
import android.view.ViewTreeObserver;
|
||||||
import android.view.WindowInsets;
|
import android.view.WindowInsets;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.Button;
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
|
import org.joinmastodon.android.MastodonApp;
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
|
import org.joinmastodon.android.api.requests.instance.GetInstance;
|
||||||
import org.joinmastodon.android.fragments.onboarding.InstanceCatalogSignupFragment;
|
import org.joinmastodon.android.fragments.onboarding.InstanceCatalogSignupFragment;
|
||||||
import org.joinmastodon.android.fragments.onboarding.InstanceChooserLoginFragment;
|
import org.joinmastodon.android.fragments.onboarding.InstanceChooserLoginFragment;
|
||||||
|
import org.joinmastodon.android.fragments.onboarding.InstanceRulesFragment;
|
||||||
|
import org.joinmastodon.android.model.Instance;
|
||||||
|
import org.joinmastodon.android.ui.InterpolatingMotionEffect;
|
||||||
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
import org.joinmastodon.android.ui.views.SizeListenerFrameLayout;
|
import org.joinmastodon.android.ui.views.SizeListenerFrameLayout;
|
||||||
|
import org.parceler.Parcels;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
|
||||||
import androidx.viewpager2.widget.ViewPager2;
|
|
||||||
import me.grishka.appkit.Nav;
|
import me.grishka.appkit.Nav;
|
||||||
|
import me.grishka.appkit.api.Callback;
|
||||||
|
import me.grishka.appkit.api.ErrorResponse;
|
||||||
import me.grishka.appkit.fragments.AppKitFragment;
|
import me.grishka.appkit.fragments.AppKitFragment;
|
||||||
import me.grishka.appkit.utils.V;
|
import me.grishka.appkit.utils.V;
|
||||||
|
import me.grishka.appkit.views.BottomSheet;
|
||||||
|
|
||||||
public class SplashFragment extends AppKitFragment{
|
public class SplashFragment extends AppKitFragment{
|
||||||
|
|
||||||
|
private static final String DEFAULT_SERVER="mastodon.social";
|
||||||
|
|
||||||
private SizeListenerFrameLayout contentView;
|
private SizeListenerFrameLayout contentView;
|
||||||
private View artContainer, blueFill, greenFill;
|
private View artContainer, blueFill, greenFill;
|
||||||
private ViewPager2 pager;
|
private InterpolatingMotionEffect motionEffect;
|
||||||
private ViewGroup pagerDots;
|
|
||||||
private View artClouds, artPlaneElephant, artRightHill, artLeftHill, artCenterHill;
|
private View artClouds, artPlaneElephant, artRightHill, artLeftHill, artCenterHill;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState){
|
public void onCreate(Bundle savedInstanceState){
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
motionEffect=new InterpolatingMotionEffect(MastodonApp.context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@@ -46,44 +50,26 @@ public class SplashFragment extends AppKitFragment{
|
|||||||
contentView=(SizeListenerFrameLayout) inflater.inflate(R.layout.fragment_splash, container, false);
|
contentView=(SizeListenerFrameLayout) inflater.inflate(R.layout.fragment_splash, container, false);
|
||||||
contentView.findViewById(R.id.btn_get_started).setOnClickListener(this::onButtonClick);
|
contentView.findViewById(R.id.btn_get_started).setOnClickListener(this::onButtonClick);
|
||||||
contentView.findViewById(R.id.btn_log_in).setOnClickListener(this::onButtonClick);
|
contentView.findViewById(R.id.btn_log_in).setOnClickListener(this::onButtonClick);
|
||||||
|
Button joinDefault=contentView.findViewById(R.id.btn_join_default_server);
|
||||||
|
joinDefault.setText(getString(R.string.join_default_server, DEFAULT_SERVER));
|
||||||
|
joinDefault.setOnClickListener(this::onJoinDefaultServerClick);
|
||||||
|
contentView.findViewById(R.id.btn_learn_more).setOnClickListener(this::onLearnMoreClick);
|
||||||
|
|
||||||
artClouds=contentView.findViewById(R.id.art_clouds);
|
artClouds=contentView.findViewById(R.id.art_clouds);
|
||||||
artPlaneElephant=contentView.findViewById(R.id.art_plane_elephant);
|
artPlaneElephant=contentView.findViewById(R.id.art_plane_elephant);
|
||||||
artRightHill=contentView.findViewById(R.id.art_right_hill);
|
artRightHill=contentView.findViewById(R.id.art_right_hill);
|
||||||
artLeftHill=contentView.findViewById(R.id.art_left_hill);
|
artLeftHill=contentView.findViewById(R.id.art_left_hill);
|
||||||
artCenterHill=contentView.findViewById(R.id.art_center_hill);
|
artCenterHill=contentView.findViewById(R.id.art_center_hill);
|
||||||
pager=contentView.findViewById(R.id.pager);
|
|
||||||
pagerDots=contentView.findViewById(R.id.pager_dots);
|
|
||||||
pager.setAdapter(new PagerAdapter());
|
|
||||||
pager.setOffscreenPageLimit(3);
|
|
||||||
pager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback(){
|
|
||||||
@Override
|
|
||||||
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels){
|
|
||||||
for(int i=0;i<pagerDots.getChildCount();i++){
|
|
||||||
float alpha;
|
|
||||||
if(i==position){
|
|
||||||
alpha=0.3f+0.7f*(1f-positionOffset);
|
|
||||||
}else if(i==position+1){
|
|
||||||
alpha=0.3f+0.7f*positionOffset;
|
|
||||||
}else{
|
|
||||||
alpha=0.3f;
|
|
||||||
}
|
|
||||||
pagerDots.getChildAt(i).setAlpha(alpha);
|
|
||||||
}
|
|
||||||
|
|
||||||
float parallaxProgress=(position+positionOffset)/2f;
|
|
||||||
artClouds.setTranslationX(V.dp(-27)*(position>=1 ? 1f : positionOffset));
|
|
||||||
artPlaneElephant.setTranslationX(V.dp(101.55f)*parallaxProgress);
|
|
||||||
artLeftHill.setTranslationX(V.dp(-88)*parallaxProgress);
|
|
||||||
artLeftHill.setTranslationY(V.dp(24)*parallaxProgress);
|
|
||||||
artRightHill.setTranslationX(V.dp(-88)*parallaxProgress);
|
|
||||||
artRightHill.setTranslationY(V.dp(-24)*parallaxProgress);
|
|
||||||
artCenterHill.setTranslationX(V.dp(-40)*parallaxProgress);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
artContainer=contentView.findViewById(R.id.art_container);
|
artContainer=contentView.findViewById(R.id.art_container);
|
||||||
blueFill=contentView.findViewById(R.id.blue_fill);
|
blueFill=contentView.findViewById(R.id.blue_fill);
|
||||||
greenFill=contentView.findViewById(R.id.green_fill);
|
greenFill=contentView.findViewById(R.id.green_fill);
|
||||||
|
motionEffect.addViewEffect(new InterpolatingMotionEffect.ViewEffect(artClouds, V.dp(-5), V.dp(5), V.dp(-5), V.dp(5)));
|
||||||
|
motionEffect.addViewEffect(new InterpolatingMotionEffect.ViewEffect(artRightHill, V.dp(-15), V.dp(25), V.dp(-10), V.dp(10)));
|
||||||
|
motionEffect.addViewEffect(new InterpolatingMotionEffect.ViewEffect(artLeftHill, V.dp(-25), V.dp(15), V.dp(-15), V.dp(15)));
|
||||||
|
motionEffect.addViewEffect(new InterpolatingMotionEffect.ViewEffect(artCenterHill, V.dp(-14), V.dp(14), V.dp(-5), V.dp(25)));
|
||||||
|
motionEffect.addViewEffect(new InterpolatingMotionEffect.ViewEffect(artPlaneElephant, V.dp(-20), V.dp(12), V.dp(-20), V.dp(12)));
|
||||||
|
artContainer.setOnTouchListener(motionEffect);
|
||||||
|
|
||||||
contentView.setSizeListener(new SizeListenerFrameLayout.OnSizeChangedListener(){
|
contentView.setSizeListener(new SizeListenerFrameLayout.OnSizeChangedListener(){
|
||||||
@Override
|
@Override
|
||||||
@@ -109,6 +95,38 @@ public class SplashFragment extends AppKitFragment{
|
|||||||
Nav.go(getActivity(), isSignup ? InstanceCatalogSignupFragment.class : InstanceChooserLoginFragment.class, extras);
|
Nav.go(getActivity(), isSignup ? InstanceCatalogSignupFragment.class : InstanceChooserLoginFragment.class, extras);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void onJoinDefaultServerClick(View v){
|
||||||
|
new GetInstance()
|
||||||
|
.setCallback(new Callback<>(){
|
||||||
|
@Override
|
||||||
|
public void onSuccess(Instance result){
|
||||||
|
if(getActivity()==null)
|
||||||
|
return;
|
||||||
|
Bundle args=new Bundle();
|
||||||
|
args.putParcelable("instance", Parcels.wrap(result));
|
||||||
|
Nav.go(getActivity(), InstanceRulesFragment.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(ErrorResponse error){
|
||||||
|
if(getActivity()==null)
|
||||||
|
return;
|
||||||
|
error.showToast(getActivity());
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.wrapProgress(getActivity(), R.string.loading_instance, true)
|
||||||
|
.execNoAuth(DEFAULT_SERVER);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onLearnMoreClick(View v){
|
||||||
|
View sheetView=getActivity().getLayoutInflater().inflate(R.layout.intro_bottom_sheet, null);
|
||||||
|
BottomSheet sheet=new BottomSheet(getActivity());
|
||||||
|
sheet.setContentView(sheetView);
|
||||||
|
sheet.setNavigationBarBackground(new ColorDrawable(UiUtils.alphaBlendColors(UiUtils.getThemeColor(getActivity(), R.attr.colorM3Surface),
|
||||||
|
UiUtils.getThemeColor(getActivity(), R.attr.colorM3Primary), 0.05f)), !UiUtils.isDarkTheme());
|
||||||
|
sheet.show();
|
||||||
|
}
|
||||||
|
|
||||||
private void updateArtSize(int w, int h){
|
private void updateArtSize(int w, int h){
|
||||||
float scale=w/(float)V.dp(360);
|
float scale=w/(float)V.dp(360);
|
||||||
artContainer.setScaleX(scale);
|
artContainer.setScaleX(scale);
|
||||||
@@ -139,60 +157,15 @@ public class SplashFragment extends AppKitFragment{
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class PagerAdapter extends RecyclerView.Adapter<PagerViewHolder>{
|
@Override
|
||||||
|
protected void onShown(){
|
||||||
@NonNull
|
super.onShown();
|
||||||
@Override
|
motionEffect.activate();
|
||||||
public PagerViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
|
|
||||||
return new PagerViewHolder(viewType);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBindViewHolder(@NonNull PagerViewHolder holder, int position){}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getItemCount(){
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getItemViewType(int position){
|
|
||||||
return position;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class PagerViewHolder extends RecyclerView.ViewHolder{
|
@Override
|
||||||
public PagerViewHolder(int page){
|
protected void onHidden(){
|
||||||
super(new LinearLayout(getActivity()));
|
super.onHidden();
|
||||||
LinearLayout ll=(LinearLayout) itemView;
|
motionEffect.deactivate();
|
||||||
ll.setOrientation(LinearLayout.VERTICAL);
|
|
||||||
int pad=V.dp(16);
|
|
||||||
ll.setPadding(pad, pad, pad, pad);
|
|
||||||
ll.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
|
||||||
|
|
||||||
TextView title=new TextView(getActivity());
|
|
||||||
title.setTextAppearance(R.style.m3_headline_medium);
|
|
||||||
title.setText(switch(page){
|
|
||||||
case 0 -> getString(R.string.welcome_page1_title);
|
|
||||||
case 1 -> getString(R.string.welcome_page2_title);
|
|
||||||
case 2 -> getString(R.string.welcome_page3_title);
|
|
||||||
default -> throw new IllegalStateException("Unexpected value: "+page);
|
|
||||||
});
|
|
||||||
title.setTextColor(0xFF17063B);
|
|
||||||
LinearLayout.LayoutParams lp=new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, V.dp(page==0 ? 46 : 36));
|
|
||||||
lp.bottomMargin=V.dp(page==0 ? 4 : 14);
|
|
||||||
ll.addView(title, lp);
|
|
||||||
|
|
||||||
TextView text=new TextView(getActivity());
|
|
||||||
text.setTextAppearance(R.style.m3_body_medium);
|
|
||||||
text.setText(switch(page){
|
|
||||||
case 0 -> R.string.welcome_page1_text;
|
|
||||||
case 1 -> R.string.welcome_page2_text;
|
|
||||||
case 2 -> R.string.welcome_page3_text;
|
|
||||||
default -> throw new IllegalStateException("Unexpected value: "+page);
|
|
||||||
});
|
|
||||||
text.setTextColor(0xFF17063B);
|
|
||||||
ll.addView(text, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,7 +79,10 @@ public abstract class BaseAccountListFragment extends RecyclerFragment<BaseAccou
|
|||||||
if(refreshing){
|
if(refreshing){
|
||||||
relationships.clear();
|
relationships.clear();
|
||||||
}
|
}
|
||||||
loadRelationships(d);
|
if(!d.isEmpty() && !d.get(0).account.reloadWhenClicked){
|
||||||
|
loadRelationships(d);
|
||||||
|
}
|
||||||
|
|
||||||
super.onDataLoaded(d, more);
|
super.onDataLoaded(d, more);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -242,7 +245,12 @@ public abstract class BaseAccountListFragment extends RecyclerFragment<BaseAccou
|
|||||||
public void bindRelationship(){
|
public void bindRelationship(){
|
||||||
Relationship rel=relationships.get(item.account.id);
|
Relationship rel=relationships.get(item.account.id);
|
||||||
if(rel==null || AccountSessionManager.getInstance().isSelf(accountID, item.account)){
|
if(rel==null || AccountSessionManager.getInstance().isSelf(accountID, item.account)){
|
||||||
button.setVisibility(View.GONE);
|
if(item.account.reloadWhenClicked){
|
||||||
|
button.setVisibility(View.VISIBLE);
|
||||||
|
button.setText(R.string.button_follow);
|
||||||
|
} else {
|
||||||
|
button.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
}else{
|
}else{
|
||||||
button.setVisibility(View.VISIBLE);
|
button.setVisibility(View.VISIBLE);
|
||||||
UiUtils.setRelationshipToActionButton(rel, button);
|
UiUtils.setRelationshipToActionButton(rel, button);
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ public abstract class PaginatedAccountListFragment extends BaseAccountListFragme
|
|||||||
|
|
||||||
protected Account targetAccount;
|
protected Account targetAccount;
|
||||||
|
|
||||||
|
protected Account remoteAccount;
|
||||||
|
|
||||||
public abstract HeaderPaginationRequest<Account> onCreateRequest(String maxID, int count);
|
public abstract HeaderPaginationRequest<Account> onCreateRequest(String maxID, int count);
|
||||||
|
|
||||||
public abstract HeaderPaginationRequest<Account> onCreateRemoteRequest(String id, String maxID, int count);
|
public abstract HeaderPaginationRequest<Account> onCreateRemoteRequest(String id, String maxID, int count);
|
||||||
@@ -27,13 +29,18 @@ public abstract class PaginatedAccountListFragment extends BaseAccountListFragme
|
|||||||
@Override
|
@Override
|
||||||
protected void doLoadData(int offset, int count){
|
protected void doLoadData(int offset, int count){
|
||||||
if (shouldLoadRemote()) {
|
if (shouldLoadRemote()) {
|
||||||
|
if(remoteAccount == null){
|
||||||
UiUtils.lookupRemoteAccount(getContext(), targetAccount, accountID, null, account -> {
|
UiUtils.lookupRemoteAccount(getContext(), targetAccount, accountID, null, account -> {
|
||||||
if(account != null){
|
remoteAccount = account;
|
||||||
loadRemoteFollower(offset, count, account);
|
if(remoteAccount != null){
|
||||||
|
loadRemoteFollower(offset, count, remoteAccount);
|
||||||
} else {
|
} else {
|
||||||
loadFollower(offset, count);
|
loadFollower(offset, count);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
loadRemoteFollower(offset, count, remoteAccount);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
loadFollower(offset, count);
|
loadFollower(offset, count);
|
||||||
}
|
}
|
||||||
@@ -79,7 +86,11 @@ public abstract class PaginatedAccountListFragment extends BaseAccountListFragme
|
|||||||
remoteAccount.acct = remoteAccount.username;
|
remoteAccount.acct = remoteAccount.username;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
onDataLoaded(result.stream().map(AccountItem::new).collect(Collectors.toList()), nextMaxID!=null);
|
if(!result.isEmpty()){
|
||||||
|
onDataLoaded(result.stream().map(AccountItem::new).collect(Collectors.toList()), nextMaxID!=null);
|
||||||
|
} else {
|
||||||
|
loadFollower(offset, count);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -223,11 +223,11 @@ public class DiscoverAccountsFragment extends BaseRecyclerFragment<DiscoverAccou
|
|||||||
actionProgress=findViewById(R.id.action_progress);
|
actionProgress=findViewById(R.id.action_progress);
|
||||||
actionWrap=findViewById(R.id.action_btn_wrap);
|
actionWrap=findViewById(R.id.action_btn_wrap);
|
||||||
|
|
||||||
itemView.setOutlineProvider(OutlineProviders.roundedRect(6));
|
itemView.setOutlineProvider(OutlineProviders.roundedRect(12));
|
||||||
itemView.setClipToOutline(true);
|
itemView.setClipToOutline(true);
|
||||||
avatar.setOutlineProvider(OutlineProviders.roundedRect(12));
|
avatar.setOutlineProvider(OutlineProviders.roundedRect(12));
|
||||||
avatar.setClipToOutline(true);
|
avatar.setClipToOutline(true);
|
||||||
cover.setOutlineProvider(OutlineProviders.roundedRect(3));
|
cover.setOutlineProvider(OutlineProviders.roundedRect(12));
|
||||||
cover.setClipToOutline(true);
|
cover.setClipToOutline(true);
|
||||||
actionButton.setOnClickListener(this::onActionButtonClick);
|
actionButton.setOnClickListener(this::onActionButtonClick);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ public class FederatedTimelineFragment extends StatusListFragment {
|
|||||||
private String maxID;
|
private String maxID;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean withComposeButton() {
|
protected boolean wantsComposeButton() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ public class LocalTimelineFragment extends StatusListFragment {
|
|||||||
private String maxID;
|
private String maxID;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean withComposeButton() {
|
protected boolean wantsComposeButton() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -77,12 +77,13 @@ public class OnboardingFollowSuggestionsFragment extends RecyclerFragment<Parsed
|
|||||||
public void onViewCreated(View view, Bundle savedInstanceState){
|
public void onViewCreated(View view, Bundle savedInstanceState){
|
||||||
super.onViewCreated(view, savedInstanceState);
|
super.onViewCreated(view, savedInstanceState);
|
||||||
buttonBar=view.findViewById(R.id.button_bar);
|
buttonBar=view.findViewById(R.id.button_bar);
|
||||||
setStatusBarColor(UiUtils.getThemeColor(getActivity(), R.attr.colorM3Background));
|
setStatusBarColor(UiUtils.getThemeColor(getActivity(), R.attr.colorBackgroundLight));
|
||||||
view.setBackgroundColor(UiUtils.getThemeColor(getActivity(), R.attr.colorM3Background));
|
getToolbar().setBackgroundResource(UiUtils.getThemeColorRes(getToolbarContext(), R.attr.colorBackgroundLight));
|
||||||
list.addOnScrollListener(onScrollListener=new ElevationOnScrollListener((FragmentRootLinearLayout) view, buttonBar, getToolbar()));
|
view.setBackgroundColor(UiUtils.getThemeColor(getActivity(), R.attr.colorWindowBackground));
|
||||||
|
// list.addOnScrollListener(onScrollListener=new ElevationOnScrollListener((FragmentRootLinearLayout) view, buttonBar, getToolbar()));
|
||||||
|
|
||||||
view.findViewById(R.id.btn_next).setOnClickListener(UiUtils.rateLimitedClickListener(this::onFollowAllClick));
|
view.findViewById(R.id.btn_next).setOnClickListener(UiUtils.rateLimitedClickListener(this::onFollowAllClick));
|
||||||
view.findViewById(R.id.btn_skip).setOnClickListener(UiUtils.rateLimitedClickListener(v->proceed()));
|
// view.findViewById(R.id.btn_skip).setOnClickListener(UiUtils.rateLimitedClickListener(v->proceed()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -266,7 +267,7 @@ public class OnboardingFollowSuggestionsFragment extends RecyclerFragment<Parsed
|
|||||||
private Relationship relationship;
|
private Relationship relationship;
|
||||||
|
|
||||||
public SuggestionViewHolder(){
|
public SuggestionViewHolder(){
|
||||||
super(getActivity(), R.layout.item_user_row_m3, list);
|
super(getActivity(), R.layout.item_user_row, list);
|
||||||
name=findViewById(R.id.name);
|
name=findViewById(R.id.name);
|
||||||
username=findViewById(R.id.username);
|
username=findViewById(R.id.username);
|
||||||
bio=findViewById(R.id.bio);
|
bio=findViewById(R.id.bio);
|
||||||
@@ -296,7 +297,7 @@ public class OnboardingFollowSuggestionsFragment extends RecyclerFragment<Parsed
|
|||||||
actionWrap.setVisibility(View.GONE);
|
actionWrap.setVisibility(View.GONE);
|
||||||
}else{
|
}else{
|
||||||
actionWrap.setVisibility(View.VISIBLE);
|
actionWrap.setVisibility(View.VISIBLE);
|
||||||
UiUtils.setRelationshipToActionButtonM3(relationship, actionButton);
|
UiUtils.setRelationshipToActionButton(relationship, actionButton);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ public class Notification extends BaseModel implements DisplayItemsParent{
|
|||||||
public Account account;
|
public Account account;
|
||||||
public Status status;
|
public Status status;
|
||||||
public Report report;
|
public Report report;
|
||||||
|
public String emoji;
|
||||||
|
public String emojiUrl;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void postprocess() throws ObjectValidationException{
|
public void postprocess() throws ObjectValidationException{
|
||||||
@@ -51,6 +53,10 @@ public class Notification extends BaseModel implements DisplayItemsParent{
|
|||||||
STATUS,
|
STATUS,
|
||||||
@SerializedName("update")
|
@SerializedName("update")
|
||||||
UPDATE,
|
UPDATE,
|
||||||
|
@SerializedName("reaction")
|
||||||
|
REACTION,
|
||||||
|
@SerializedName("pleroma:emoji_reaction")
|
||||||
|
PLEROMA_EMOJI_REACTION,
|
||||||
@SerializedName("admin.sign_up")
|
@SerializedName("admin.sign_up")
|
||||||
SIGN_UP,
|
SIGN_UP,
|
||||||
@SerializedName("admin.report")
|
@SerializedName("admin.report")
|
||||||
|
|||||||
@@ -6,4 +6,5 @@ public enum NotificationAction {
|
|||||||
UNBOOST,
|
UNBOOST,
|
||||||
BOOKMARK,
|
BOOKMARK,
|
||||||
REPLY,
|
REPLY,
|
||||||
|
FOLLOW_BACK
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package org.joinmastodon.android.model;
|
|||||||
import static org.joinmastodon.android.api.MastodonAPIController.gson;
|
import static org.joinmastodon.android.api.MastodonAPIController.gson;
|
||||||
import static org.joinmastodon.android.api.MastodonAPIController.gsonWithoutDeserializer;
|
import static org.joinmastodon.android.api.MastodonAPIController.gsonWithoutDeserializer;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import com.google.gson.JsonDeserializationContext;
|
import com.google.gson.JsonDeserializationContext;
|
||||||
import com.google.gson.JsonDeserializer;
|
import com.google.gson.JsonDeserializer;
|
||||||
import com.google.gson.JsonElement;
|
import com.google.gson.JsonElement;
|
||||||
@@ -60,6 +62,8 @@ public class Status extends BaseModel implements DisplayItemsParent, Searchable{
|
|||||||
public Card card;
|
public Card card;
|
||||||
public String language;
|
public String language;
|
||||||
public String text;
|
public String text;
|
||||||
|
@Nullable
|
||||||
|
public Account rebloggedBy;
|
||||||
public boolean localOnly;
|
public boolean localOnly;
|
||||||
|
|
||||||
public boolean favourited;
|
public boolean favourited;
|
||||||
|
|||||||
@@ -1,17 +1,25 @@
|
|||||||
package org.joinmastodon.android.ui;
|
package org.joinmastodon.android.ui;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.hardware.Sensor;
|
import android.hardware.Sensor;
|
||||||
import android.hardware.SensorEvent;
|
import android.hardware.SensorEvent;
|
||||||
import android.hardware.SensorEventListener;
|
import android.hardware.SensorEventListener;
|
||||||
import android.hardware.SensorManager;
|
import android.hardware.SensorManager;
|
||||||
|
import android.view.MotionEvent;
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
|
import android.view.animation.PathInterpolator;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
public class InterpolatingMotionEffect implements SensorEventListener{
|
import androidx.dynamicanimation.animation.DynamicAnimation;
|
||||||
|
import androidx.dynamicanimation.animation.FloatValueHolder;
|
||||||
|
import androidx.dynamicanimation.animation.SpringAnimation;
|
||||||
|
import androidx.dynamicanimation.animation.SpringForce;
|
||||||
|
|
||||||
|
public class InterpolatingMotionEffect implements SensorEventListener, View.OnTouchListener{
|
||||||
|
|
||||||
private SensorManager sm;
|
private SensorManager sm;
|
||||||
private WindowManager wm;
|
private WindowManager wm;
|
||||||
@@ -20,6 +28,34 @@ public class InterpolatingMotionEffect implements SensorEventListener{
|
|||||||
private Sensor accelerometer;
|
private Sensor accelerometer;
|
||||||
private boolean accelerometerEnabled;
|
private boolean accelerometerEnabled;
|
||||||
private ArrayList<ViewEffect> views=new ArrayList<>();
|
private ArrayList<ViewEffect> views=new ArrayList<>();
|
||||||
|
private float pitch, roll;
|
||||||
|
private float touchDownX, touchDownY, touchAddX, touchAddY, touchAddLastAnimX, touchAddLastAnimY;
|
||||||
|
private PathInterpolator touchInterpolator=new PathInterpolator(0.5f, 1f, 0.89f, 1f);
|
||||||
|
private SpringAnimation touchSpringX, touchSpringY;
|
||||||
|
private FloatValueHolder touchSpringXHolder=new FloatValueHolder(){
|
||||||
|
@Override
|
||||||
|
public float getValue(){
|
||||||
|
return touchAddX;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setValue(float value){
|
||||||
|
touchAddX=value;
|
||||||
|
updateEffects();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
private FloatValueHolder touchSpringYHolder=new FloatValueHolder(){
|
||||||
|
@Override
|
||||||
|
public float getValue(){
|
||||||
|
return touchAddY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setValue(float value){
|
||||||
|
touchAddY=value;
|
||||||
|
updateEffects();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
public InterpolatingMotionEffect(Context context){
|
public InterpolatingMotionEffect(Context context){
|
||||||
sm=context.getSystemService(SensorManager.class);
|
sm=context.getSystemService(SensorManager.class);
|
||||||
@@ -50,8 +86,8 @@ public class InterpolatingMotionEffect implements SensorEventListener{
|
|||||||
float z=event.values[2]/SensorManager.GRAVITY_EARTH;
|
float z=event.values[2]/SensorManager.GRAVITY_EARTH;
|
||||||
|
|
||||||
|
|
||||||
float pitch=(float) (Math.atan2(x, Math.sqrt(y*y+z*z))/Math.PI*2.0);
|
pitch=(float) (Math.atan2(x, Math.sqrt(y*y+z*z))/Math.PI*2.0);
|
||||||
float roll=(float) (Math.atan2(y, Math.sqrt(x*x+z*z))/Math.PI*2.0);
|
roll=(float) (Math.atan2(y, Math.sqrt(x*x+z*z))/Math.PI*2.0);
|
||||||
|
|
||||||
switch(rotation){
|
switch(rotation){
|
||||||
case Surface.ROTATION_0:
|
case Surface.ROTATION_0:
|
||||||
@@ -88,9 +124,7 @@ public class InterpolatingMotionEffect implements SensorEventListener{
|
|||||||
}else if(roll<-1f){
|
}else if(roll<-1f){
|
||||||
roll=-2f-roll;
|
roll=-2f-roll;
|
||||||
}
|
}
|
||||||
for(ViewEffect view:views){
|
updateEffects();
|
||||||
view.update(pitch, roll);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -110,6 +144,62 @@ public class InterpolatingMotionEffect implements SensorEventListener{
|
|||||||
views.clear();
|
views.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
|
@Override
|
||||||
|
public boolean onTouch(View v, MotionEvent ev){
|
||||||
|
switch(ev.getAction()){
|
||||||
|
case MotionEvent.ACTION_DOWN -> {
|
||||||
|
if(touchSpringX!=null){
|
||||||
|
touchAddLastAnimX=touchAddX;
|
||||||
|
touchSpringX.cancel();
|
||||||
|
touchSpringX=null;
|
||||||
|
}else{
|
||||||
|
touchAddLastAnimX=0;
|
||||||
|
}
|
||||||
|
if(touchSpringY!=null){
|
||||||
|
touchAddLastAnimY=touchAddY;
|
||||||
|
touchSpringY.cancel();
|
||||||
|
touchSpringY=null;
|
||||||
|
}else{
|
||||||
|
touchAddLastAnimY=0;
|
||||||
|
}
|
||||||
|
touchDownX=ev.getX();
|
||||||
|
touchDownY=ev.getY();
|
||||||
|
}
|
||||||
|
case MotionEvent.ACTION_MOVE -> {
|
||||||
|
touchAddX=touchInterpolator.getInterpolation(Math.min(1f, Math.abs((ev.getX()-touchDownX)/(v.getWidth()/2f))));
|
||||||
|
touchAddY=touchInterpolator.getInterpolation(Math.min(1f, Math.abs((ev.getY()-touchDownY)/(v.getHeight()/2f))));
|
||||||
|
if(ev.getX()>touchDownX)
|
||||||
|
touchAddX=-touchAddX;
|
||||||
|
if(ev.getY()<touchDownY)
|
||||||
|
touchAddY=-touchAddY;
|
||||||
|
touchAddX+=touchAddLastAnimX;
|
||||||
|
touchAddY+=touchAddLastAnimY;
|
||||||
|
updateEffects();
|
||||||
|
}
|
||||||
|
case MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
|
||||||
|
touchSpringX=new SpringAnimation(touchSpringXHolder, 0f);
|
||||||
|
touchSpringX.setMinimumVisibleChange(0.01f);
|
||||||
|
touchSpringX.getSpring().setStiffness(SpringForce.STIFFNESS_LOW).setDampingRatio(0.85f);
|
||||||
|
touchSpringX.addEndListener((animation, canceled, value, velocity)->touchSpringX=null);
|
||||||
|
touchSpringX.start();
|
||||||
|
touchSpringY=new SpringAnimation(touchSpringYHolder, 0f);
|
||||||
|
touchSpringY.setMinimumVisibleChange(0.01f);
|
||||||
|
touchSpringY.getSpring().setStiffness(SpringForce.STIFFNESS_LOW).setDampingRatio(0.85f);
|
||||||
|
touchSpringY.addEndListener((animation, canceled, value, velocity)->touchSpringY=null);
|
||||||
|
touchSpringY.start();
|
||||||
|
updateEffects();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateEffects(){
|
||||||
|
for(ViewEffect view:views){
|
||||||
|
view.update(Math.min(1f, Math.max(-1f, pitch+touchAddX)), Math.min(1f, Math.max(-1f, roll+touchAddY)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static class ViewEffect{
|
public static class ViewEffect{
|
||||||
private View view;
|
private View view;
|
||||||
private float minX, maxX, minY, maxY;
|
private float minX, maxX, minY, maxY;
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import org.joinmastodon.android.fragments.account_list.StatusFavoritesListFragme
|
|||||||
import org.joinmastodon.android.fragments.account_list.StatusReblogsListFragment;
|
import org.joinmastodon.android.fragments.account_list.StatusReblogsListFragment;
|
||||||
import org.joinmastodon.android.fragments.account_list.StatusRelatedAccountListFragment;
|
import org.joinmastodon.android.fragments.account_list.StatusRelatedAccountListFragment;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
|
import org.joinmastodon.android.model.StatusPrivacy;
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
import org.parceler.Parcels;
|
import org.parceler.Parcels;
|
||||||
|
|
||||||
@@ -72,7 +73,11 @@ public class ExtendedFooterStatusDisplayItem extends StatusDisplayItem{
|
|||||||
public void onBind(ExtendedFooterStatusDisplayItem item){
|
public void onBind(ExtendedFooterStatusDisplayItem item){
|
||||||
Status s=item.status;
|
Status s=item.status;
|
||||||
favorites.setText(context.getResources().getQuantityString(R.plurals.x_favorites, (int)(s.favouritesCount%1000), s.favouritesCount));
|
favorites.setText(context.getResources().getQuantityString(R.plurals.x_favorites, (int)(s.favouritesCount%1000), s.favouritesCount));
|
||||||
reblogs.setText(context.getResources().getQuantityString(R.plurals.x_reblogs, (int)(s.reblogsCount%1000), s.reblogsCount));
|
|
||||||
|
reblogs.setText(context.getResources().getQuantityString(R.plurals.x_reblogs, (int) (s.reblogsCount % 1000), s.reblogsCount));
|
||||||
|
if (s.visibility==StatusPrivacy.DIRECT)
|
||||||
|
reblogs.setVisibility(View.GONE);
|
||||||
|
|
||||||
if(s.editedAt!=null){
|
if(s.editedAt!=null){
|
||||||
editHistory.setVisibility(View.VISIBLE);
|
editHistory.setVisibility(View.VISIBLE);
|
||||||
editHistory.setText(UiUtils.formatRelativeTimestampAsMinutesAgo(itemView.getContext(), s.editedAt));
|
editHistory.setText(UiUtils.formatRelativeTimestampAsMinutesAgo(itemView.getContext(), s.editedAt));
|
||||||
|
|||||||
@@ -450,4 +450,4 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,12 +37,14 @@ public class HashtagStatusDisplayItem extends StatusDisplayItem{
|
|||||||
public void onBind(HashtagStatusDisplayItem _item){
|
public void onBind(HashtagStatusDisplayItem _item){
|
||||||
Hashtag item=_item.tag;
|
Hashtag item=_item.tag;
|
||||||
title.setText('#'+item.name);
|
title.setText('#'+item.name);
|
||||||
int numPeople=item.history.get(0).accounts;
|
int numPeople = 0;
|
||||||
if(item.history.size()>1)
|
if(item.history != null){
|
||||||
numPeople+=item.history.get(1).accounts;
|
numPeople=item.history.get(0).accounts;
|
||||||
|
if(item.history.size()>1)
|
||||||
|
numPeople+=item.history.get(1).accounts;
|
||||||
|
chart.setData(item.history);
|
||||||
|
}
|
||||||
subtitle.setText(_item.parentFragment.getResources().getQuantityString(R.plurals.x_people_talking, numPeople, numPeople));
|
subtitle.setText(_item.parentFragment.getResources().getQuantityString(R.plurals.x_people_talking, numPeople, numPeople));
|
||||||
chart.setData(item.history);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,13 +86,13 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
|
|||||||
public final Status status;
|
public final Status status;
|
||||||
private boolean hasVisibilityToggle;
|
private boolean hasVisibilityToggle;
|
||||||
boolean needBottomPadding;
|
boolean needBottomPadding;
|
||||||
private String extraText;
|
private CharSequence extraText;
|
||||||
private Notification notification;
|
private Notification notification;
|
||||||
private ScheduledStatus scheduledStatus;
|
private ScheduledStatus scheduledStatus;
|
||||||
private Announcement announcement;
|
private Announcement announcement;
|
||||||
private Consumer<String> consumeReadAnnouncement;
|
private Consumer<String> consumeReadAnnouncement;
|
||||||
|
|
||||||
public HeaderStatusDisplayItem(String parentID, Account user, Instant createdAt, BaseStatusListFragment parentFragment, String accountID, Status status, String extraText, Notification notification, ScheduledStatus scheduledStatus){
|
public HeaderStatusDisplayItem(String parentID, Account user, Instant createdAt, BaseStatusListFragment parentFragment, String accountID, Status status, CharSequence extraText, Notification notification, ScheduledStatus scheduledStatus){
|
||||||
super(parentID, parentFragment);
|
super(parentID, parentFragment);
|
||||||
user=scheduledStatus != null ? AccountSessionManager.getInstance().getAccount(accountID).self : user;
|
user=scheduledStatus != null ? AccountSessionManager.getInstance().getAccount(accountID).self : user;
|
||||||
this.user=user;
|
this.user=user;
|
||||||
@@ -117,6 +117,7 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.extraText=extraText;
|
this.extraText=extraText;
|
||||||
|
emojiHelper.addText(extraText);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static HeaderStatusDisplayItem fromAnnouncement(Announcement a, Status fakeStatus, Account instanceUser, BaseStatusListFragment parentFragment, String accountID, Consumer<String> consumeReadID) {
|
public static HeaderStatusDisplayItem fromAnnouncement(Announcement a, Status fakeStatus, Account instanceUser, BaseStatusListFragment parentFragment, String accountID, Consumer<String> consumeReadID) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package org.joinmastodon.android.ui.displayitems;
|
package org.joinmastodon.android.ui.displayitems;
|
||||||
|
|
||||||
import static org.joinmastodon.android.ui.utils.MediaAttachmentViewController.altWrapPadding;
|
import static org.joinmastodon.android.GlobalUserPreferences.*;
|
||||||
|
|
||||||
import android.animation.Animator;
|
import android.animation.Animator;
|
||||||
import android.animation.AnimatorListenerAdapter;
|
import android.animation.AnimatorListenerAdapter;
|
||||||
@@ -183,10 +183,11 @@ public class MediaGridStatusDisplayItem extends StatusDisplayItem{
|
|||||||
altTextIndex=index;
|
altTextIndex=index;
|
||||||
Attachment att=item.attachments.get(index);
|
Attachment att=item.attachments.get(index);
|
||||||
boolean hasAltText = !TextUtils.isEmpty(att.description);
|
boolean hasAltText = !TextUtils.isEmpty(att.description);
|
||||||
altTextButton.setVisibility(hasAltText && GlobalUserPreferences.showAltIndicator ? View.VISIBLE : View.GONE);
|
if ((hasAltText && !showAltIndicator) || (!hasAltText && !showNoAltIndicator)) return;
|
||||||
noAltTextButton.setVisibility(!hasAltText && GlobalUserPreferences.showNoAltIndicator ? View.VISIBLE : View.GONE);
|
altTextButton.setVisibility(hasAltText && showAltIndicator ? View.VISIBLE : View.GONE);
|
||||||
altText.setVisibility(hasAltText && GlobalUserPreferences.showAltIndicator ? View.VISIBLE : View.GONE);
|
noAltTextButton.setVisibility(!hasAltText && showNoAltIndicator ? View.VISIBLE : View.GONE);
|
||||||
noAltText.setVisibility(!hasAltText && GlobalUserPreferences.showNoAltIndicator ? View.VISIBLE : View.GONE);
|
altText.setVisibility(hasAltText && showAltIndicator ? View.VISIBLE : View.GONE);
|
||||||
|
noAltText.setVisibility(!hasAltText && showNoAltIndicator ? View.VISIBLE : View.GONE);
|
||||||
altText.setText(att.description);
|
altText.setText(att.description);
|
||||||
altTextWrapper.setVisibility(View.VISIBLE);
|
altTextWrapper.setVisibility(View.VISIBLE);
|
||||||
altTextWrapper.setBackgroundResource(hasAltText ? R.drawable.bg_image_alt_overlay : R.drawable.bg_image_no_alt_overlay);
|
altTextWrapper.setBackgroundResource(hasAltText ? R.drawable.bg_image_alt_overlay : R.drawable.bg_image_no_alt_overlay);
|
||||||
@@ -202,15 +203,16 @@ public class MediaGridStatusDisplayItem extends StatusDisplayItem{
|
|||||||
btnL-=loc[0];
|
btnL-=loc[0];
|
||||||
btnT-=loc[1];
|
btnT-=loc[1];
|
||||||
|
|
||||||
|
ViewGroup.MarginLayoutParams margins = (ViewGroup.MarginLayoutParams) altTextWrapper.getLayoutParams();
|
||||||
ArrayList<Animator> anims=new ArrayList<>();
|
ArrayList<Animator> anims=new ArrayList<>();
|
||||||
anims.add(ObjectAnimator.ofFloat(altTextButton, View.ALPHA, 1, 0));
|
anims.add(ObjectAnimator.ofFloat(altTextButton, View.ALPHA, 1, 0));
|
||||||
anims.add(ObjectAnimator.ofFloat(noAltTextButton, View.ALPHA, 1, 0));
|
anims.add(ObjectAnimator.ofFloat(noAltTextButton, View.ALPHA, 1, 0));
|
||||||
anims.add(ObjectAnimator.ofFloat(altTextScroller, View.ALPHA, 0, 1));
|
anims.add(ObjectAnimator.ofFloat(altTextScroller, View.ALPHA, 0, 1));
|
||||||
anims.add(ObjectAnimator.ofFloat(altTextClose, View.ALPHA, 0, 1));
|
anims.add(ObjectAnimator.ofFloat(altTextClose, View.ALPHA, 0, 1));
|
||||||
anims.add(ObjectAnimator.ofInt(altTextWrapper, "left", btnL+altWrapPadding[0], altTextWrapper.getLeft()));
|
anims.add(ObjectAnimator.ofInt(altTextWrapper, "left", btnL+margins.leftMargin, altTextWrapper.getLeft()));
|
||||||
anims.add(ObjectAnimator.ofInt(altTextWrapper, "top", btnT+altWrapPadding[1], altTextWrapper.getTop()));
|
anims.add(ObjectAnimator.ofInt(altTextWrapper, "top", btnT+margins.topMargin, altTextWrapper.getTop()));
|
||||||
anims.add(ObjectAnimator.ofInt(altTextWrapper, "right", btnL+v.getWidth()-altWrapPadding[2], altTextWrapper.getRight()));
|
anims.add(ObjectAnimator.ofInt(altTextWrapper, "right", btnL+v.getWidth()-margins.rightMargin, altTextWrapper.getRight()));
|
||||||
anims.add(ObjectAnimator.ofInt(altTextWrapper, "bottom", btnT+v.getHeight()-altWrapPadding[3], altTextWrapper.getBottom()));
|
anims.add(ObjectAnimator.ofInt(altTextWrapper, "bottom", btnT+v.getHeight()-margins.bottomMargin, altTextWrapper.getBottom()));
|
||||||
for(Animator a:anims)
|
for(Animator a:anims)
|
||||||
a.setDuration(300);
|
a.setDuration(300);
|
||||||
|
|
||||||
@@ -252,8 +254,7 @@ public class MediaGridStatusDisplayItem extends StatusDisplayItem{
|
|||||||
boolean hasAltText = !TextUtils.isEmpty(item.attachments.get(i).description);
|
boolean hasAltText = !TextUtils.isEmpty(item.attachments.get(i).description);
|
||||||
if(c.btnsWrap!=null
|
if(c.btnsWrap!=null
|
||||||
&& c.btnsWrap!=btn
|
&& c.btnsWrap!=btn
|
||||||
&& ((hasAltText && GlobalUserPreferences.showAltIndicator)
|
&& ((hasAltText && showAltIndicator) || (!hasAltText && showNoAltIndicator))
|
||||||
|| (!hasAltText && GlobalUserPreferences.showNoAltIndicator))
|
|
||||||
) c.btnsWrap.setVisibility(View.VISIBLE);
|
) c.btnsWrap.setVisibility(View.VISIBLE);
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
@@ -265,15 +266,16 @@ public class MediaGridStatusDisplayItem extends StatusDisplayItem{
|
|||||||
btnL-=loc[0];
|
btnL-=loc[0];
|
||||||
btnT-=loc[1];
|
btnT-=loc[1];
|
||||||
|
|
||||||
|
ViewGroup.MarginLayoutParams margins = (ViewGroup.MarginLayoutParams) altTextWrapper.getLayoutParams();
|
||||||
ArrayList<Animator> anims=new ArrayList<>();
|
ArrayList<Animator> anims=new ArrayList<>();
|
||||||
anims.add(ObjectAnimator.ofFloat(altTextButton, View.ALPHA, 1));
|
anims.add(ObjectAnimator.ofFloat(altTextButton, View.ALPHA, 1));
|
||||||
anims.add(ObjectAnimator.ofFloat(noAltTextButton, View.ALPHA, 1));
|
anims.add(ObjectAnimator.ofFloat(noAltTextButton, View.ALPHA, 1));
|
||||||
anims.add(ObjectAnimator.ofFloat(altTextScroller, View.ALPHA, 0));
|
anims.add(ObjectAnimator.ofFloat(altTextScroller, View.ALPHA, 0));
|
||||||
anims.add(ObjectAnimator.ofFloat(altTextClose, View.ALPHA, 0));
|
anims.add(ObjectAnimator.ofFloat(altTextClose, View.ALPHA, 0));
|
||||||
anims.add(ObjectAnimator.ofInt(altTextWrapper, "left", btnL+altWrapPadding[0]));
|
anims.add(ObjectAnimator.ofInt(altTextWrapper, "left", btnL+margins.leftMargin));
|
||||||
anims.add(ObjectAnimator.ofInt(altTextWrapper, "top", btnT+altWrapPadding[1]));
|
anims.add(ObjectAnimator.ofInt(altTextWrapper, "top", btnT+margins.topMargin));
|
||||||
anims.add(ObjectAnimator.ofInt(altTextWrapper, "right", btnL+btn.getWidth()-altWrapPadding[2]));
|
anims.add(ObjectAnimator.ofInt(altTextWrapper, "right", btnL+btn.getWidth()-margins.rightMargin));
|
||||||
anims.add(ObjectAnimator.ofInt(altTextWrapper, "bottom", btnT+btn.getHeight()-altWrapPadding[3]));
|
anims.add(ObjectAnimator.ofInt(altTextWrapper, "bottom", btnT+btn.getHeight()-margins.bottomMargin));
|
||||||
for(Animator a:anims)
|
for(Animator a:anims)
|
||||||
a.setDuration(300);
|
a.setDuration(300);
|
||||||
|
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ public abstract class StatusDisplayItem{
|
|||||||
.filter(f -> f.context.contains(filterContext)).collect(Collectors.toList());
|
.filter(f -> f.context.contains(filterContext)).collect(Collectors.toList());
|
||||||
StatusFilterPredicate filterPredicate = new StatusFilterPredicate(filters);
|
StatusFilterPredicate filterPredicate = new StatusFilterPredicate(filters);
|
||||||
|
|
||||||
if(!statusForContent.filterRevealed){
|
if(statusForContent != null && !statusForContent.filterRevealed){
|
||||||
statusForContent.filterRevealed = filterPredicate.testWithWarning(status);
|
statusForContent.filterRevealed = filterPredicate.testWithWarning(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,6 +132,7 @@ public abstract class StatusDisplayItem{
|
|||||||
|
|
||||||
if(status.reblog!=null){
|
if(status.reblog!=null){
|
||||||
boolean isOwnPost = AccountSessionManager.getInstance().isSelf(fragment.getAccountID(), status.account);
|
boolean isOwnPost = AccountSessionManager.getInstance().isSelf(fragment.getAccountID(), status.account);
|
||||||
|
statusForContent.rebloggedBy = status.account;
|
||||||
String fullText = fragment.getString(R.string.user_boosted, status.account.displayName);
|
String fullText = fragment.getString(R.string.user_boosted, status.account.displayName);
|
||||||
String text = GlobalUserPreferences.compactReblogReplyLine && replyLine != null ? status.account.displayName : fullText;
|
String text = GlobalUserPreferences.compactReblogReplyLine && replyLine != null ? status.account.displayName : fullText;
|
||||||
items.add(new ReblogOrReplyLineStatusDisplayItem(parentID, fragment, text, status.account.emojis, R.drawable.ic_fluent_arrow_repeat_all_20_filled, isOwnPost ? status.visibility : null, i->{
|
items.add(new ReblogOrReplyLineStatusDisplayItem(parentID, fragment, text, status.account.emojis, R.drawable.ic_fluent_arrow_repeat_all_20_filled, isOwnPost ? status.visibility : null, i->{
|
||||||
@@ -206,7 +207,6 @@ public abstract class StatusDisplayItem{
|
|||||||
items.add(new GapStatusDisplayItem(parentID, fragment));
|
items.add(new GapStatusDisplayItem(parentID, fragment));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int i=1;
|
int i=1;
|
||||||
for(StatusDisplayItem item:items){
|
for(StatusDisplayItem item:items){
|
||||||
item.inset=inset;
|
item.inset=inset;
|
||||||
|
|||||||
@@ -208,49 +208,13 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
|
|||||||
translateButton.animate().alpha(0.5f).setInterpolator(CubicBezierInterpolator.DEFAULT).setDuration(150).start();
|
translateButton.animate().alpha(0.5f).setInterpolator(CubicBezierInterpolator.DEFAULT).setDuration(150).start();
|
||||||
|
|
||||||
if(item.status.reloadWhenClicked){
|
if(item.status.reloadWhenClicked){
|
||||||
UiUtils.lookupStatus(item.parentFragment.getContext(), item.status, item.parentFragment.getAccountID(), null, status1 -> {
|
UiUtils.lookupStatus(item.parentFragment.getContext(),
|
||||||
new TranslateStatus(item.status.id).setCallback(new Callback<>() {
|
item.status,
|
||||||
@Override
|
item.parentFragment.getAccountID(),
|
||||||
public void onSuccess(TranslatedStatus translatedStatus) {
|
null,
|
||||||
item.status.translation = translatedStatus;
|
reloadedStatus -> loadTranslation(reloadedStatus.id));
|
||||||
item.setTranslationShown(true);
|
|
||||||
if (item.parentFragment.getActivity() == null) return;
|
|
||||||
translateProgress.setVisibility(View.GONE);
|
|
||||||
translateButton.setClickable(true);
|
|
||||||
translateButton.animate().alpha(1).setInterpolator(CubicBezierInterpolator.DEFAULT).setDuration(50).start();
|
|
||||||
rebind();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onError(ErrorResponse error) {
|
|
||||||
translateProgress.setVisibility(View.GONE);
|
|
||||||
translateButton.setClickable(true);
|
|
||||||
translateButton.animate().alpha(1).setInterpolator(CubicBezierInterpolator.DEFAULT).setDuration(50).start();
|
|
||||||
error.showToast(itemView.getContext());
|
|
||||||
}
|
|
||||||
}).exec(item.parentFragment.getAccountID());
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
new TranslateStatus(item.status.id).setCallback(new Callback<>() {
|
loadTranslation(item.status.id);
|
||||||
@Override
|
|
||||||
public void onSuccess(TranslatedStatus translatedStatus) {
|
|
||||||
item.status.translation = translatedStatus;
|
|
||||||
item.setTranslationShown(true);
|
|
||||||
if (item.parentFragment.getActivity() == null) return;
|
|
||||||
translateProgress.setVisibility(View.GONE);
|
|
||||||
translateButton.setClickable(true);
|
|
||||||
translateButton.animate().alpha(1).setInterpolator(CubicBezierInterpolator.DEFAULT).setDuration(50).start();
|
|
||||||
rebind();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onError(ErrorResponse error) {
|
|
||||||
translateProgress.setVisibility(View.GONE);
|
|
||||||
translateButton.setClickable(true);
|
|
||||||
translateButton.animate().alpha(1).setInterpolator(CubicBezierInterpolator.DEFAULT).setDuration(50).start();
|
|
||||||
error.showToast(itemView.getContext());
|
|
||||||
}
|
|
||||||
}).exec(item.parentFragment.getAccountID());
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
item.setTranslationShown(!item.translationShown);
|
item.setTranslationShown(!item.translationShown);
|
||||||
@@ -303,5 +267,29 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
|
|||||||
private CustomEmojiHelper getEmojiHelper(){
|
private CustomEmojiHelper getEmojiHelper(){
|
||||||
return item.spoilerEmojiHelper!=null && !item.status.spoilerRevealed ? item.spoilerEmojiHelper : item.emojiHelper;
|
return item.spoilerEmojiHelper!=null && !item.status.spoilerRevealed ? item.spoilerEmojiHelper : item.emojiHelper;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void loadTranslation(String statusId) {
|
||||||
|
new TranslateStatus(statusId).setCallback(new Callback<>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(TranslatedStatus translatedStatus) {
|
||||||
|
item.status.translation = translatedStatus;
|
||||||
|
item.setTranslationShown(true);
|
||||||
|
if (item.parentFragment.getActivity() == null) return;
|
||||||
|
translateProgress.setVisibility(View.GONE);
|
||||||
|
translateButton.setClickable(true);
|
||||||
|
translateButton.animate().alpha(1).setInterpolator(CubicBezierInterpolator.DEFAULT).setDuration(50).start();
|
||||||
|
rebind();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(ErrorResponse error) {
|
||||||
|
translateProgress.setVisibility(View.GONE);
|
||||||
|
translateButton.setClickable(true);
|
||||||
|
translateButton.animate().alpha(1).setInterpolator(CubicBezierInterpolator.DEFAULT).setDuration(50).start();
|
||||||
|
error.showToast(itemView.getContext());
|
||||||
|
}
|
||||||
|
}).exec(item.parentFragment.getAccountID());
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
package org.joinmastodon.android.ui.text;
|
package org.joinmastodon.android.ui.text;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
|
||||||
import android.text.TextPaint;
|
import android.text.TextPaint;
|
||||||
import android.text.style.CharacterStyle;
|
import android.text.style.CharacterStyle;
|
||||||
import android.util.Log;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
@@ -49,14 +47,7 @@ public class LinkSpan extends CharacterStyle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void onLongClick(View view) {
|
public void onLongClick(View view) {
|
||||||
if (getType() == Type.URL) {
|
UiUtils.copyText(view, getType() == Type.URL ? link : text);
|
||||||
Intent shareIntent = new Intent(Intent.ACTION_SEND)
|
|
||||||
.setType("text/plain")
|
|
||||||
.putExtra(Intent.EXTRA_TEXT, link);
|
|
||||||
view.getContext().startActivity(Intent.createChooser(shareIntent, null));
|
|
||||||
} else {
|
|
||||||
UiUtils.copyText(view, text);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getLink(){
|
public String getLink(){
|
||||||
|
|||||||
@@ -29,6 +29,16 @@ public class CustomEmojiHelper{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addText(CharSequence text) {
|
||||||
|
if(!(text instanceof Spanned))
|
||||||
|
return;
|
||||||
|
CustomEmojiSpan[] spans=((Spanned) text).getSpans(0, text.length(), CustomEmojiSpan.class);
|
||||||
|
for(List<CustomEmojiSpan> group:Arrays.stream(spans).collect(Collectors.groupingBy(s->s.emoji)).values()){
|
||||||
|
this.spans.add(group);
|
||||||
|
requests.add(group.get(0).createImageLoaderRequest());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public int getImageCount(){
|
public int getImageCount(){
|
||||||
return requests.size();
|
return requests.size();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ public class MediaAttachmentViewController{
|
|||||||
public final MediaGridStatusDisplayItem.GridItemType type;
|
public final MediaGridStatusDisplayItem.GridItemType type;
|
||||||
public final ImageView photo;
|
public final ImageView photo;
|
||||||
public final View altButton, noAltButton, btnsWrap;
|
public final View altButton, noAltButton, btnsWrap;
|
||||||
public static int[] altWrapPadding = null;
|
|
||||||
private BlurhashCrossfadeDrawable crossfadeDrawable=new BlurhashCrossfadeDrawable();
|
private BlurhashCrossfadeDrawable crossfadeDrawable=new BlurhashCrossfadeDrawable();
|
||||||
private final Context context;
|
private final Context context;
|
||||||
private boolean didClear;
|
private boolean didClear;
|
||||||
@@ -37,9 +36,6 @@ public class MediaAttachmentViewController{
|
|||||||
btnsWrap=view.findViewById(R.id.alt_badges);
|
btnsWrap=view.findViewById(R.id.alt_badges);
|
||||||
this.type=type;
|
this.type=type;
|
||||||
this.context=context;
|
this.context=context;
|
||||||
if (altWrapPadding == null) {
|
|
||||||
altWrapPadding = new int[] { btnsWrap.getPaddingLeft(), btnsWrap.getPaddingTop(), btnsWrap.getPaddingRight(), btnsWrap.getPaddingBottom() };
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void bind(Attachment attachment, Status status){
|
public void bind(Attachment attachment, Status status){
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ import org.joinmastodon.android.GlobalUserPreferences;
|
|||||||
import org.joinmastodon.android.MastodonApp;
|
import org.joinmastodon.android.MastodonApp;
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.StatusInteractionController;
|
import org.joinmastodon.android.api.StatusInteractionController;
|
||||||
|
import org.joinmastodon.android.api.requests.accounts.GetAccountByHandle;
|
||||||
import org.joinmastodon.android.api.requests.accounts.SetAccountBlocked;
|
import org.joinmastodon.android.api.requests.accounts.SetAccountBlocked;
|
||||||
import org.joinmastodon.android.api.requests.accounts.SetAccountFollowed;
|
import org.joinmastodon.android.api.requests.accounts.SetAccountFollowed;
|
||||||
import org.joinmastodon.android.api.requests.accounts.SetAccountMuted;
|
import org.joinmastodon.android.api.requests.accounts.SetAccountMuted;
|
||||||
@@ -758,6 +759,33 @@ public class UiUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void performAccountAction(Activity activity, Account account, String accountID, Relationship relationship, Button button, Consumer<Boolean> progressCallback, Consumer<Relationship> resultCallback) {
|
public static void performAccountAction(Activity activity, Account account, String accountID, Relationship relationship, Button button, Consumer<Boolean> progressCallback, Consumer<Relationship> resultCallback) {
|
||||||
|
if(relationship == null){
|
||||||
|
UiUtils.lookupAccount(button.getContext(), account, accountID, null, account1 -> {
|
||||||
|
if(account1 == null){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
progressCallback.accept(true);
|
||||||
|
new SetAccountFollowed(account1.id, true, true, false)
|
||||||
|
.setCallback(new Callback<>(){
|
||||||
|
@Override
|
||||||
|
public void onSuccess(Relationship result){
|
||||||
|
resultCallback.accept(result);
|
||||||
|
progressCallback.accept(false);
|
||||||
|
if(!result.following && !result.requested){
|
||||||
|
E.post(new RemoveAccountPostsEvent(accountID, account.id, true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(ErrorResponse error){
|
||||||
|
error.showToast(activity);
|
||||||
|
progressCallback.accept(false);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.exec(accountID);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (relationship.blocking) {
|
if (relationship.blocking) {
|
||||||
confirmToggleBlockUser(activity, accountID, account, true, resultCallback);
|
confirmToggleBlockUser(activity, accountID, account, true, resultCallback);
|
||||||
}else if(relationship.muting){
|
}else if(relationship.muting){
|
||||||
@@ -1123,9 +1151,9 @@ public class UiUtils {
|
|||||||
domain = matcher.group(1);
|
domain = matcher.group(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(domain == null){
|
// if(domain == null){
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
|
|
||||||
Pattern patternForQuery = Pattern.compile("https?:\\/\\/[^\\/]+\\/@(\\w+)");
|
Pattern patternForQuery = Pattern.compile("https?:\\/\\/[^\\/]+\\/@(\\w+)");
|
||||||
Matcher matcherForQuery = patternForQuery.matcher(query.getQuery());
|
Matcher matcherForQuery = patternForQuery.matcher(query.getQuery());
|
||||||
@@ -1135,11 +1163,41 @@ public class UiUtils {
|
|||||||
trimmedQuery = matcherForQuery.group(1);
|
trimmedQuery = matcherForQuery.group(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(trimmedQuery == null){
|
// if(trimmedQuery == null){
|
||||||
return;
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
if(query instanceof Account){
|
||||||
|
domain = ((Account) query).getDomain();
|
||||||
|
trimmedQuery = ((Account) query).username;
|
||||||
}
|
}
|
||||||
|
|
||||||
String finalDomain = domain;
|
String finalDomain = domain;
|
||||||
|
|
||||||
|
if(query instanceof Account){
|
||||||
|
new GetAccountByHandle(((Account) query).acct)
|
||||||
|
.setCallback(new Callback<Account>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(Account result) {
|
||||||
|
if(result != null){
|
||||||
|
resultConsumer.accept((T) result);
|
||||||
|
} else {
|
||||||
|
Toast.makeText(context, R.string.sk_resource_not_found, Toast.LENGTH_SHORT).show();
|
||||||
|
resultConsumer.accept(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(ErrorResponse error) {
|
||||||
|
error.showToast(context);
|
||||||
|
resultConsumer.accept(null);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.wrapProgress((Activity)context, R.string.loading, true,
|
||||||
|
d -> transformDialogForLookup(context, targetAccountID, null, d, finalDomain))
|
||||||
|
.execNoAuth(domain);
|
||||||
|
return;
|
||||||
|
}
|
||||||
new GetSearchResults(trimmedQuery, type, false).setCallback(new Callback<>() {
|
new GetSearchResults(trimmedQuery, type, false).setCallback(new Callback<>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(SearchResults results) {
|
public void onSuccess(SearchResults results) {
|
||||||
@@ -1154,6 +1212,7 @@ public class UiUtils {
|
|||||||
@Override
|
@Override
|
||||||
public void onError(ErrorResponse error) {
|
public void onError(ErrorResponse error) {
|
||||||
error.showToast(context);
|
error.showToast(context);
|
||||||
|
resultConsumer.accept(null);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.wrapProgress((Activity)context, R.string.loading, true,
|
.wrapProgress((Activity)context, R.string.loading, true,
|
||||||
|
|||||||
4
mastodon/src/main/res/color/m3_on_surface_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="?colorM3OnSurface" android:alpha="0.12"/>
|
||||||
|
</selector>
|
||||||
@@ -3,7 +3,13 @@
|
|||||||
<item>
|
<item>
|
||||||
<shape>
|
<shape>
|
||||||
<solid android:color="?colorWindowBackground"/>
|
<solid android:color="?colorWindowBackground"/>
|
||||||
<corners android:topLeftRadius="12dp" android:topRightRadius="12dp"/>
|
<corners android:topLeftRadius="28dp" android:topRightRadius="28dp"/>
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<shape android:tint="?colorWindowBackground">
|
||||||
|
<solid android:color="#0D000000"/>
|
||||||
|
<corners android:topLeftRadius="28dp" android:topRightRadius="28dp"/>
|
||||||
</shape>
|
</shape>
|
||||||
</item>
|
</item>
|
||||||
</layer-list>
|
</layer-list>
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<item android:gravity="center" android:width="36dp" android:height="4dp">
|
<item android:gravity="center" android:width="32dp" android:height="4dp">
|
||||||
<shape>
|
<shape android:tint="?colorM3Outline">
|
||||||
<solid android:color="?android:textColorSecondary"/>
|
<solid android:color="#66000000"/>
|
||||||
<corners android:radius="2dp"/>
|
<corners android:radius="2dp"/>
|
||||||
</shape>
|
</shape>
|
||||||
</item>
|
</item>
|
||||||
|
|||||||
25
mastodon/src/main/res/drawable/bg_button_m3_outlined.xml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ripple android:color="@color/m3_primary_overlay" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item>
|
||||||
|
<selector>
|
||||||
|
<item android:state_enabled="true">
|
||||||
|
<shape>
|
||||||
|
<stroke android:color="?colorM3Outline" android:width="1dp"/>
|
||||||
|
<corners android:radius="20dp"/>
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<shape>
|
||||||
|
<stroke android:color="@color/m3_on_surface_overlay" android:width="1dp"/>
|
||||||
|
<corners android:radius="20dp"/>
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
</selector>
|
||||||
|
</item>
|
||||||
|
<item android:id="@android:id/mask">
|
||||||
|
<shape>
|
||||||
|
<solid android:color="#000"/>
|
||||||
|
<corners android:radius="20dp"/>
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
</ripple>
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ripple android:color="#1effffff" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item>
|
||||||
|
<selector>
|
||||||
|
<item android:state_enabled="true">
|
||||||
|
<shape>
|
||||||
|
<stroke android:color="?colorM3Outline" android:width="1dp"/>
|
||||||
|
<corners android:radius="20dp"/>
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<shape>
|
||||||
|
<stroke android:color="@color/m3_on_surface_overlay" android:width="1dp"/>
|
||||||
|
<corners android:radius="20dp"/>
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
</selector>
|
||||||
|
</item>
|
||||||
|
<item android:id="@android:id/mask">
|
||||||
|
<shape>
|
||||||
|
<solid android:color="#000"/>
|
||||||
|
<corners android:radius="20dp"/>
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
</ripple>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ripple android:color="#1effffff" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:id="@android:id/mask">
|
||||||
|
<shape>
|
||||||
|
<solid android:color="#000"/>
|
||||||
|
<corners android:radius="20dp"/>
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
</ripple>
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<solid android:color="?colorPollVoted"/>
|
<solid android:color="?colorPollVoted"/>
|
||||||
<corners android:radius="4dp"/>
|
<corners android:radius="12dp"/>
|
||||||
</shape>
|
</shape>
|
||||||
@@ -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="M22,12c0,-5.523 -4.477,-10 -10,-10S2,6.477 2,12a9.96,9.96 0,0 0,1.115 4.592l-1.068,3.823a1.25,1.25 0,0 0,1.54 1.54l3.826,-1.067a9.96,9.96 0,0 0,5.368 1.082,6.518 6.518,0 0,1 -1.051,-1.474 8.449,8.449 0,0 1,-3.863 -1.066l-0.27,-0.15 -3.986,1.111 1.113,-3.984 -0.151,-0.27A8.458,8.458 0,0 1,3.5 12a8.5,8.5 0,0 1,16.996 -0.27c0.54,0.281 1.036,0.636 1.474,1.05 0.02,-0.257 0.03,-0.517 0.03,-0.78ZM12.837,16.472a2,2 0,0 0,1.441 -2.496l-0.198,-0.687a5.28,5.28 0,0 1,1.483 -0.912l0.499,0.524a2,2 0,0 0,2.899 0.001l0.493,-0.518a5.28,5.28 0,0 1,1.484 0.921l-0.186,0.631a2,2 0,0 0,1.45 2.51l0.539,0.13a5.732,5.732 0,0 1,0.006 1.808l-0.584,0.144a2,2 0,0 0,-1.44 2.496l0.197,0.686c-0.439,0.383 -0.939,0.693 -1.483,0.913l-0.498,-0.525a2,2 0,0 0,-2.9 0l-0.493,0.519a5.28,5.28 0,0 1,-1.484 -0.922l0.187,-0.631a2,2 0,0 0,-1.45 -2.51l-0.54,-0.13a5.718,5.718 0,0 1,-0.006 -1.808l0.584,-0.144ZM18.95,17.5c0,-0.828 -0.65,-1.5 -1.45,-1.5 -0.8,0 -1.45,0.672 -1.45,1.5S16.7,19 17.5,19c0.8,0 1.45,-0.672 1.45,-1.5Z"
|
||||||
|
android:fillColor="#212121"/>
|
||||||
|
</vector>
|
||||||
@@ -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="M18,2C15.791,2 14,3.791 14,6.001C14,8.21 15.791,10.001 18,10.001C18.826,10.001 19.588,9.76 20.222,9.335C20.451,9.181 20.762,9.243 20.915,9.472C21.069,9.702 21.008,10.012 20.778,10.166C19.98,10.701 19.025,11.001 18,11.001C15.238,11.001 13,8.762 13,6.001C13,3.239 15.238,1 18,1C20.762,1 23.001,3.239 23.001,6.001L23,6.01V6.751C23,7.717 22.217,8.501 21.25,8.501C20.648,8.501 20.117,8.196 19.802,7.733C19.348,8.206 18.708,8.501 18,8.501C16.62,8.501 15.5,7.381 15.5,6.001C15.5,4.62 16.62,3.501 18,3.501C18.563,3.501 19.083,3.687 19.5,4C19.501,3.724 19.724,3.501 20,3.501C20.277,3.501 20.5,3.724 20.5,4.001V6.751C20.5,7.165 20.836,7.501 21.25,7.501C21.665,7.501 22,7.165 22,6.751V5.995L22.001,5.987C21.993,3.784 20.205,2 18,2ZM16.5,6.001C16.5,6.829 17.172,7.501 18,7.501C18.829,7.501 19.5,6.829 19.5,6.001C19.5,5.172 18.829,4.501 18,4.501C17.172,4.501 16.5,5.172 16.5,6.001ZM22,14.75V10.473C21.555,10.871 21.05,11.204 20.5,11.457V14.75C20.5,15.717 19.716,16.5 18.75,16.5H12.514L7.5,20.251L7.499,16.5H5.25C4.284,16.5 3.5,15.717 3.5,14.75V6.251C3.5,5.284 4.284,4.501 5.25,4.501H12.189C12.326,3.968 12.534,3.465 12.803,3.001H5.25C3.455,3.001 2,4.456 2,6.251V14.75C2,16.545 3.455,18 5.25,18H5.999L6,20.75C6,21.02 6.087,21.283 6.249,21.499C6.662,22.052 7.446,22.165 7.999,21.751L13.012,18H18.75C20.545,18 22,16.545 22,14.75Z"
|
||||||
|
android:fillColor="#212121"/>
|
||||||
|
</vector>
|
||||||
27
mastodon/src/main/res/drawable/splash_logo.xml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:aapt="http://schemas.android.com/aapt"
|
||||||
|
android:width="134dp"
|
||||||
|
android:height="34dp"
|
||||||
|
android:viewportWidth="313"
|
||||||
|
android:viewportHeight="81">
|
||||||
|
<path
|
||||||
|
android:pathData="M72.95,18.45C71.82,9.95 64.5,3.24 55.85,1.95C54.38,1.73 48.85,0.93 36.02,0.93H35.92C23.09,0.93 20.34,1.73 18.87,1.95C10.44,3.22 2.76,9.23 0.88,17.84C-0,22.08 -0.1,26.78 0.07,31.09C0.31,37.27 0.36,43.43 0.91,49.6C1.29,53.69 1.95,57.74 2.9,61.73C4.68,69.11 11.86,75.25 18.9,77.75C26.43,80.35 34.53,80.79 42.29,79C43.14,78.79 43.98,78.56 44.82,78.29C46.71,77.68 48.92,77 50.55,75.81C50.57,75.8 50.59,75.77 50.6,75.75C50.61,75.72 50.62,75.7 50.62,75.66V69.7C50.62,69.7 50.62,69.65 50.6,69.62C50.6,69.6 50.57,69.58 50.55,69.56C50.53,69.55 50.5,69.54 50.48,69.53C50.45,69.53 50.43,69.53 50.41,69.53C45.43,70.73 40.33,71.34 35.23,71.33C26.43,71.33 24.06,67.09 23.39,65.34C22.85,63.82 22.5,62.22 22.36,60.61C22.36,60.59 22.36,60.57 22.37,60.54C22.37,60.52 22.39,60.49 22.42,60.48C22.44,60.47 22.46,60.46 22.49,60.44H22.57C27.46,61.64 32.48,62.25 37.51,62.25C38.72,62.25 39.92,62.25 41.14,62.21C46.19,62.06 51.52,61.81 56.51,60.82C56.63,60.8 56.76,60.77 56.87,60.75C64.72,59.21 72.19,54.42 72.95,42.27C72.97,41.79 73.04,37.25 73.04,36.76C73.04,35.07 73.58,24.79 72.96,18.48L72.95,18.45Z">
|
||||||
|
<aapt:attr name="android:fillColor">
|
||||||
|
<gradient
|
||||||
|
android:startX="36.62"
|
||||||
|
android:startY="0.93"
|
||||||
|
android:endX="36.62"
|
||||||
|
android:endY="80.07"
|
||||||
|
android:type="linear">
|
||||||
|
<item android:offset="0" android:color="#FF6364FF"/>
|
||||||
|
<item android:offset="1" android:color="#FF563ACC"/>
|
||||||
|
</gradient>
|
||||||
|
</aapt:attr>
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
android:pathData="M14.81,23.2C14.81,20.72 16.77,18.72 19.2,18.72C21.62,18.72 23.58,20.73 23.58,23.2C23.58,25.67 21.62,27.68 19.2,27.68C16.77,27.68 14.81,25.67 14.81,23.2Z"
|
||||||
|
android:fillColor="#fff"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M80.02,27.06V47.66H72.03V27.67C72.03,23.45 70.3,21.32 66.83,21.32C63,21.32 61.07,23.87 61.07,28.87V39.82H53.14V28.87C53.14,23.84 51.24,21.32 47.38,21.32C43.92,21.32 42.18,23.45 42.18,27.67V47.65H34.21V27.06C34.21,22.86 35.25,19.51 37.35,17.03C39.53,14.54 42.37,13.29 45.89,13.29C49.97,13.29 53.07,14.9 55.11,18.11L57.11,21.52L59.1,18.11C61.14,14.91 64.23,13.29 68.32,13.29C71.84,13.29 74.69,14.55 76.86,17.03C78.96,19.51 80.01,22.83 80.01,27.06H80.02ZM107.49,37.3C109.15,35.51 109.93,33.29 109.93,30.59C109.93,27.89 109.14,25.65 107.49,23.94C105.91,22.15 103.89,21.3 101.45,21.3C99.02,21.3 97.01,22.15 95.41,23.94C93.83,25.65 93.04,27.89 93.04,30.59C93.04,33.29 93.83,35.53 95.41,37.3C97,39 99.02,39.87 101.45,39.87C103.89,39.87 105.9,39.02 107.49,37.3ZM109.93,14.12H117.8V47.06H109.93V43.18C107.55,46.41 104.26,48 99.99,48C95.71,48 92.42,46.36 89.5,43C86.64,39.64 85.18,35.48 85.18,30.61C85.18,25.74 86.65,21.65 89.5,18.29C92.43,14.93 95.92,13.23 99.99,13.23C104.06,13.23 107.55,14.81 109.93,18.02V14.14V14.12ZM144.26,29.97C146.58,31.76 147.73,34.25 147.67,37.41C147.67,40.77 146.52,43.41 144.14,45.24C141.76,47.03 138.89,47.94 135.41,47.94C129.13,47.94 124.87,45.3 122.61,40.11L129.43,35.96C130.34,38.78 132.35,40.25 135.41,40.25C138.22,40.25 139.62,39.33 139.62,37.42C139.62,36.03 137.79,34.78 134.07,33.8C132.66,33.41 131.5,33.01 130.6,32.68C129.31,32.16 128.22,31.56 127.31,30.83C125.05,29.04 123.9,26.68 123.9,23.65C123.9,20.42 124.99,17.85 127.19,16C129.45,14.09 132.19,13.18 135.48,13.18C140.73,13.18 144.56,15.48 147.07,20.16L140.37,24.1C139.4,21.86 137.74,20.74 135.48,20.74C133.11,20.74 131.95,21.65 131.95,23.44C131.95,24.83 133.78,26.08 137.5,27.06C140.37,27.72 142.63,28.7 144.26,29.97H144.27H144.26ZM169.26,22.27H162.37V35.98C162.37,37.63 162.98,38.63 164.15,39.08C165,39.4 166.71,39.47 169.27,39.34V47.05C163.98,47.71 160.14,47.17 157.88,45.41C155.62,43.7 154.53,40.53 154.53,36V22.27H149.23V14.1H154.53V7.46L162.39,4.89V14.12H169.29V22.29H169.27L169.26,22.27ZM194.34,37.1C195.92,35.4 196.71,33.22 196.71,30.58C196.71,27.94 195.92,25.78 194.34,24.05C192.74,22.35 190.79,21.48 188.42,21.48C186.04,21.48 184.09,22.33 182.49,24.05C180.97,25.84 180.18,28 180.18,30.58C180.18,33.16 180.97,35.31 182.49,37.1C184.08,38.81 186.04,39.67 188.42,39.67C190.79,39.67 192.74,38.82 194.34,37.1ZM176.96,42.96C173.85,39.6 172.32,35.52 172.32,30.58C172.32,25.63 173.85,21.62 176.96,18.26C180.07,14.9 183.91,13.19 188.42,13.19C192.92,13.19 196.77,14.9 199.87,18.26C202.97,21.62 204.57,25.77 204.57,30.58C204.57,35.39 202.97,39.6 199.87,42.96C196.76,46.32 192.98,47.96 188.42,47.96C183.85,47.96 180.06,46.32 176.96,42.96ZM230.86,37.29C232.45,35.5 233.24,33.28 233.24,30.58C233.24,27.87 232.45,25.63 230.86,23.93C229.28,22.14 227.26,21.29 224.82,21.29C222.39,21.29 220.37,22.14 218.73,23.93C217.14,25.63 216.35,27.87 216.35,30.58C216.35,33.28 217.14,35.52 218.73,37.29C220.38,38.99 222.45,39.86 224.82,39.86C227.2,39.86 229.27,39 230.86,37.29ZM233.24,0.92H241.11V47.05H233.24V43.17C230.93,46.39 227.63,47.99 223.36,47.99C219.09,47.99 215.75,46.35 212.8,42.98C209.93,39.62 208.48,35.47 208.48,30.6C208.48,25.73 209.95,21.64 212.8,18.28C215.72,14.92 219.26,13.22 223.36,13.22C227.45,13.22 230.93,14.8 233.24,18.01V0.93V0.92ZM268.74,37.07C270.32,35.36 271.12,33.18 271.12,30.54C271.12,27.9 270.32,25.74 268.74,24.01C267.15,22.31 265.21,21.45 262.82,21.45C260.43,21.45 258.5,22.3 256.9,24.01C255.37,25.8 254.58,27.96 254.58,30.54C254.58,33.12 255.37,35.28 256.9,37.07C258.48,38.77 260.44,39.64 262.82,39.64C265.2,39.64 267.14,38.78 268.74,37.07ZM251.36,42.92C248.26,39.56 246.73,35.48 246.73,30.54C246.73,25.6 248.25,21.58 251.36,18.22C254.47,14.86 258.32,13.15 262.82,13.15C267.32,13.15 271.18,14.86 274.27,18.22C277.38,21.58 278.97,25.73 278.97,30.54C278.97,35.35 277.38,39.56 274.27,42.92C271.16,46.28 267.38,47.93 262.82,47.93C258.26,47.93 254.46,46.28 251.36,42.92ZM313,26.78V47.01H305.14V27.84C305.14,25.66 304.59,24.01 303.48,22.77C302.45,21.65 300.98,21.07 299.09,21.07C294.65,21.07 292.39,23.77 292.39,29.24V47.03H284.53V14.1H292.39V17.81C294.28,14.71 297.28,13.19 301.47,13.19C304.82,13.19 307.57,14.37 309.71,16.81C311.91,19.24 313,22.54 313,26.82"
|
||||||
|
android:fillColor="#fff"/>
|
||||||
|
</vector>
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:includeFontPadding="false"
|
android:includeFontPadding="false"
|
||||||
android:paddingHorizontal="5dp"
|
android:paddingHorizontal="5dp"
|
||||||
android:paddingVertical="1dp"
|
android:paddingVertical="2dp"
|
||||||
android:background="@drawable/bg_image_alt_overlay"
|
android:background="@drawable/bg_image_alt_overlay"
|
||||||
android:text="@string/sk_alt_button"/>
|
android:text="@string/sk_alt_button"/>
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:paddingTop="13dp"
|
|
||||||
android:paddingEnd="4dp"
|
android:paddingEnd="4dp"
|
||||||
android:paddingStart="16dp">
|
android:paddingStart="16dp">
|
||||||
|
|
||||||
@@ -11,6 +10,7 @@
|
|||||||
android:id="@+id/more"
|
android:id="@+id/more"
|
||||||
android:layout_width="36dp"
|
android:layout_width="36dp"
|
||||||
android:layout_height="36dp"
|
android:layout_height="36dp"
|
||||||
|
android:layout_marginTop="13dp"
|
||||||
android:layout_alignParentTop="true"
|
android:layout_alignParentTop="true"
|
||||||
android:layout_alignParentEnd="true"
|
android:layout_alignParentEnd="true"
|
||||||
android:background="?android:actionBarItemBackground"
|
android:background="?android:actionBarItemBackground"
|
||||||
@@ -23,6 +23,7 @@
|
|||||||
android:id="@+id/delete_notification"
|
android:id="@+id/delete_notification"
|
||||||
android:layout_width="36dp"
|
android:layout_width="36dp"
|
||||||
android:layout_height="36dp"
|
android:layout_height="36dp"
|
||||||
|
android:layout_marginTop="13dp"
|
||||||
android:layout_toStartOf="@id/more"
|
android:layout_toStartOf="@id/more"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
android:background="?android:actionBarItemBackground"
|
android:background="?android:actionBarItemBackground"
|
||||||
@@ -36,6 +37,7 @@
|
|||||||
android:id="@+id/visibility"
|
android:id="@+id/visibility"
|
||||||
android:layout_width="36dp"
|
android:layout_width="36dp"
|
||||||
android:layout_height="36dp"
|
android:layout_height="36dp"
|
||||||
|
android:layout_marginTop="13dp"
|
||||||
android:layout_toStartOf="@id/delete_notification"
|
android:layout_toStartOf="@id/delete_notification"
|
||||||
android:background="?android:actionBarItemBackground"
|
android:background="?android:actionBarItemBackground"
|
||||||
android:scaleType="center"
|
android:scaleType="center"
|
||||||
@@ -46,6 +48,7 @@
|
|||||||
android:id="@+id/collapse_btn"
|
android:id="@+id/collapse_btn"
|
||||||
android:layout_width="36dp"
|
android:layout_width="36dp"
|
||||||
android:layout_height="36dp"
|
android:layout_height="36dp"
|
||||||
|
android:layout_marginTop="13dp"
|
||||||
android:layout_toStartOf="@id/visibility"
|
android:layout_toStartOf="@id/visibility"
|
||||||
android:background="?android:actionBarItemBackground"
|
android:background="?android:actionBarItemBackground"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
@@ -70,6 +73,7 @@
|
|||||||
android:id="@+id/unread_indicator"
|
android:id="@+id/unread_indicator"
|
||||||
android:layout_width="36dp"
|
android:layout_width="36dp"
|
||||||
android:layout_height="36dp"
|
android:layout_height="36dp"
|
||||||
|
android:layout_marginTop="13dp"
|
||||||
android:layout_toStartOf="@id/collapse_btn"
|
android:layout_toStartOf="@id/collapse_btn"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
android:tint="?android:colorAccent"
|
android:tint="?android:colorAccent"
|
||||||
@@ -83,18 +87,16 @@
|
|||||||
android:layout_alignParentStart="true"
|
android:layout_alignParentStart="true"
|
||||||
android:layout_alignParentTop="true"
|
android:layout_alignParentTop="true"
|
||||||
android:layout_marginEnd="12dp"
|
android:layout_marginEnd="12dp"
|
||||||
android:layout_marginTop="3dp" />
|
android:layout_marginTop="16dp" />
|
||||||
|
|
||||||
<org.joinmastodon.android.ui.views.HeaderSubtitleLinearLayout
|
<org.joinmastodon.android.ui.views.HeaderSubtitleLinearLayout
|
||||||
android:id="@+id/name_wrap"
|
android:id="@+id/name_wrap"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginEnd="8dp"
|
android:layout_marginEnd="8dp"
|
||||||
android:layout_marginTop="3dp"
|
|
||||||
android:paddingTop="3sp"
|
|
||||||
android:layout_toStartOf="@id/unread_indicator"
|
android:layout_toStartOf="@id/unread_indicator"
|
||||||
android:layout_toEndOf="@id/avatar"
|
android:layout_toEndOf="@id/avatar"
|
||||||
android:minHeight="24sp">
|
android:layout_above="@+id/username_wrap">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/name"
|
android:id="@+id/name"
|
||||||
@@ -121,13 +123,15 @@
|
|||||||
</org.joinmastodon.android.ui.views.HeaderSubtitleLinearLayout>
|
</org.joinmastodon.android.ui.views.HeaderSubtitleLinearLayout>
|
||||||
|
|
||||||
<org.joinmastodon.android.ui.views.HeaderSubtitleLinearLayout
|
<org.joinmastodon.android.ui.views.HeaderSubtitleLinearLayout
|
||||||
|
android:id="@id/username_wrap"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_below="@id/name_wrap"
|
|
||||||
android:layout_marginEnd="8dp"
|
android:layout_marginEnd="8dp"
|
||||||
android:layout_toStartOf="@id/unread_indicator"
|
android:layout_toStartOf="@id/unread_indicator"
|
||||||
android:layout_toEndOf="@id/avatar"
|
android:layout_toEndOf="@id/avatar"
|
||||||
|
android:layout_alignBottom="@id/avatar"
|
||||||
android:layoutDirection="locale"
|
android:layoutDirection="locale"
|
||||||
|
android:paddingBottom="3sp"
|
||||||
android:orientation="horizontal">
|
android:orientation="horizontal">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
|
|||||||
@@ -73,7 +73,6 @@
|
|||||||
<RelativeLayout
|
<RelativeLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:paddingTop="16dp"
|
|
||||||
android:paddingRight="16dp"
|
android:paddingRight="16dp"
|
||||||
android:paddingLeft="16dp">
|
android:paddingLeft="16dp">
|
||||||
|
|
||||||
@@ -81,6 +80,7 @@
|
|||||||
android:id="@+id/self_avatar"
|
android:id="@+id/self_avatar"
|
||||||
android:layout_width="46sp"
|
android:layout_width="46sp"
|
||||||
android:layout_height="46sp"
|
android:layout_height="46sp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
android:layout_alignParentStart="true"
|
android:layout_alignParentStart="true"
|
||||||
android:layout_alignParentTop="true"
|
android:layout_alignParentTop="true"
|
||||||
android:layout_marginEnd="12dp" />
|
android:layout_marginEnd="12dp" />
|
||||||
@@ -90,8 +90,7 @@
|
|||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_toEndOf="@id/self_avatar"
|
android:layout_toEndOf="@id/self_avatar"
|
||||||
android:paddingTop="3sp"
|
android:layout_above="@+id/self_username"
|
||||||
android:minHeight="24sp"
|
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:textAppearance="@style/m3_title_medium"
|
android:textAppearance="@style/m3_title_medium"
|
||||||
@@ -103,7 +102,7 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="8sp"
|
android:layout_marginStart="8sp"
|
||||||
android:layout_toEndOf="@id/self_name"
|
android:layout_toEndOf="@id/self_name"
|
||||||
android:paddingTop="3sp"
|
android:layout_above="@id/self_username"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:fontFamily="sans-serif"
|
android:fontFamily="sans-serif"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
@@ -112,14 +111,15 @@
|
|||||||
tools:text="@string/sk_local_only" />
|
tools:text="@string/sk_local_only" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/self_username"
|
android:id="@id/self_username"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_below="@id/self_name"
|
|
||||||
android:layout_toEndOf="@id/self_avatar"
|
android:layout_toEndOf="@id/self_avatar"
|
||||||
|
android:layout_alignBottom="@id/self_avatar"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:textAppearance="@style/m3_title_small"
|
android:textAppearance="@style/m3_title_small"
|
||||||
|
android:paddingBottom="3sp"
|
||||||
tools:text="\@Gargron" />
|
tools:text="\@Gargron" />
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|||||||
@@ -36,29 +36,30 @@
|
|||||||
android:id="@+id/button_bar"
|
android:id="@+id/button_bar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="end"
|
android:background="?colorBackgroundLight"
|
||||||
android:background="@drawable/bg_onboarding_panel">
|
android:outlineProvider="bounds"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:elevation="0dp">
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
|
style="?primaryLargeButtonStyle"
|
||||||
android:id="@+id/btn_next"
|
android:id="@+id/btn_next"
|
||||||
android:layout_width="wrap_content"
|
android:minWidth="145dp"
|
||||||
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="16dp"
|
android:layout_margin="16dp"
|
||||||
android:layout_marginTop="8dp"
|
android:layout_weight="1"
|
||||||
android:layout_marginBottom="16dp"
|
android:text="@string/follow_all" />
|
||||||
style="@style/Widget.Mastodon.M3.Button.Tonal"
|
<!-- <Button-->
|
||||||
android:text="@string/follow_all"/>
|
<!-- android:id="@+id/btn_skip"-->
|
||||||
|
<!-- android:layout_width="wrap_content"-->
|
||||||
<Button
|
<!-- android:layout_height="wrap_content"-->
|
||||||
android:id="@+id/btn_skip"
|
<!-- android:layout_marginStart="16dp"-->
|
||||||
android:layout_width="wrap_content"
|
<!-- android:layout_marginEnd="16dp"-->
|
||||||
android:layout_height="wrap_content"
|
<!-- android:layout_marginTop="8dp"-->
|
||||||
android:layout_marginStart="16dp"
|
<!-- android:layout_marginBottom="16dp"-->
|
||||||
android:layout_marginEnd="16dp"
|
<!-- style="@style/Widget.Mastodon.M3.Button.Filled"-->
|
||||||
android:layout_marginTop="8dp"
|
<!-- android:text="@string/skip"/>-->
|
||||||
android:layout_marginBottom="16dp"
|
|
||||||
style="@style/Widget.Mastodon.M3.Button.Filled"
|
|
||||||
android:text="@string/skip"/>
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,8 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:fitsSystemWindows="true"
|
android:fitsSystemWindows="true"
|
||||||
android:clipToPadding="false">
|
android:clipToPadding="false"
|
||||||
|
android:theme="@style/Theme.Mastodon.Dark.SplashFragment">
|
||||||
|
|
||||||
<View
|
<View
|
||||||
android:id="@+id/blue_fill"
|
android:id="@+id/blue_fill"
|
||||||
@@ -26,7 +27,8 @@
|
|||||||
android:id="@+id/art_clouds"
|
android:id="@+id/art_clouds"
|
||||||
android:layout_width="414dp"
|
android:layout_width="414dp"
|
||||||
android:layout_height="541dp"
|
android:layout_height="541dp"
|
||||||
android:layout_marginTop="91dp"
|
android:layout_marginTop="37dp"
|
||||||
|
android:layout_marginLeft="-27dp"
|
||||||
android:layout_gravity="top|left"
|
android:layout_gravity="top|left"
|
||||||
android:alpha="0.3"
|
android:alpha="0.3"
|
||||||
android:importantForAccessibility="no"
|
android:importantForAccessibility="no"
|
||||||
@@ -36,8 +38,8 @@
|
|||||||
android:id="@+id/art_plane_elephant"
|
android:id="@+id/art_plane_elephant"
|
||||||
android:layout_width="245.64dp"
|
android:layout_width="245.64dp"
|
||||||
android:layout_height="72.65dp"
|
android:layout_height="72.65dp"
|
||||||
android:layout_marginLeft="-101.55dp"
|
android:layout_marginLeft="-50.775dp"
|
||||||
android:layout_marginTop="238.12dp"
|
android:layout_marginTop="184.12dp"
|
||||||
android:layout_gravity="left|top"
|
android:layout_gravity="left|top"
|
||||||
android:alpha="0.3"
|
android:alpha="0.3"
|
||||||
android:importantForAccessibility="no"
|
android:importantForAccessibility="no"
|
||||||
@@ -48,8 +50,8 @@
|
|||||||
android:layout_width="150.84dp"
|
android:layout_width="150.84dp"
|
||||||
android:layout_height="176.44dp"
|
android:layout_height="176.44dp"
|
||||||
android:layout_gravity="top|left"
|
android:layout_gravity="top|left"
|
||||||
android:layout_marginLeft="322dp"
|
android:layout_marginLeft="278dp"
|
||||||
android:layout_marginTop="310dp"
|
android:layout_marginTop="244dp"
|
||||||
android:importantForAccessibility="no"
|
android:importantForAccessibility="no"
|
||||||
android:src="@drawable/splash_art_layer1"/>
|
android:src="@drawable/splash_art_layer1"/>
|
||||||
|
|
||||||
@@ -58,7 +60,8 @@
|
|||||||
android:layout_width="197.2dp"
|
android:layout_width="197.2dp"
|
||||||
android:layout_height="153.61dp"
|
android:layout_height="153.61dp"
|
||||||
android:layout_gravity="top|left"
|
android:layout_gravity="top|left"
|
||||||
android:layout_marginTop="294dp"
|
android:layout_marginTop="252dp"
|
||||||
|
android:layout_marginLeft="-44dp"
|
||||||
android:importantForAccessibility="no"
|
android:importantForAccessibility="no"
|
||||||
android:src="@drawable/splash_art_layer2"/>
|
android:src="@drawable/splash_art_layer2"/>
|
||||||
|
|
||||||
@@ -67,7 +70,8 @@
|
|||||||
android:layout_width="400dp"
|
android:layout_width="400dp"
|
||||||
android:layout_height="346dp"
|
android:layout_height="346dp"
|
||||||
android:layout_gravity="top|left"
|
android:layout_gravity="top|left"
|
||||||
android:layout_marginTop="294dp"
|
android:layout_marginTop="240dp"
|
||||||
|
android:layout_marginLeft="-20dp"
|
||||||
android:importantForAccessibility="no"
|
android:importantForAccessibility="no"
|
||||||
android:src="@drawable/splash_art_layer3"/>
|
android:src="@drawable/splash_art_layer3"/>
|
||||||
|
|
||||||
@@ -86,61 +90,108 @@
|
|||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:clipToPadding="false"
|
android:clipToPadding="false"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="300dp"
|
||||||
|
android:layout_height="78dp"
|
||||||
|
android:layout_gravity="center_horizontal"
|
||||||
|
android:layout_marginTop="32dp"
|
||||||
|
android:importantForAccessibility="no"
|
||||||
|
android:src="@drawable/splash_logo"/>
|
||||||
|
|
||||||
<androidx.viewpager2.widget.ViewPager2
|
<Space
|
||||||
android:id="@+id/pager"
|
android:layout_width="1px"
|
||||||
android:layout_width="match_parent"
|
android:layout_height="0px"
|
||||||
android:layout_height="0dp"
|
|
||||||
android:layout_weight="1"/>
|
android:layout_weight="1"/>
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/pager_dots"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center_horizontal"
|
|
||||||
android:layout_marginBottom="16dp"
|
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<View
|
|
||||||
android:layout_width="8dp"
|
|
||||||
android:layout_height="8dp"/>
|
|
||||||
<!-- android:background="@drawable/white_circle"/>-->
|
|
||||||
|
|
||||||
<View
|
|
||||||
android:layout_width="8dp"
|
|
||||||
android:layout_height="8dp"
|
|
||||||
android:layout_marginLeft="16dp"
|
|
||||||
android:layout_marginRight="16dp"
|
|
||||||
android:alpha="0.3"/>
|
|
||||||
<!-- android:background="@drawable/white_circle"-->
|
|
||||||
|
|
||||||
|
|
||||||
<View
|
|
||||||
android:layout_width="8dp"
|
|
||||||
android:layout_height="8dp"
|
|
||||||
android:alpha="0.3"/>
|
|
||||||
<!-- android:background="@drawable/white_circle"-->
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/btn_log_in"
|
android:id="@+id/btn_join_default_server"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginLeft="16dp"
|
android:layout_marginLeft="16dp"
|
||||||
android:layout_marginRight="16dp"
|
android:layout_marginRight="16dp"
|
||||||
style="@style/Widget.Mastodon.M3.Button.Text"
|
style="@style/Widget.Mastodon.M3.Button.Filled"
|
||||||
android:textColor="#fff"
|
tools:text="@string/join_default_server"/>
|
||||||
android:text="@string/already_have_account"/>
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/btn_get_started"
|
android:id="@+id/btn_get_started"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginLeft="16dp"
|
android:layout_marginLeft="16dp"
|
||||||
android:layout_marginRight="16dp"
|
android:layout_marginRight="16dp"
|
||||||
android:layout_marginBottom="16dp"
|
android:layout_marginTop="8dp"
|
||||||
style="@style/Widget.Mastodon.M3.Button.Filled"
|
android:textColor="#FFF"
|
||||||
android:text="@string/get_started"/>
|
style="@style/Widget.Mastodon.M3.Button.Outlined"
|
||||||
|
android:background="@drawable/bg_button_m3_outlined_white"
|
||||||
|
android:text="@string/pick_server"/>
|
||||||
|
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginLeft="16dp"
|
||||||
|
android:layout_marginRight="16dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:background="#B2FFFFFF"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="20dp"
|
||||||
|
android:layout_marginLeft="8dp"
|
||||||
|
android:layout_marginRight="8dp"
|
||||||
|
android:textAllCaps="true"
|
||||||
|
android:textAppearance="@style/m3_label_large"
|
||||||
|
android:textColor="#B2FFFFFF"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:text="@string/signup_or_login"/>
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:background="#B2FFFFFF"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_marginLeft="8dp"
|
||||||
|
android:layout_marginRight="8dp">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/btn_learn_more"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="8dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:textColor="#FFF"
|
||||||
|
style="@style/Widget.Mastodon.M3.Button.Text"
|
||||||
|
android:background="@drawable/bg_button_m3_text_white"
|
||||||
|
android:text="@string/learn_more"/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/btn_log_in"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="8dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:textColor="#FFF"
|
||||||
|
style="@style/Widget.Mastodon.M3.Button.Text"
|
||||||
|
android:background="@drawable/bg_button_m3_text_white"
|
||||||
|
android:text="@string/log_in"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</org.joinmastodon.android.ui.views.SizeListenerFrameLayout>
|
</org.joinmastodon.android.ui.views.SizeListenerFrameLayout>
|
||||||
50
mastodon/src/main/res/layout/intro_bottom_sheet.xml
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@drawable/bg_bottom_sheet"
|
||||||
|
android:padding="16dp">
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="4dp"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:background="@drawable/bg_bottom_sheet_handle"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAppearance="@style/m3_headline_medium"
|
||||||
|
android:textColor="?colorM3OnSurface"
|
||||||
|
android:text="@string/welcome_to_mastodon"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:textAppearance="@style/m3_body_large"
|
||||||
|
android:textColor="?colorM3OnSurface"
|
||||||
|
android:text="@string/welcome_paragraph1"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:textAppearance="@style/m3_headline_medium"
|
||||||
|
android:textColor="?colorM3OnSurface"
|
||||||
|
android:text="@string/what_are_servers"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:textAppearance="@style/m3_body_large"
|
||||||
|
android:textColor="?colorM3OnSurface"
|
||||||
|
android:text="@string/welcome_paragraph2"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</ScrollView>
|
||||||
89
mastodon/src/main/res/layout/item_user_row.xml
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
<?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">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/avatar"
|
||||||
|
android:layout_width="40dp"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:layout_alignParentStart="true"
|
||||||
|
android:layout_alignParentTop="true"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:layout_marginBottom="12dp"
|
||||||
|
android:importantForAccessibility="no"
|
||||||
|
tools:src="#0f0"/>
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/action_btn_wrap"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:layout_alignParentTop="true"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:layout_marginTop="18dp"
|
||||||
|
android:layout_marginStart="-8dp">
|
||||||
|
|
||||||
|
<org.joinmastodon.android.ui.views.ProgressBarButton
|
||||||
|
android:id="@+id/action_btn"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:singleLine="true"
|
||||||
|
tools:text="@string/follow_back"/>
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/action_progress"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:indeterminate="true"
|
||||||
|
style="?android:progressBarStyleSmall"
|
||||||
|
android:elevation="10dp"
|
||||||
|
android:outlineProvider="none"
|
||||||
|
android:visibility="gone"/>
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/name"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_toEndOf="@id/avatar"
|
||||||
|
android:layout_toStartOf="@id/action_btn_wrap"
|
||||||
|
android:layout_alignParentTop="true"
|
||||||
|
android:layout_marginTop="14dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:textAppearance="@style/m3_title_medium"
|
||||||
|
tools:text="User Name"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/username"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="20dp"
|
||||||
|
android:layout_toEndOf="@id/avatar"
|
||||||
|
android:layout_below="@id/name"
|
||||||
|
android:layout_toStartOf="@id/action_btn_wrap"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:textAppearance="@style/m3_title_small"
|
||||||
|
tools:text="\@username@server.social"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/bio"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@id/avatar"
|
||||||
|
android:layout_marginLeft="16dp"
|
||||||
|
android:layout_marginRight="16dp"
|
||||||
|
android:layout_marginBottom="12dp"
|
||||||
|
android:textAppearance="@style/m3_body_medium"
|
||||||
|
tools:text="Description"/>
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||