Compare commits
344 Commits
v2.0.1+for
...
revert-804
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f6ea0404ef | ||
|
|
8cd55fc365 | ||
|
|
f14df2bb0f | ||
|
|
53369eb2d4 | ||
|
|
807010893a | ||
|
|
ea01b14ffb | ||
|
|
3fd9dc1dcd | ||
|
|
02a4a77885 | ||
|
|
651090a504 | ||
|
|
203254c9f4 | ||
|
|
16ef577a7a | ||
|
|
734b3bced6 | ||
|
|
e26c641dc7 | ||
|
|
9295cf4e9c | ||
|
|
dd9237e9ca | ||
|
|
ea81c1fad6 | ||
|
|
d334703c65 | ||
|
|
5f6f3c94c9 | ||
|
|
09ba42a974 | ||
|
|
b1e43d6f97 | ||
|
|
ea92a61d13 | ||
|
|
7fda69a6aa | ||
|
|
cf8b9ac649 | ||
|
|
3c122b005d | ||
|
|
f9dc6105f4 | ||
|
|
d7fe3c80e6 | ||
|
|
e996deea0b | ||
|
|
580ae15af6 | ||
|
|
e66078e52e | ||
|
|
f84356f5d0 | ||
|
|
1fa5a8436b | ||
|
|
2a471ffa96 | ||
|
|
1ee5e1ab3a | ||
|
|
4d90cad034 | ||
|
|
45615b1fc5 | ||
|
|
9fd0e7fea4 | ||
|
|
696016bd8f | ||
|
|
cc46e09853 | ||
|
|
83e84836b5 | ||
|
|
14bb544344 | ||
|
|
ceec18ff5e | ||
|
|
d36ad43700 | ||
|
|
fb39f74ba5 | ||
|
|
9bfc73d6ee | ||
|
|
efb72eace9 | ||
|
|
84b15f4a49 | ||
|
|
2c793fd83b | ||
|
|
900b204bb0 | ||
|
|
17d679901a | ||
|
|
c3d9147705 | ||
|
|
d8036779f8 | ||
|
|
2fafdcc4f8 | ||
|
|
ef3f96ba74 | ||
|
|
91bf1b277f | ||
|
|
c6acd35ceb | ||
|
|
a85a0b7d78 | ||
|
|
3a35674ea2 | ||
|
|
3235cf1c4f | ||
|
|
3f6bda28b3 | ||
|
|
c0b4f4dd79 | ||
|
|
ad96031aeb | ||
|
|
aa9e66e6a2 | ||
|
|
86081654fb | ||
|
|
cac030ffed | ||
|
|
2a913e26e7 | ||
|
|
f60375cd5f | ||
|
|
5920270899 | ||
|
|
f36aee44c6 | ||
|
|
cd24526a9d | ||
|
|
a345ac1390 | ||
|
|
71f4f089b6 | ||
|
|
0f72809342 | ||
|
|
40a033d692 | ||
|
|
d44df2c23c | ||
|
|
47fde1e08b | ||
|
|
0688521ae8 | ||
|
|
bdf0f21647 | ||
|
|
bb03342ff2 | ||
|
|
937304f27b | ||
|
|
6b4ce0ea69 | ||
|
|
7f0c4860f8 | ||
|
|
9b4c70a5ed | ||
|
|
49137273ae | ||
|
|
647e3e5e85 | ||
|
|
4920bf63e3 | ||
|
|
0afcdb2cdf | ||
|
|
d96c3c3c8a | ||
|
|
3d987b8e1d | ||
|
|
f5e5408d70 | ||
|
|
d62899c990 | ||
|
|
57043912e0 | ||
|
|
00aef5ea6b | ||
|
|
369b69668c | ||
|
|
65245f4560 | ||
|
|
4d4fdc97d4 | ||
|
|
c96577891c | ||
|
|
f48b2fc9cb | ||
|
|
2fca2580ed | ||
|
|
7adc1da361 | ||
|
|
6dc24dde43 | ||
|
|
4929e0e6ec | ||
|
|
16a8b8ed71 | ||
|
|
ad1412817e | ||
|
|
d9e6bb3bea | ||
|
|
8970404638 | ||
|
|
2a2241d7f9 | ||
|
|
db1a47e8eb | ||
|
|
3e57061cef | ||
|
|
cd200f8450 | ||
|
|
782013079f | ||
|
|
e5db8acd66 | ||
|
|
1a6a8019c8 | ||
|
|
e935eef29f | ||
|
|
381defda51 | ||
|
|
02ae80c204 | ||
|
|
82214b30e8 | ||
|
|
33a1f48602 | ||
|
|
aee845e5cc | ||
|
|
cd780f6006 | ||
|
|
d4741fefa0 | ||
|
|
7e1e8a2616 | ||
|
|
d73c05cdfc | ||
|
|
78323023cb | ||
|
|
2cf084c98f | ||
|
|
e5bdeba1d7 | ||
|
|
8d7db7774f | ||
|
|
78d22c670c | ||
|
|
0a679109f5 | ||
|
|
e843142b7e | ||
|
|
72e728f655 | ||
|
|
ef56792f56 | ||
|
|
504a6959e8 | ||
|
|
6054a3d65c | ||
|
|
f50eac02d8 | ||
|
|
9634db9061 | ||
|
|
97e3e283dd | ||
|
|
f1e233569b | ||
|
|
04dd637fa9 | ||
|
|
c48a4105a9 | ||
|
|
aac53d949b | ||
|
|
9bb4e5b467 | ||
|
|
fb0391d5cd | ||
|
|
e4d898c903 | ||
|
|
da222f75bb | ||
|
|
25fbd91eb3 | ||
|
|
d458cca7bf | ||
|
|
3933a61b5a | ||
|
|
29092bbf36 | ||
|
|
a33d2578c9 | ||
|
|
9afe4b5ac6 | ||
|
|
6782006b05 | ||
|
|
90bdbefd48 | ||
|
|
b7bcf1082e | ||
|
|
ba9bbc5b6e | ||
|
|
0fecfbd50c | ||
|
|
0031dc6119 | ||
|
|
79e606698e | ||
|
|
3c9fc43780 | ||
|
|
adb9b7394a | ||
|
|
6191fdfaef | ||
|
|
446754e8a6 | ||
|
|
30c67b0b39 | ||
|
|
1043ea7b11 | ||
|
|
b449bcd006 | ||
|
|
a9d513b564 | ||
|
|
5cef527810 | ||
|
|
8bb907747d | ||
|
|
9c889f8df3 | ||
|
|
cbc164d844 | ||
|
|
b8e3060887 | ||
|
|
1aa1ede421 | ||
|
|
480dba7629 | ||
|
|
9b9c66a149 | ||
|
|
0f5eb923ee | ||
|
|
7f521b3129 | ||
|
|
9ce217d1f2 | ||
|
|
7b1bd3ccad | ||
|
|
13b1cbde6b | ||
|
|
8d898a1a78 | ||
|
|
51c2890ede | ||
|
|
03642faa9c | ||
|
|
084c6e1e59 | ||
|
|
90ed28e7a0 | ||
|
|
d2b45c1c84 | ||
|
|
a119ba5f80 | ||
|
|
8c1191a08f | ||
|
|
4275d596e6 | ||
|
|
2709d5226d | ||
|
|
8f613e3255 | ||
|
|
6831e846cf | ||
|
|
034eb9427d | ||
|
|
f73c325db3 | ||
|
|
5e2b11c504 | ||
|
|
ec13133431 | ||
|
|
a8a56a3ed8 | ||
|
|
4e9c7c4de2 | ||
|
|
ffb7894098 | ||
|
|
0d9520ac45 | ||
|
|
be852e57df | ||
|
|
157b38b8ae | ||
|
|
83196a1a0d | ||
|
|
306225b054 | ||
|
|
6efc71d8d2 | ||
|
|
cc4cd4d3f8 | ||
|
|
00e3292205 | ||
|
|
316952423c | ||
|
|
61c2abd014 | ||
|
|
ee5f299b90 | ||
|
|
f153846381 | ||
|
|
0656db0858 | ||
|
|
7f250cb8df | ||
|
|
a1e73eca89 | ||
|
|
1dc6936da6 | ||
|
|
0431d80a8d | ||
|
|
eaa78093f7 | ||
|
|
25b7151fde | ||
|
|
0438b579b6 | ||
|
|
afa50a4e8c | ||
|
|
85bdb0067b | ||
|
|
760cbc7f9a | ||
|
|
d0e34fcd90 | ||
|
|
da434b9a9b | ||
|
|
48863dd510 | ||
|
|
2ca34278f9 | ||
|
|
a79779f813 | ||
|
|
cc83f2baf3 | ||
|
|
728496b831 | ||
|
|
bbc99162c6 | ||
|
|
eed3af9e3e | ||
|
|
50187ff376 | ||
|
|
5f30919fb4 | ||
|
|
14c3cfac85 | ||
|
|
e978f02765 | ||
|
|
8d877c480f | ||
|
|
c53efee9a1 | ||
|
|
148c461e86 | ||
|
|
fcadb9883d | ||
|
|
bb6491e10a | ||
|
|
6248ccf376 | ||
|
|
c9e08f36fa | ||
|
|
10b95d753b | ||
|
|
c3989083cf | ||
|
|
01db585094 | ||
|
|
cc67cb330c | ||
|
|
52ed3c5a04 | ||
|
|
5976f6230a | ||
|
|
3553f03a95 | ||
|
|
d6e2d889c3 | ||
|
|
a777b3b450 | ||
|
|
9957efbea0 | ||
|
|
22e7b9730f | ||
|
|
91470b8509 | ||
|
|
c9d5327328 | ||
|
|
1aa61b72e5 | ||
|
|
3ca5edc3fc | ||
|
|
a092ebaeb3 | ||
|
|
5b9e84c255 | ||
|
|
9c058b926f | ||
|
|
4f2d2ae6e8 | ||
|
|
75aa26a018 | ||
|
|
0f795254e5 | ||
|
|
33592f0a83 | ||
|
|
d6fd01eaca | ||
|
|
1cdc58378a | ||
|
|
584b11fce3 | ||
|
|
fe2039062b | ||
|
|
0269756b52 | ||
|
|
df1a6cf764 | ||
|
|
6d2385b6b3 | ||
|
|
44eaa36cef | ||
|
|
50b40c4a07 | ||
|
|
ee6e0ff26c | ||
|
|
4d9574bf38 | ||
|
|
813be9a2be | ||
|
|
cc76ebfafb | ||
|
|
7989ee0243 | ||
|
|
3aa1997cfd | ||
|
|
c3da15552e | ||
|
|
a014fe9443 | ||
|
|
92551d4ca3 | ||
|
|
8010858e85 | ||
|
|
4efb4875b0 | ||
|
|
c5d041e46d | ||
|
|
53c2223aae | ||
|
|
25034ac0ae | ||
|
|
ac9de72b75 | ||
|
|
1f48ad93f2 | ||
|
|
38f7f7aa00 | ||
|
|
fe8175c63a | ||
|
|
2d9e01bbc1 | ||
|
|
022a227b08 | ||
|
|
a2228259f1 | ||
|
|
a61af7c56f | ||
|
|
5d6a646976 | ||
|
|
628d0d7492 | ||
|
|
b76c8745ec | ||
|
|
51e67bc441 | ||
|
|
8887f75b70 | ||
|
|
9436a838c0 | ||
|
|
ef120fa36f | ||
|
|
8c6385e2c5 | ||
|
|
0bd85d9905 | ||
|
|
ce0dab7b28 | ||
|
|
bdcebf1576 | ||
|
|
ddcc5670ce | ||
|
|
86afa184e2 | ||
|
|
77f341f139 | ||
|
|
918b5d99c2 | ||
|
|
7a098d6eff | ||
|
|
239b6f8202 | ||
|
|
8404c79148 | ||
|
|
5b2d04e09d | ||
|
|
6bd13f99d2 | ||
|
|
2e8e12c1c8 | ||
|
|
17929a6b2d | ||
|
|
9455eaf820 | ||
|
|
cc054487ba | ||
|
|
e2df320d00 | ||
|
|
d74313f996 | ||
|
|
b3cab67049 | ||
|
|
996f0b22b9 | ||
|
|
67952ea98e | ||
|
|
7a02ca435f | ||
|
|
71f81283f5 | ||
|
|
058c7c3c33 | ||
|
|
870e33879b | ||
|
|
3ca82bdfc5 | ||
|
|
4721bad286 | ||
|
|
f040cf2f07 | ||
|
|
8d50717c90 | ||
|
|
2512ad3c95 | ||
|
|
8d55f62da9 | ||
|
|
bc7e007634 | ||
|
|
1f3c87e0c7 | ||
|
|
ee0048a406 | ||
|
|
14dcc769f2 | ||
|
|
f2e6255eb3 | ||
|
|
7d392e20fb | ||
|
|
73e08faee9 | ||
|
|
02dc7711e4 | ||
|
|
67b4d80e5b | ||
|
|
5168d2bb39 | ||
|
|
57190a75bf | ||
|
|
f10e865895 |
1
.github/FUNDING.yml
vendored
1
.github/FUNDING.yml
vendored
@@ -3,7 +3,6 @@
|
|||||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||||
patreon: # mastodon
|
patreon: # mastodon
|
||||||
open_collective: # Replace with a single Open Collective username e.g., user1
|
open_collective: # Replace with a single Open Collective username e.g., user1
|
||||||
ko_fi: xsk22
|
|
||||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||||
liberapay: # Replace with a single Liberapay username e.g., user1
|
liberapay: # Replace with a single Liberapay username e.g., user1
|
||||||
|
|||||||
16
README.md
16
README.md
@@ -54,9 +54,15 @@ You can create drafts, edit them, send them manually later or set a scheduled da
|
|||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
### IzzyOnDroid
|
### Google Play Store
|
||||||
|
|
||||||
[apt.izzysoft.de/fdroid/index/apk/org.joinmastodon.android.sk](https://apt.izzysoft.de/fdroid/index/apk/org.joinmastodon.android.sk)
|
[https://play.google.com/store/apps/details?id=org.joinmastodon.android.sk](https://play.google.com/store/apps/details?id=org.joinmastodon.android.sk)
|
||||||
|
|
||||||
|
<a href="https://play.google.com/store/apps/details?id=org.joinmastodon.android.sk"><img height="50" alt="Get it on Google Play" src="img/google-play-badge.png"></a>
|
||||||
|
|
||||||
|
### F-Droid via IzzyOnDroid
|
||||||
|
|
||||||
|
[https://apt.izzysoft.de/fdroid/index/apk/org.joinmastodon.android.sk](https://apt.izzysoft.de/fdroid/index/apk/org.joinmastodon.android.sk)
|
||||||
|
|
||||||
<a href="https://apt.izzysoft.de/fdroid/index/apk/org.joinmastodon.android.sk"><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.sk"><img height="50" alt="Get it on IzzyOnDroid" src="img/izzy-badge.png"></a>
|
||||||
|
|
||||||
@@ -64,11 +70,11 @@ Note that you'll need to add Izzy's F-Droid repository to your F-Droid app first
|
|||||||
|
|
||||||
[`https://apt.izzysoft.de/fdroid/repo`](https://apt.izzysoft.de/fdroid/repo)
|
[`https://apt.izzysoft.de/fdroid/repo`](https://apt.izzysoft.de/fdroid/repo)
|
||||||
|
|
||||||
### Google Play Store
|
### F-Droid via saunarepo
|
||||||
|
|
||||||
[play.google.com/store/apps/details?id=org.joinmastodon.android.sk](https://play.google.com/store/apps/details?id=org.joinmastodon.android.sk)
|
[https://repo.the-sauna.icu](https://repo.the-sauna.icu/)
|
||||||
|
|
||||||
<a href="https://play.google.com/store/apps/details?id=org.joinmastodon.android.sk"><img height="50" alt="Get it on Google Play" src="img/google-play-badge.png"></a>
|
<a href="https://repo.the-sauna.icu"><img height="28" alt="Get it on SaunaRepo" src="img/saunarepo-badge.svg"></a>
|
||||||
|
|
||||||
### F-Droid
|
### F-Droid
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,12 @@ buildscript {
|
|||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
maven {
|
||||||
|
url "https://www.jitpack.io"
|
||||||
|
content {
|
||||||
|
includeModule 'com.github.UnifiedPush', 'android-connector'
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:8.0.0'
|
classpath 'com.android.tools.build:gradle:8.0.0'
|
||||||
|
|||||||
1
img/saunarepo-badge.svg
Normal file
1
img/saunarepo-badge.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="124.25" height="28" role="img" aria-label="SAUNAREPO"><title>SAUNAREPO</title><g shape-rendering="crispEdges"><rect width="124.25" height="28" fill="#fb8441"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="100"><image x="9" y="7" width="14" height="14" xlink:href="data:image/svg+xml;base64,PHN2ZyBmaWxsPSJ3aGl0ZSIgcm9sZT0iaW1nIiB2aWV3Qm94PSIwIDAgMjQgMjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHRpdGxlPkFuZHJvaWQ8L3RpdGxlPjxwYXRoIGQ9Ik0xNy41MjMgMTUuMzQxNGMtLjU1MTEgMC0uOTk5My0uNDQ4Ni0uOTk5My0uOTk5N3MuNDQ4My0uOTk5My45OTkzLS45OTkzYy41NTExIDAgLjk5OTMuNDQ4My45OTkzLjk5OTMuMDAwMS41NTExLS40NDgyLjk5OTctLjk5OTMuOTk5N20tMTEuMDQ2IDBjLS41NTExIDAtLjk5OTMtLjQ0ODYtLjk5OTMtLjk5OTdzLjQ0ODItLjk5OTMuOTk5My0uOTk5M2MuNTUxMSAwIC45OTkzLjQ0ODMuOTk5My45OTkzIDAgLjU1MTEtLjQ0ODMuOTk5Ny0uOTk5My45OTk3bTExLjQwNDUtNi4wMmwxLjk5NzMtMy40NTkyYS40MTYuNDE2IDAgMDAtLjE1MjEtLjU2NzYuNDE2LjQxNiAwIDAwLS41Njc2LjE1MjFsLTIuMDIyMyAzLjUwM0MxNS41OTAyIDguMjQzOSAxMy44NTMzIDcuODUwOCAxMiA3Ljg1MDhzLTMuNTkwMi4zOTMxLTUuMTM2NyAxLjA5ODlMNC44NDEgNS40NDY3YS40MTYxLjQxNjEgMCAwMC0uNTY3Ny0uMTUyMS40MTU3LjQxNTcgMCAwMC0uMTUyMS41Njc2bDEuOTk3MyAzLjQ1OTJDMi42ODg5IDExLjE4NjcuMzQzMiAxNC42NTg5IDAgMTguNzYxaDI0Yy0uMzQzNS00LjEwMjEtMi42ODkyLTcuNTc0My02LjExODUtOS40Mzk2Ii8+PC9zdmc+"/><text transform="scale(.1)" x="721.25" y="175" textLength="802.5" fill="#fff" font-weight="bold">SAUNAREPO</text></g></svg>
|
||||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -15,8 +15,8 @@ android {
|
|||||||
applicationId "org.joinmastodon.android.sk"
|
applicationId "org.joinmastodon.android.sk"
|
||||||
minSdk 23
|
minSdk 23
|
||||||
targetSdk 33
|
targetSdk 33
|
||||||
versionCode 96
|
versionCode 98
|
||||||
versionName "2.0.1+fork.96"
|
versionName "2.0.3+fork.98"
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
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']
|
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']
|
||||||
}
|
}
|
||||||
@@ -34,6 +34,7 @@ android {
|
|||||||
}
|
}
|
||||||
githubRelease { initWith release }
|
githubRelease { initWith release }
|
||||||
playRelease { initWith release }
|
playRelease { initWith release }
|
||||||
|
fdroidRelease { initWith release }
|
||||||
}
|
}
|
||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility JavaVersion.VERSION_17
|
sourceCompatibility JavaVersion.VERSION_17
|
||||||
@@ -78,6 +79,7 @@ dependencies {
|
|||||||
implementation 'com.github.bottom-software-foundation:bottom-java:2.1.0'
|
implementation 'com.github.bottom-software-foundation:bottom-java:2.1.0'
|
||||||
annotationProcessor 'org.parceler:parceler:1.1.12'
|
annotationProcessor 'org.parceler:parceler:1.1.12'
|
||||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.3'
|
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.3'
|
||||||
|
implementation 'com.github.UnifiedPush:android-connector:2.1.1'
|
||||||
|
|
||||||
androidTestImplementation 'androidx.test:core:1.5.0'
|
androidTestImplementation 'androidx.test:core:1.5.0'
|
||||||
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
|
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
<manifest xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
||||||
@@ -36,18 +37,6 @@
|
|||||||
<action android:name="android.intent.action.MAIN"/>
|
<action android:name="android.intent.action.MAIN"/>
|
||||||
<category android:name="android.intent.category.LAUNCHER"/>
|
<category android:name="android.intent.category.LAUNCHER"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<intent-filter android:autoVerify="true">
|
|
||||||
<action android:name="android.intent.action.VIEW"/>
|
|
||||||
<category android:name="android.intent.category.BROWSABLE"/>
|
|
||||||
<category android:name="android.intent.category.DEFAULT"/>
|
|
||||||
<data android:scheme="https" android:host="mastodon.social" android:pathPrefix="/@"/>
|
|
||||||
</intent-filter>
|
|
||||||
<intent-filter android:autoVerify="true">
|
|
||||||
<action android:name="android.intent.action.VIEW"/>
|
|
||||||
<category android:name="android.intent.category.BROWSABLE"/>
|
|
||||||
<category android:name="android.intent.category.DEFAULT"/>
|
|
||||||
<data android:scheme="https" android:host="mastodon.online" android:pathPrefix="/@"/>
|
|
||||||
</intent-filter>
|
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".PanicResponderActivity"
|
android:name=".PanicResponderActivity"
|
||||||
@@ -57,7 +46,6 @@
|
|||||||
android:theme="@android:style/Theme.NoDisplay">
|
android:theme="@android:style/Theme.NoDisplay">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="info.guardianproject.panic.action.TRIGGER" />
|
<action android:name="info.guardianproject.panic.action.TRIGGER" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
@@ -100,6 +88,15 @@
|
|||||||
<category android:name="me.grishka.fcmtest"/>
|
<category android:name="me.grishka.fcmtest"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</receiver>
|
</receiver>
|
||||||
|
<receiver android:exported="true" android:enabled="true" android:name=".UnifiedPushNotificationReceiver"
|
||||||
|
tools:ignore="ExportedReceiver">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="org.unifiedpush.android.connector.MESSAGE"/>
|
||||||
|
<action android:name="org.unifiedpush.android.connector.UNREGISTERED"/>
|
||||||
|
<action android:name="org.unifiedpush.android.connector.NEW_ENDPOINT"/>
|
||||||
|
<action android:name="org.unifiedpush.android.connector.REGISTRATION_FAILED"/>
|
||||||
|
</intent-filter>
|
||||||
|
</receiver>
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
|
|||||||
@@ -169,7 +169,8 @@ public class AudioPlayerService extends Service{
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateNotification(false, false);
|
updateNotification(false, false);
|
||||||
getSystemService(AudioManager.class).requestAudioFocus(audioFocusChangeListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
|
int audiofocus = GlobalUserPreferences.overlayMedia ? AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK : AudioManager.AUDIOFOCUS_GAIN;
|
||||||
|
getSystemService(AudioManager.class).requestAudioFocus(audioFocusChangeListener, AudioManager.STREAM_MUSIC, audiofocus);
|
||||||
|
|
||||||
player=new MediaPlayer();
|
player=new MediaPlayer();
|
||||||
player.setOnPreparedListener(this::onPlayerPrepared);
|
player.setOnPreparedListener(this::onPlayerPrepared);
|
||||||
|
|||||||
@@ -57,6 +57,9 @@ public class GlobalUserPreferences{
|
|||||||
public static AutoRevealMode autoRevealEqualSpoilers;
|
public static AutoRevealMode autoRevealEqualSpoilers;
|
||||||
public static ColorPreference color;
|
public static ColorPreference color;
|
||||||
public static boolean disableM3PillActiveIndicator;
|
public static boolean disableM3PillActiveIndicator;
|
||||||
|
public static boolean showNavigationLabels;
|
||||||
|
public static boolean displayPronounsInTimelines, displayPronounsInThreads, displayPronounsInUserListings;
|
||||||
|
public static boolean overlayMedia;
|
||||||
|
|
||||||
private static SharedPreferences getPrefs(){
|
private static SharedPreferences getPrefs(){
|
||||||
return MastodonApp.context.getSharedPreferences("global", Context.MODE_PRIVATE);
|
return MastodonApp.context.getSharedPreferences("global", Context.MODE_PRIVATE);
|
||||||
@@ -111,6 +114,11 @@ public class GlobalUserPreferences{
|
|||||||
autoRevealEqualSpoilers=AutoRevealMode.valueOf(prefs.getString("autoRevealEqualSpoilers", AutoRevealMode.THREADS.name()));
|
autoRevealEqualSpoilers=AutoRevealMode.valueOf(prefs.getString("autoRevealEqualSpoilers", AutoRevealMode.THREADS.name()));
|
||||||
forwardReportDefault=prefs.getBoolean("forwardReportDefault", true);
|
forwardReportDefault=prefs.getBoolean("forwardReportDefault", true);
|
||||||
disableM3PillActiveIndicator=prefs.getBoolean("disableM3PillActiveIndicator", false);
|
disableM3PillActiveIndicator=prefs.getBoolean("disableM3PillActiveIndicator", false);
|
||||||
|
showNavigationLabels=prefs.getBoolean("showNavigationLabels", true);
|
||||||
|
displayPronounsInTimelines=prefs.getBoolean("displayPronounsInTimelines", true);
|
||||||
|
displayPronounsInThreads=prefs.getBoolean("displayPronounsInThreads", true);
|
||||||
|
displayPronounsInUserListings=prefs.getBoolean("displayPronounsInUserListings", true);
|
||||||
|
overlayMedia=prefs.getBoolean("overlayMedia", false);
|
||||||
|
|
||||||
if (prefs.contains("prefixRepliesWithRe")) {
|
if (prefs.contains("prefixRepliesWithRe")) {
|
||||||
prefixReplies = prefs.getBoolean("prefixRepliesWithRe", false)
|
prefixReplies = prefs.getBoolean("prefixRepliesWithRe", false)
|
||||||
@@ -164,6 +172,11 @@ public class GlobalUserPreferences{
|
|||||||
.putString("autoRevealEqualSpoilers", autoRevealEqualSpoilers.name())
|
.putString("autoRevealEqualSpoilers", autoRevealEqualSpoilers.name())
|
||||||
.putBoolean("forwardReportDefault", forwardReportDefault)
|
.putBoolean("forwardReportDefault", forwardReportDefault)
|
||||||
.putBoolean("disableM3PillActiveIndicator", disableM3PillActiveIndicator)
|
.putBoolean("disableM3PillActiveIndicator", disableM3PillActiveIndicator)
|
||||||
|
.putBoolean("showNavigationLabels", showNavigationLabels)
|
||||||
|
.putBoolean("displayPronounsInTimelines", displayPronounsInTimelines)
|
||||||
|
.putBoolean("displayPronounsInThreads", displayPronounsInThreads)
|
||||||
|
.putBoolean("displayPronounsInUserListings", displayPronounsInUserListings)
|
||||||
|
.putBoolean("overlayMedia", overlayMedia)
|
||||||
.apply();
|
.apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import android.content.Intent;
|
|||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.text.SpannableStringBuilder;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
@@ -32,6 +33,7 @@ import org.joinmastodon.android.model.Preferences;
|
|||||||
import org.joinmastodon.android.model.PushNotification;
|
import org.joinmastodon.android.model.PushNotification;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
import org.joinmastodon.android.model.StatusPrivacy;
|
import org.joinmastodon.android.model.StatusPrivacy;
|
||||||
|
import org.joinmastodon.android.ui.text.HtmlParser;
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
import org.parceler.Parcels;
|
import org.parceler.Parcels;
|
||||||
|
|
||||||
@@ -58,7 +60,7 @@ public class PushNotificationReceiver extends BroadcastReceiver{
|
|||||||
|
|
||||||
private static final int SUMMARY_ID = 791;
|
private static final int SUMMARY_ID = 791;
|
||||||
private static int notificationId = 0;
|
private static int notificationId = 0;
|
||||||
private static Map<String, Integer> notificationIdsForAccounts = new HashMap<>();
|
private static final Map<String, Integer> notificationIdsForAccounts = new HashMap<>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent){
|
public void onReceive(Context context, Intent intent){
|
||||||
@@ -148,6 +150,11 @@ public class PushNotificationReceiver extends BroadcastReceiver{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void notifyUnifiedPush(Context context, String accountID, org.joinmastodon.android.model.Notification notification) {
|
||||||
|
// push notifications are only created from the official push notification, so we create a fake from by transforming the notification
|
||||||
|
PushNotificationReceiver.this.notify(context, PushNotification.fromNotification(context, notification), accountID, notification);
|
||||||
|
}
|
||||||
|
|
||||||
private void notify(Context context, PushNotification pn, String accountID, org.joinmastodon.android.model.Notification notification){
|
private void notify(Context context, PushNotification pn, String accountID, org.joinmastodon.android.model.Notification notification){
|
||||||
NotificationManager nm=context.getSystemService(NotificationManager.class);
|
NotificationManager nm=context.getSystemService(NotificationManager.class);
|
||||||
AccountSession session=AccountSessionManager.get(accountID);
|
AccountSession session=AccountSessionManager.get(accountID);
|
||||||
@@ -318,7 +325,7 @@ public class PushNotificationReceiver extends BroadcastReceiver{
|
|||||||
req.visibility = preferences.postingDefaultVisibility;
|
req.visibility = preferences.postingDefaultVisibility;
|
||||||
req.inReplyToId = notification.status.id;
|
req.inReplyToId = notification.status.id;
|
||||||
|
|
||||||
if (!notification.status.spoilerText.isEmpty() &&
|
if (notification.status.hasSpoiler() &&
|
||||||
(GlobalUserPreferences.prefixReplies == ALWAYS
|
(GlobalUserPreferences.prefixReplies == ALWAYS
|
||||||
|| (GlobalUserPreferences.prefixReplies == TO_OTHERS && !ownID.equals(notification.status.account.id)))
|
|| (GlobalUserPreferences.prefixReplies == TO_OTHERS && !ownID.equals(notification.status.account.id)))
|
||||||
&& !notification.status.spoilerText.startsWith("re: ")) {
|
&& !notification.status.spoilerText.startsWith("re: ")) {
|
||||||
|
|||||||
@@ -0,0 +1,84 @@
|
|||||||
|
package org.joinmastodon.android;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.joinmastodon.android.api.MastodonAPIController;
|
||||||
|
import org.joinmastodon.android.api.session.AccountSession;
|
||||||
|
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||||
|
import org.joinmastodon.android.model.Notification;
|
||||||
|
import org.joinmastodon.android.model.PaginatedResponse;
|
||||||
|
import org.unifiedpush.android.connector.MessagingReceiver;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import me.grishka.appkit.api.Callback;
|
||||||
|
import me.grishka.appkit.api.ErrorResponse;
|
||||||
|
|
||||||
|
public class UnifiedPushNotificationReceiver extends MessagingReceiver{
|
||||||
|
private static final String TAG="UnifiedPushNotificationReceiver";
|
||||||
|
|
||||||
|
public UnifiedPushNotificationReceiver() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNewEndpoint(@NotNull Context context, @NotNull String endpoint, @NotNull String instance) {
|
||||||
|
// Called when a new endpoint be used for sending push messages
|
||||||
|
Log.d(TAG, "onNewEndpoint: New Endpoint " + endpoint + " for "+ instance);
|
||||||
|
AccountSession account = AccountSessionManager.getInstance().tryGetAccount(instance);
|
||||||
|
if (account != null)
|
||||||
|
account.getPushSubscriptionManager().registerAccountForPush(null, endpoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRegistrationFailed(@NotNull Context context, @NotNull String instance) {
|
||||||
|
// called when the registration is not possible, eg. no network
|
||||||
|
Log.d(TAG, "onRegistrationFailed: " + instance);
|
||||||
|
//re-register for gcm
|
||||||
|
AccountSession account = AccountSessionManager.getInstance().tryGetAccount(instance);
|
||||||
|
if (account != null)
|
||||||
|
account.getPushSubscriptionManager().registerAccountForPush(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onUnregistered(@NotNull Context context, @NotNull String instance) {
|
||||||
|
// called when this application is unregistered from receiving push messages
|
||||||
|
Log.d(TAG, "onUnregistered: " + instance);
|
||||||
|
//re-register for gcm
|
||||||
|
AccountSession account = AccountSessionManager.getInstance().tryGetAccount(instance);
|
||||||
|
if (account != null)
|
||||||
|
account.getPushSubscriptionManager().registerAccountForPush(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMessage(@NotNull Context context, @NotNull byte[] message, @NotNull String instance) {
|
||||||
|
// Called when a new message is received. The message contains the full POST body of the push message
|
||||||
|
AccountSession account = AccountSessionManager.getInstance().tryGetAccount(instance);
|
||||||
|
|
||||||
|
if (account == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
//this is stupid
|
||||||
|
// Mastodon stores the info to decrypt the message in the HTTP headers, which are not accessible in UnifiedPush,
|
||||||
|
// thus it is not possible to decrypt them. SO we need to re-request them from the server and transform them later on
|
||||||
|
// The official uses fcm and moves the headers to extra data, see
|
||||||
|
// https://github.com/mastodon/webpush-fcm-relay/blob/cac95b28d5364b0204f629283141ac3fb749e0c5/webpush-fcm-relay.go#L116
|
||||||
|
// https://github.com/tuskyapp/Tusky/pull/2303#issue-1112080540
|
||||||
|
account.getCacheController().getNotifications(null, 1, false, false, true, new Callback<>(){
|
||||||
|
@Override
|
||||||
|
public void onSuccess(PaginatedResponse<List<Notification>> result){
|
||||||
|
result.items
|
||||||
|
.stream()
|
||||||
|
.findFirst()
|
||||||
|
.ifPresent(value->MastodonAPIController.runInBackground(()->new PushNotificationReceiver().notifyUnifiedPush(context, instance, value)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(ErrorResponse error){
|
||||||
|
//professional error handling
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -121,13 +121,13 @@ public abstract class MastodonAPIRequest<T> extends APIRequest<T>{
|
|||||||
.orElseGet(() -> this.execNoAuth(domain));
|
.orElseGet(() -> this.execNoAuth(domain));
|
||||||
}
|
}
|
||||||
|
|
||||||
public MastodonAPIRequest<T> wrapProgress(Activity activity, @StringRes int message, boolean cancelable){
|
public MastodonAPIRequest<T> wrapProgress(Context context, @StringRes int message, boolean cancelable){
|
||||||
return wrapProgress(activity, message, cancelable, null);
|
return wrapProgress(context, message, cancelable, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public MastodonAPIRequest<T> wrapProgress(Activity activity, @StringRes int message, boolean cancelable, Consumer<ProgressDialog> transform){
|
public MastodonAPIRequest<T> wrapProgress(Context context, @StringRes int message, boolean cancelable, Consumer<ProgressDialog> transform){
|
||||||
progressDialog=new ProgressDialog(activity);
|
progressDialog=new ProgressDialog(context);
|
||||||
progressDialog.setMessage(activity.getString(message));
|
progressDialog.setMessage(context.getString(message));
|
||||||
progressDialog.setCancelable(cancelable);
|
progressDialog.setCancelable(cancelable);
|
||||||
if (transform != null) transform.accept(progressDialog);
|
if (transform != null) transform.accept(progressDialog);
|
||||||
if(cancelable){
|
if(cancelable){
|
||||||
|
|||||||
@@ -120,9 +120,22 @@ public class PushSubscriptionManager{
|
|||||||
return !TextUtils.isEmpty(deviceToken);
|
return !TextUtils.isEmpty(deviceToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void registerAccountForPush(PushSubscription subscription){
|
public void registerAccountForPush(PushSubscription subscription){
|
||||||
if(TextUtils.isEmpty(deviceToken))
|
// this function is used for registering push notifications using FCM
|
||||||
throw new IllegalStateException("No device push token available");
|
// to avoid NonFreeNet in F-Droid, this registration is disabled in it
|
||||||
|
// see https://github.com/LucasGGamerM/moshidon/issues/206 for more context
|
||||||
|
if(BuildConfig.BUILD_TYPE.equals("fdroidRelease") || TextUtils.isEmpty(deviceToken)){
|
||||||
|
Log.d(TAG, "Skipping registering for FCM push notifications");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String endpoint = "https://app.joinmastodon.org/relay-to/fcm/"+deviceToken+"/"+accountID;
|
||||||
|
registerAccountForPush(subscription, endpoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerAccountForPush(PushSubscription subscription, String endpoint){
|
||||||
|
|
||||||
MastodonAPIController.runInBackground(()->{
|
MastodonAPIController.runInBackground(()->{
|
||||||
Log.d(TAG, "registerAccountForPush: started for "+accountID);
|
Log.d(TAG, "registerAccountForPush: started for "+accountID);
|
||||||
String encodedPublicKey, encodedAuthKey, pushAccountID;
|
String encodedPublicKey, encodedAuthKey, pushAccountID;
|
||||||
@@ -151,12 +164,11 @@ public class PushSubscriptionManager{
|
|||||||
Log.e(TAG, "registerAccountForPush: error generating encryption key", e);
|
Log.e(TAG, "registerAccountForPush: error generating encryption key", e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
new RegisterForPushNotifications(deviceToken,
|
new RegisterForPushNotifications(endpoint,
|
||||||
encodedPublicKey,
|
encodedPublicKey,
|
||||||
encodedAuthKey,
|
encodedAuthKey,
|
||||||
subscription==null ? PushSubscription.Alerts.ofAll() : subscription.alerts,
|
subscription==null ? PushSubscription.Alerts.ofAll() : subscription.alerts,
|
||||||
subscription==null ? PushSubscription.Policy.ALL : subscription.policy,
|
subscription==null ? PushSubscription.Policy.ALL : subscription.policy)
|
||||||
pushAccountID)
|
|
||||||
.setCallback(new Callback<>(){
|
.setCallback(new Callback<>(){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(PushSubscription result){
|
public void onSuccess(PushSubscription result){
|
||||||
|
|||||||
@@ -4,8 +4,15 @@ import org.joinmastodon.android.api.MastodonAPIRequest;
|
|||||||
import org.joinmastodon.android.model.Relationship;
|
import org.joinmastodon.android.model.Relationship;
|
||||||
|
|
||||||
public class SetAccountMuted extends MastodonAPIRequest<Relationship>{
|
public class SetAccountMuted extends MastodonAPIRequest<Relationship>{
|
||||||
public SetAccountMuted(String id, boolean muted){
|
public SetAccountMuted(String id, boolean muted, long duration){
|
||||||
super(HttpMethod.POST, "/accounts/"+id+"/"+(muted ? "mute" : "unmute"), Relationship.class);
|
super(HttpMethod.POST, "/accounts/"+id+"/"+(muted ? "mute" : "unmute"), Relationship.class);
|
||||||
setRequestBody(new Object());
|
setRequestBody(new Request(duration));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Request{
|
||||||
|
public long duration;
|
||||||
|
public Request(long duration){
|
||||||
|
this.duration=duration;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package org.joinmastodon.android.api.requests.announcements;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||||
|
import org.joinmastodon.android.model.Status;
|
||||||
|
|
||||||
|
public class AddAnnouncementReaction extends MastodonAPIRequest<Object> {
|
||||||
|
public AddAnnouncementReaction(String id, String emoji) {
|
||||||
|
super(HttpMethod.PUT, "/announcements/" + id + "/reactions/" + emoji, Object.class);
|
||||||
|
setRequestBody(new Object());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package org.joinmastodon.android.api.requests.announcements;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||||
|
|
||||||
|
public class DeleteAnnouncementReaction extends MastodonAPIRequest<Object> {
|
||||||
|
public DeleteAnnouncementReaction(String id, String emoji) {
|
||||||
|
super(HttpMethod.DELETE, "/announcements/" + id + "/reactions/" + emoji, Object.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,10 +4,10 @@ import org.joinmastodon.android.api.MastodonAPIRequest;
|
|||||||
import org.joinmastodon.android.model.PushSubscription;
|
import org.joinmastodon.android.model.PushSubscription;
|
||||||
|
|
||||||
public class RegisterForPushNotifications extends MastodonAPIRequest<PushSubscription>{
|
public class RegisterForPushNotifications extends MastodonAPIRequest<PushSubscription>{
|
||||||
public RegisterForPushNotifications(String deviceToken, String encryptionKey, String authKey, PushSubscription.Alerts alerts, PushSubscription.Policy policy, String accountID){
|
public RegisterForPushNotifications(String endpoint, String encryptionKey, String authKey, PushSubscription.Alerts alerts, PushSubscription.Policy policy){
|
||||||
super(HttpMethod.POST, "/push/subscription", PushSubscription.class);
|
super(HttpMethod.POST, "/push/subscription", PushSubscription.class);
|
||||||
Request r=new Request();
|
Request r=new Request();
|
||||||
r.subscription.endpoint="https://app.joinmastodon.org/relay-to/fcm/"+deviceToken+"/"+accountID;
|
r.subscription.endpoint=endpoint;
|
||||||
r.data.alerts=alerts;
|
r.data.alerts=alerts;
|
||||||
r.policy=policy;
|
r.policy=policy;
|
||||||
r.subscription.keys.p256dh=encryptionKey;
|
r.subscription.keys.p256dh=encryptionKey;
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package org.joinmastodon.android.api.requests.statuses;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||||
|
import org.joinmastodon.android.model.Status;
|
||||||
|
|
||||||
|
public class AddStatusReaction extends MastodonAPIRequest<Status> {
|
||||||
|
public AddStatusReaction(String id, String emoji) {
|
||||||
|
super(HttpMethod.POST, "/statuses/" + id + "/react/" + emoji, Status.class);
|
||||||
|
setRequestBody(new Object());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,13 +11,11 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class CreateStatus extends MastodonAPIRequest<Status>{
|
public class CreateStatus extends MastodonAPIRequest<Status>{
|
||||||
public static final Instant DRAFTS_AFTER_INSTANT = Instant.ofEpochMilli(253370764799999L) /* end of 9998 */;
|
public static long EPOCH_OF_THE_YEAR_FIVE_THOUSAND=95617584000000L;
|
||||||
private static final float draftFactor = 31536000000f /* one year */ / 253370764799999f /* end of 9998 */;
|
public static final Instant DRAFTS_AFTER_INSTANT=Instant.ofEpochMilli(EPOCH_OF_THE_YEAR_FIVE_THOUSAND - 1) /* end of 4999 */;
|
||||||
|
|
||||||
public static Instant getDraftInstant() {
|
public static Instant getDraftInstant() {
|
||||||
// returns an instant between 9999-01-01 00:00:00 and 9999-12-31 23:59:59
|
return DRAFTS_AFTER_INSTANT.plusMillis(System.currentTimeMillis());
|
||||||
// yes, this is a weird implementation for something that hardly matters
|
|
||||||
return DRAFTS_AFTER_INSTANT.plusMillis(1 + (long) (System.currentTimeMillis() * draftFactor));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public CreateStatus(CreateStatus.Request req, String uuid){
|
public CreateStatus(CreateStatus.Request req, String uuid){
|
||||||
@@ -36,6 +34,7 @@ public class CreateStatus extends MastodonAPIRequest<Status>{
|
|||||||
|
|
||||||
public static class Request{
|
public static class Request{
|
||||||
public String status;
|
public String status;
|
||||||
|
public List<MediaAttribute> mediaAttributes;
|
||||||
public List<String> mediaIds;
|
public List<String> mediaIds;
|
||||||
public Poll poll;
|
public Poll poll;
|
||||||
public String inReplyToId;
|
public String inReplyToId;
|
||||||
@@ -55,5 +54,17 @@ public class CreateStatus extends MastodonAPIRequest<Status>{
|
|||||||
public boolean multiple;
|
public boolean multiple;
|
||||||
public boolean hideTotals;
|
public boolean hideTotals;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class MediaAttribute{
|
||||||
|
public String id;
|
||||||
|
public String description;
|
||||||
|
public String focus;
|
||||||
|
|
||||||
|
public MediaAttribute(String id, String description, String focus){
|
||||||
|
this.id=id;
|
||||||
|
this.description=description;
|
||||||
|
this.focus=focus;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package org.joinmastodon.android.api.requests.statuses;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||||
|
import org.joinmastodon.android.model.Status;
|
||||||
|
|
||||||
|
public class DeleteStatusReaction extends MastodonAPIRequest<Status> {
|
||||||
|
public DeleteStatusReaction(String id, String emoji) {
|
||||||
|
super(HttpMethod.POST, "/statuses/" + id + "/unreact/" + emoji, Status.class);
|
||||||
|
setRequestBody(new Object());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package org.joinmastodon.android.api.requests.statuses;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||||
|
import org.joinmastodon.android.model.Status;
|
||||||
|
|
||||||
|
public class PleromaAddStatusReaction extends MastodonAPIRequest<Status> {
|
||||||
|
public PleromaAddStatusReaction(String id, String emoji) {
|
||||||
|
super(HttpMethod.PUT, "/pleroma/statuses/" + id + "/reactions/" + emoji, Status.class);
|
||||||
|
setRequestBody(new Object());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package org.joinmastodon.android.api.requests.statuses;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||||
|
import org.joinmastodon.android.model.Status;
|
||||||
|
|
||||||
|
public class PleromaDeleteStatusReaction extends MastodonAPIRequest<Status> {
|
||||||
|
public PleromaDeleteStatusReaction(String id, String emoji) {
|
||||||
|
super(HttpMethod.DELETE, "/pleroma/statuses/" + id + "/reactions/" + emoji, Status.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package org.joinmastodon.android.api.requests.statuses;
|
||||||
|
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||||
|
import org.joinmastodon.android.model.EmojiReaction;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class PleromaGetStatusReactions extends MastodonAPIRequest<List<EmojiReaction>> {
|
||||||
|
public PleromaGetStatusReactions(String id, String emoji) {
|
||||||
|
super(HttpMethod.GET, "/pleroma/statuses/" + id + "/reactions/" + (emoji != null ? emoji : ""), new TypeToken<>(){});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,7 +13,6 @@ import org.joinmastodon.android.model.TimelineDefinition;
|
|||||||
|
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class AccountLocalPreferences{
|
public class AccountLocalPreferences{
|
||||||
private final SharedPreferences prefs;
|
private final SharedPreferences prefs;
|
||||||
@@ -38,6 +37,9 @@ public class AccountLocalPreferences{
|
|||||||
public String timelineReplyVisibility; // akkoma-only
|
public String timelineReplyVisibility; // akkoma-only
|
||||||
public boolean keepOnlyLatestNotification;
|
public boolean keepOnlyLatestNotification;
|
||||||
|
|
||||||
|
public boolean emojiReactionsEnabled;
|
||||||
|
public ShowEmojiReactions showEmojiReactions;
|
||||||
|
|
||||||
private final static Type recentLanguagesType = new TypeToken<ArrayList<String>>() {}.getType();
|
private final static Type recentLanguagesType = new TypeToken<ArrayList<String>>() {}.getType();
|
||||||
private final static Type timelinesType = new TypeToken<ArrayList<TimelineDefinition>>() {}.getType();
|
private final static Type timelinesType = new TypeToken<ArrayList<TimelineDefinition>>() {}.getType();
|
||||||
|
|
||||||
@@ -62,6 +64,8 @@ public class AccountLocalPreferences{
|
|||||||
publishButtonText=prefs.getString("publishButtonText", null);
|
publishButtonText=prefs.getString("publishButtonText", null);
|
||||||
timelineReplyVisibility=prefs.getString("timelineReplyVisibility", null);
|
timelineReplyVisibility=prefs.getString("timelineReplyVisibility", null);
|
||||||
keepOnlyLatestNotification=prefs.getBoolean("keepOnlyLatestNotification", false);
|
keepOnlyLatestNotification=prefs.getBoolean("keepOnlyLatestNotification", false);
|
||||||
|
emojiReactionsEnabled=prefs.getBoolean("emojiReactionsEnabled", session.getInstance().isPresent() && session.getInstance().get().isAkkoma());
|
||||||
|
showEmojiReactions=ShowEmojiReactions.valueOf(prefs.getString("showEmojiReactions", ShowEmojiReactions.HIDE_EMPTY.name()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getNotificationsPauseEndTime(){
|
public long getNotificationsPauseEndTime(){
|
||||||
@@ -93,6 +97,14 @@ public class AccountLocalPreferences{
|
|||||||
.putString("publishButtonText", publishButtonText)
|
.putString("publishButtonText", publishButtonText)
|
||||||
.putString("timelineReplyVisibility", timelineReplyVisibility)
|
.putString("timelineReplyVisibility", timelineReplyVisibility)
|
||||||
.putBoolean("keepOnlyLatestNotification", keepOnlyLatestNotification)
|
.putBoolean("keepOnlyLatestNotification", keepOnlyLatestNotification)
|
||||||
|
.putBoolean("emojiReactionsEnabled", emojiReactionsEnabled)
|
||||||
|
.putString("showEmojiReactions", showEmojiReactions.name())
|
||||||
.apply();
|
.apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum ShowEmojiReactions{
|
||||||
|
HIDE_EMPTY,
|
||||||
|
ONLY_OPENED,
|
||||||
|
ALWAYS
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -146,6 +146,9 @@ public class AccountSession{
|
|||||||
@Override
|
@Override
|
||||||
public void onError(ErrorResponse error){
|
public void onError(ErrorResponse error){
|
||||||
Log.w(TAG, "Failed to load preferences for account "+getID()+": "+error);
|
Log.w(TAG, "Failed to load preferences for account "+getID()+": "+error);
|
||||||
|
if (preferences==null)
|
||||||
|
preferences=new Preferences();
|
||||||
|
preferencesFromAccountSource(self);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.exec(getID());
|
.exec(getID());
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package org.joinmastodon.android.events;
|
||||||
|
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
import org.joinmastodon.android.model.EmojiReaction;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class EmojiReactionsUpdatedEvent{
|
||||||
|
public final String id;
|
||||||
|
public final List<EmojiReaction> reactions;
|
||||||
|
public final boolean updateTextPadding;
|
||||||
|
public RecyclerView.ViewHolder viewHolder;
|
||||||
|
|
||||||
|
public EmojiReactionsUpdatedEvent(String id, List<EmojiReaction> reactions, boolean updateTextPadding, RecyclerView.ViewHolder viewHolder){
|
||||||
|
this.id=id;
|
||||||
|
this.reactions=reactions;
|
||||||
|
this.updateTextPadding=updateTextPadding;
|
||||||
|
this.viewHolder=viewHolder;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
package org.joinmastodon.android.events;
|
package org.joinmastodon.android.events;
|
||||||
|
|
||||||
import org.joinmastodon.android.api.CacheController;
|
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
|
|
||||||
public class StatusCountersUpdatedEvent{
|
public class StatusCountersUpdatedEvent{
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ import org.joinmastodon.android.model.HeaderPaginationList;
|
|||||||
import org.joinmastodon.android.model.Instance;
|
import org.joinmastodon.android.model.Instance;
|
||||||
import org.joinmastodon.android.model.ScheduledStatus;
|
import org.joinmastodon.android.model.ScheduledStatus;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
|
import org.joinmastodon.android.ui.displayitems.DummyStatusDisplayItem;
|
||||||
|
import org.joinmastodon.android.ui.displayitems.EmojiReactionsStatusDisplayItem;
|
||||||
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;
|
||||||
@@ -69,10 +71,12 @@ public class AnnouncementsFragment extends BaseStatusListFragment<Announcement>
|
|||||||
Status fakeStatus = a.toStatus();
|
Status fakeStatus = a.toStatus();
|
||||||
TextStatusDisplayItem textItem = new TextStatusDisplayItem(a.id, HtmlParser.parse(a.content, a.emojis, a.mentions, a.tags, accountID), this, fakeStatus, true);
|
TextStatusDisplayItem textItem = new TextStatusDisplayItem(a.id, HtmlParser.parse(a.content, a.emojis, a.mentions, a.tags, accountID), this, fakeStatus, true);
|
||||||
textItem.textSelectable = true;
|
textItem.textSelectable = true;
|
||||||
return List.of(
|
|
||||||
HeaderStatusDisplayItem.fromAnnouncement(a, fakeStatus, instanceUser, this, accountID, this::onMarkAsRead),
|
List<StatusDisplayItem> items=new ArrayList<>();
|
||||||
textItem
|
items.add(HeaderStatusDisplayItem.fromAnnouncement(a, fakeStatus, instanceUser, this, accountID, this::onMarkAsRead));
|
||||||
);
|
items.add(textItem);
|
||||||
|
if(!isInstanceAkkoma()) items.add(new EmojiReactionsStatusDisplayItem(a.id, this, fakeStatus, accountID, false, true));
|
||||||
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onMarkAsRead(String id) {
|
public void onMarkAsRead(String id) {
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import org.joinmastodon.android.model.Relationship;
|
|||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
import org.joinmastodon.android.ui.BetterItemAnimator;
|
import org.joinmastodon.android.ui.BetterItemAnimator;
|
||||||
import org.joinmastodon.android.ui.displayitems.AccountStatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.AccountStatusDisplayItem;
|
||||||
|
import org.joinmastodon.android.ui.displayitems.EmojiReactionsStatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.displayitems.ExtendedFooterStatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.ExtendedFooterStatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.displayitems.FooterStatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.FooterStatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.displayitems.GapStatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.GapStatusDisplayItem;
|
||||||
@@ -607,6 +608,15 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
|||||||
if (header != null) header.rebind();
|
if (header != null) header.rebind();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void updateEmojiReactions(Status status, String itemID){
|
||||||
|
EmojiReactionsStatusDisplayItem.Holder reactions=findHolderOfType(itemID, EmojiReactionsStatusDisplayItem.Holder.class);
|
||||||
|
if(reactions != null){
|
||||||
|
reactions.getItem().status.reactions.clear();
|
||||||
|
reactions.getItem().status.reactions.addAll(status.reactions);
|
||||||
|
reactions.rebind();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void onGapClick(GapStatusDisplayItem.Holder item){}
|
public void onGapClick(GapStatusDisplayItem.Holder item){}
|
||||||
|
|
||||||
public void onWarningClick(WarningFilteredStatusDisplayItem.Holder warning){
|
public void onWarningClick(WarningFilteredStatusDisplayItem.Holder warning){
|
||||||
@@ -782,6 +792,10 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void scrollBy(int x, int y) {
|
||||||
|
list.scrollBy(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
protected class DisplayItemsAdapter extends UsableRecyclerView.Adapter<BindableViewHolder<StatusDisplayItem>> implements ImageLoaderRecyclerAdapter{
|
protected class DisplayItemsAdapter extends UsableRecyclerView.Adapter<BindableViewHolder<StatusDisplayItem>> implements ImageLoaderRecyclerAdapter{
|
||||||
|
|
||||||
public DisplayItemsAdapter(){
|
public DisplayItemsAdapter(){
|
||||||
|
|||||||
@@ -300,6 +300,13 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
onCustomEmojiClick(emoji);
|
onCustomEmojiClick(emoji);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEmojiSelected(String emoji){
|
||||||
|
if(getActivity().getCurrentFocus() instanceof EditText edit && edit == mainEditText){
|
||||||
|
edit.getText().replace(edit.getSelectionStart(), edit.getSelectionEnd(), emoji);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBackspace(){
|
public void onBackspace(){
|
||||||
getActivity().dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL));
|
getActivity().dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL));
|
||||||
@@ -412,7 +419,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
hasSpoiler=true;
|
hasSpoiler=true;
|
||||||
spoilerWrap.setVisibility(View.VISIBLE);
|
spoilerWrap.setVisibility(View.VISIBLE);
|
||||||
spoilerBtn.setSelected(true);
|
spoilerBtn.setSelected(true);
|
||||||
}else if(editingStatus!=null && !TextUtils.isEmpty(editingStatus.spoilerText)){
|
}else if(editingStatus!=null && editingStatus.hasSpoiler()){
|
||||||
hasSpoiler=true;
|
hasSpoiler=true;
|
||||||
spoilerWrap.setVisibility(View.VISIBLE);
|
spoilerWrap.setVisibility(View.VISIBLE);
|
||||||
spoilerEdit.setText(getArguments().getString("sourceSpoiler", editingStatus.spoilerText));
|
spoilerEdit.setText(getArguments().getString("sourceSpoiler", editingStatus.spoilerText));
|
||||||
@@ -449,7 +456,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
} else if (getArguments().containsKey("sourceContentType")) {
|
} else if (getArguments().containsKey("sourceContentType")) {
|
||||||
try {
|
try {
|
||||||
String val = getArguments().getString("sourceContentType");
|
String val = getArguments().getString("sourceContentType");
|
||||||
contentType = val == null ? null : ContentType.valueOf(val);
|
if (val != null) contentType = ContentType.valueOf(val);
|
||||||
} catch (IllegalArgumentException ignored) {}
|
} catch (IllegalArgumentException ignored) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -666,11 +673,12 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
? UiUtils.formatRelativeTimestamp(getContext(), status.createdAt)
|
? UiUtils.formatRelativeTimestamp(getContext(), status.createdAt)
|
||||||
: getString(R.string.edited_timestamp, UiUtils.formatRelativeTimestamp(getContext(), status.editedAt));
|
: getString(R.string.edited_timestamp, UiUtils.formatRelativeTimestamp(getContext(), status.editedAt));
|
||||||
|
|
||||||
String sepp = getString(R.string.sk_separator);
|
((TextView) view.findViewById(R.id.username)).setText(status.account.getDisplayUsername());
|
||||||
String username = status.account.getDisplayUsername();
|
view.findViewById(R.id.separator).setVisibility(time==null ? View.GONE : View.VISIBLE);
|
||||||
((TextView) view.findViewById(R.id.time_and_username)).setText(time == null ? username :
|
view.findViewById(R.id.time).setVisibility(time==null ? View.GONE : View.VISIBLE);
|
||||||
username + " " + sepp + " " + time);
|
if(time!=null) ((TextView) view.findViewById(R.id.time)).setText(time);
|
||||||
if (status.spoilerText != null && !status.spoilerText.isBlank()) {
|
|
||||||
|
if (status.hasSpoiler()) {
|
||||||
TextView replyToSpoiler = view.findViewById(R.id.reply_to_spoiler);
|
TextView replyToSpoiler = view.findViewById(R.id.reply_to_spoiler);
|
||||||
replyToSpoiler.setVisibility(View.VISIBLE);
|
replyToSpoiler.setVisibility(View.VISIBLE);
|
||||||
replyToSpoiler.setText(status.spoilerText);
|
replyToSpoiler.setText(status.spoilerText);
|
||||||
@@ -1058,6 +1066,9 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
req.scheduledAt=scheduledAt;
|
req.scheduledAt=scheduledAt;
|
||||||
if(!mediaViewController.isEmpty()){
|
if(!mediaViewController.isEmpty()){
|
||||||
req.mediaIds=mediaViewController.getAttachmentIDs();
|
req.mediaIds=mediaViewController.getAttachmentIDs();
|
||||||
|
if(editingStatus != null){
|
||||||
|
req.mediaAttributes=mediaViewController.getAttachmentAttributes();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// ask whether to publish now when editing an existing draft
|
// ask whether to publish now when editing an existing draft
|
||||||
if (!force && editingStatus != null && scheduledAt != null && scheduledAt.isAfter(DRAFTS_AFTER_INSTANT)) {
|
if (!force && editingStatus != null && scheduledAt != null && scheduledAt.isAfter(DRAFTS_AFTER_INSTANT)) {
|
||||||
@@ -1435,8 +1446,8 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateHeaders() {
|
private void updateHeaders() {
|
||||||
UiUtils.setExtraTextInfo(getContext(), selfExtraText, null, false, localOnly || statusVisibility==StatusPrivacy.LOCAL, null);
|
UiUtils.setExtraTextInfo(getContext(), selfExtraText, null, false, false, localOnly || statusVisibility==StatusPrivacy.LOCAL, null);
|
||||||
if (replyTo != null) UiUtils.setExtraTextInfo(getContext(), extraText, pronouns, false, replyTo.localOnly || replyTo.visibility==StatusPrivacy.LOCAL, replyTo.account);
|
if (replyTo != null) UiUtils.setExtraTextInfo(getContext(), extraText, pronouns, true, false, replyTo.localOnly || replyTo.visibility==StatusPrivacy.LOCAL, replyTo.account);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void buildVisibilityPopup(View v){
|
private void buildVisibilityPopup(View v){
|
||||||
|
|||||||
@@ -45,8 +45,8 @@ public class FeaturedHashtagsListFragment extends BaseStatusListFragment<Hashtag
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onItemClick(String id){
|
public void onItemClick(String hashtag){
|
||||||
UiUtils.openHashtagTimeline(getActivity(), accountID, id, data.stream().filter(h -> Objects.equals(h.name, id)).findAny().map(h -> h.following).orElse(null));
|
UiUtils.openHashtagTimeline(getActivity(), accountID, hashtag, data.stream().filter(h -> Objects.equals(h.name, hashtag)).findAny().map(h -> h.following).orElse(null));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -201,7 +201,7 @@ public class FollowRequestsListFragment extends MastodonRecyclerFragment<FollowR
|
|||||||
}
|
}
|
||||||
|
|
||||||
// literally the same as AccountCardStatusDisplayItem and DiscoverAccountsFragment. code should be generalized
|
// literally the same as AccountCardStatusDisplayItem and DiscoverAccountsFragment. code should be generalized
|
||||||
private class AccountViewHolder extends BindableViewHolder<AccountWrapper> implements ImageLoaderViewHolder, UsableRecyclerView.Clickable{
|
private class AccountViewHolder extends BindableViewHolder<AccountWrapper> implements ImageLoaderViewHolder, UsableRecyclerView.DisableableClickable{
|
||||||
private final ImageView cover, avatar;
|
private final ImageView cover, avatar;
|
||||||
private final TextView name, username, bio, followersCount, followingCount, postsCount, followersLabel, followingLabel, postsLabel;
|
private final TextView name, username, bio, followersCount, followingCount, postsCount, followersLabel, followingLabel, postsLabel;
|
||||||
private final ProgressBarButton actionButton, acceptButton, rejectButton;
|
private final ProgressBarButton actionButton, acceptButton, rejectButton;
|
||||||
@@ -233,15 +233,24 @@ public class FollowRequestsListFragment extends MastodonRecyclerFragment<FollowR
|
|||||||
rejectProgress=findViewById(R.id.reject_progress);
|
rejectProgress=findViewById(R.id.reject_progress);
|
||||||
rejectWrap=findViewById(R.id.reject_btn_wrap);
|
rejectWrap=findViewById(R.id.reject_btn_wrap);
|
||||||
|
|
||||||
itemView.setOutlineProvider(OutlineProviders.roundedRect(6));
|
avatar.setOutlineProvider(OutlineProviders.roundedRect(15));
|
||||||
itemView.setClipToOutline(true);
|
|
||||||
avatar.setOutlineProvider(OutlineProviders.roundedRect(12));
|
|
||||||
avatar.setClipToOutline(true);
|
avatar.setClipToOutline(true);
|
||||||
cover.setOutlineProvider(OutlineProviders.roundedRect(3));
|
View border=findViewById(R.id.avatar_border);
|
||||||
|
border.setOutlineProvider(OutlineProviders.roundedRect(17));
|
||||||
|
border.setClipToOutline(true);
|
||||||
|
cover.setOutlineProvider(OutlineProviders.roundedRect(9));
|
||||||
cover.setClipToOutline(true);
|
cover.setClipToOutline(true);
|
||||||
|
itemView.setOutlineProvider(OutlineProviders.roundedRect(12));
|
||||||
|
itemView.setClipToOutline(true);
|
||||||
actionButton.setOnClickListener(this::onActionButtonClick);
|
actionButton.setOnClickListener(this::onActionButtonClick);
|
||||||
acceptButton.setOnClickListener(this::onFollowRequestButtonClick);
|
acceptButton.setOnClickListener(this::onFollowRequestButtonClick);
|
||||||
rejectButton.setOnClickListener(this::onFollowRequestButtonClick);
|
rejectButton.setOnClickListener(this::onFollowRequestButtonClick);
|
||||||
|
itemView.setOnClickListener(v->this.onClick());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabled(){
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -254,26 +263,23 @@ public class FollowRequestsListFragment extends MastodonRecyclerFragment<FollowR
|
|||||||
postsCount.setText(UiUtils.abbreviateNumber(item.account.statusesCount));
|
postsCount.setText(UiUtils.abbreviateNumber(item.account.statusesCount));
|
||||||
followersLabel.setText(getResources().getQuantityString(R.plurals.followers, (int)Math.min(999, item.account.followersCount)));
|
followersLabel.setText(getResources().getQuantityString(R.plurals.followers, (int)Math.min(999, item.account.followersCount)));
|
||||||
followingLabel.setText(getResources().getQuantityString(R.plurals.following, (int)Math.min(999, item.account.followingCount)));
|
followingLabel.setText(getResources().getQuantityString(R.plurals.following, (int)Math.min(999, item.account.followingCount)));
|
||||||
postsLabel.setText(getResources().getQuantityString(R.plurals.x_posts, (int)(item.account.statusesCount%1000), item.account.statusesCount));
|
postsLabel.setText(getResources().getQuantityString(R.plurals.sk_posts_count_label, (int)(item.account.statusesCount%1000), item.account.statusesCount));
|
||||||
followersCount.setVisibility(item.account.followersCount < 0 ? View.GONE : View.VISIBLE);
|
followersCount.setVisibility(item.account.followersCount < 0 ? View.GONE : View.VISIBLE);
|
||||||
followersLabel.setVisibility(item.account.followersCount < 0 ? View.GONE : View.VISIBLE);
|
followersLabel.setVisibility(item.account.followersCount < 0 ? View.GONE : View.VISIBLE);
|
||||||
followingCount.setVisibility(item.account.followingCount < 0 ? View.GONE : View.VISIBLE);
|
followingCount.setVisibility(item.account.followingCount < 0 ? View.GONE : View.VISIBLE);
|
||||||
followingLabel.setVisibility(item.account.followingCount < 0 ? View.GONE : View.VISIBLE);
|
followingLabel.setVisibility(item.account.followingCount < 0 ? View.GONE : View.VISIBLE);
|
||||||
relationship=relationships.get(item.account.id);
|
relationship=relationships.get(item.account.id);
|
||||||
if(relationship == null || !relationship.followedBy){
|
UiUtils.setExtraTextInfo(getContext(), null, findViewById(R.id.pronouns), true, false, false, item.account);
|
||||||
|
|
||||||
|
if(relationship==null || !relationship.followedBy){
|
||||||
actionWrap.setVisibility(View.GONE);
|
actionWrap.setVisibility(View.GONE);
|
||||||
acceptWrap.setVisibility(View.VISIBLE);
|
acceptWrap.setVisibility(View.VISIBLE);
|
||||||
rejectWrap.setVisibility(View.VISIBLE);
|
rejectWrap.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
// i hate that i wasn't able to do this in xml
|
|
||||||
acceptButton.setCompoundDrawableTintList(acceptButton.getTextColors());
|
acceptButton.setCompoundDrawableTintList(acceptButton.getTextColors());
|
||||||
acceptProgress.setIndeterminateTintList(acceptButton.getTextColors());
|
acceptProgress.setIndeterminateTintList(acceptButton.getTextColors());
|
||||||
rejectButton.setCompoundDrawableTintList(rejectButton.getTextColors());
|
rejectButton.setCompoundDrawableTintList(rejectButton.getTextColors());
|
||||||
rejectProgress.setIndeterminateTintList(rejectButton.getTextColors());
|
rejectProgress.setIndeterminateTintList(rejectButton.getTextColors());
|
||||||
}else if(relationship==null){
|
|
||||||
actionWrap.setVisibility(View.GONE);
|
|
||||||
acceptWrap.setVisibility(View.GONE);
|
|
||||||
rejectWrap.setVisibility(View.GONE);
|
|
||||||
}else{
|
}else{
|
||||||
actionWrap.setVisibility(View.VISIBLE);
|
actionWrap.setVisibility(View.VISIBLE);
|
||||||
acceptWrap.setVisibility(View.GONE);
|
acceptWrap.setVisibility(View.GONE);
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import android.annotation.SuppressLint;
|
|||||||
import android.app.Fragment;
|
import android.app.Fragment;
|
||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
import android.app.assist.AssistContent;
|
import android.app.assist.AssistContent;
|
||||||
|
import android.graphics.drawable.RippleDrawable;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.service.notification.StatusBarNotification;
|
import android.service.notification.StatusBarNotification;
|
||||||
@@ -126,12 +127,31 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
|
|||||||
tabBarWrap=content.findViewById(R.id.tabbar_wrap);
|
tabBarWrap=content.findViewById(R.id.tabbar_wrap);
|
||||||
|
|
||||||
// this one's for the pill haters (https://m3.material.io/components/navigation-bar/overview)
|
// this one's for the pill haters (https://m3.material.io/components/navigation-bar/overview)
|
||||||
if (GlobalUserPreferences.disableM3PillActiveIndicator) {
|
if(GlobalUserPreferences.disableM3PillActiveIndicator){
|
||||||
for(int i=0; i<tabBar.getChildCount(); i++){
|
tabBar.findViewById(R.id.tab_home_pill).setBackground(null);
|
||||||
ViewGroup f=(ViewGroup) tabBar.getChildAt(i);
|
tabBar.findViewById(R.id.tab_search_pill).setBackground(null);
|
||||||
f.setBackgroundResource(R.drawable.bg_tabbar_tab_ripple);
|
tabBar.findViewById(R.id.tab_notifications_pill).setBackground(null);
|
||||||
|
tabBar.findViewById(R.id.tab_profile_pill).setBackgroundResource(R.drawable.bg_tab_profile);
|
||||||
|
|
||||||
|
View[] tabs={
|
||||||
|
tabBar.findViewById(R.id.tab_home),
|
||||||
|
tabBar.findViewById(R.id.tab_search),
|
||||||
|
tabBar.findViewById(R.id.tab_notifications),
|
||||||
|
tabBar.findViewById(R.id.tab_profile)
|
||||||
|
};
|
||||||
|
|
||||||
|
for(View tab : tabs){
|
||||||
|
tab.setBackgroundResource(R.drawable.bg_tabbar_tab_ripple);
|
||||||
|
((RippleDrawable) tab.getBackground())
|
||||||
|
.setRadius(V.dp(GlobalUserPreferences.showNavigationLabels ? 56 : 42));
|
||||||
}
|
}
|
||||||
tabBar.findViewById(R.id.tab_profile).setBackgroundResource(R.drawable.bg_tab_profile);
|
}
|
||||||
|
|
||||||
|
if(!GlobalUserPreferences.showNavigationLabels){
|
||||||
|
tabBar.findViewById(R.id.tab_home_label).setVisibility(View.GONE);
|
||||||
|
tabBar.findViewById(R.id.tab_search_label).setVisibility(View.GONE);
|
||||||
|
tabBar.findViewById(R.id.tab_notifications_label).setVisibility(View.GONE);
|
||||||
|
tabBar.findViewById(R.id.tab_profile_label).setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
tabBarAvatar=tabBar.findViewById(R.id.tab_profile_ava);
|
tabBarAvatar=tabBar.findViewById(R.id.tab_profile_ava);
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ import org.joinmastodon.android.utils.ElevationOnScrollListener;
|
|||||||
import org.joinmastodon.android.utils.ProvidesAssistContent;
|
import org.joinmastodon.android.utils.ProvidesAssistContent;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Comparator;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -336,11 +337,13 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
|||||||
hashtagsMenu.clear();
|
hashtagsMenu.clear();
|
||||||
hashtagsMenu.getItem().setVisible(hashtagsItems.size() > 0);
|
hashtagsMenu.getItem().setVisible(hashtagsItems.size() > 0);
|
||||||
UiUtils.insetPopupMenuIcon(ctx, UiUtils.makeBackItem(hashtagsMenu));
|
UiUtils.insetPopupMenuIcon(ctx, UiUtils.makeBackItem(hashtagsMenu));
|
||||||
hashtagsItems.forEach((id, hashtag) -> {
|
hashtagsItems.entrySet().stream()
|
||||||
MenuItem item = hashtagsMenu.add(Menu.NONE, id, Menu.NONE, hashtag.name);
|
.sorted(Comparator.comparing(x -> x.getValue().name, String.CASE_INSENSITIVE_ORDER))
|
||||||
item.setIcon(R.drawable.ic_fluent_number_symbol_24_regular);
|
.forEach(entry -> {
|
||||||
UiUtils.insetPopupMenuIcon(ctx, item);
|
MenuItem item = hashtagsMenu.add(Menu.NONE, entry.getKey(), Menu.NONE, entry.getValue().name);
|
||||||
});
|
item.setIcon(R.drawable.ic_fluent_number_symbol_24_regular);
|
||||||
|
UiUtils.insetPopupMenuIcon(ctx, item);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateToolbarLogo(){
|
public void updateToolbarLogo(){
|
||||||
|
|||||||
@@ -12,9 +12,10 @@ import android.view.View;
|
|||||||
import com.squareup.otto.Subscribe;
|
import com.squareup.otto.Subscribe;
|
||||||
|
|
||||||
import org.joinmastodon.android.E;
|
import org.joinmastodon.android.E;
|
||||||
|
import org.joinmastodon.android.GlobalUserPreferences;
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
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.EmojiReactionsUpdatedEvent;
|
||||||
import org.joinmastodon.android.events.PollUpdatedEvent;
|
import org.joinmastodon.android.events.PollUpdatedEvent;
|
||||||
import org.joinmastodon.android.events.RemoveAccountPostsEvent;
|
import org.joinmastodon.android.events.RemoveAccountPostsEvent;
|
||||||
import org.joinmastodon.android.events.StatusCountersUpdatedEvent;
|
import org.joinmastodon.android.events.StatusCountersUpdatedEvent;
|
||||||
@@ -22,14 +23,15 @@ import org.joinmastodon.android.model.Notification;
|
|||||||
import org.joinmastodon.android.model.PaginatedResponse;
|
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.EmojiReactionsStatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.displayitems.ExtendedFooterStatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.ExtendedFooterStatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.displayitems.FooterStatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.FooterStatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.displayitems.NotificationHeaderStatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.NotificationHeaderStatusDisplayItem;
|
||||||
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.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;
|
||||||
import org.joinmastodon.android.utils.ElevationOnScrollListener;
|
|
||||||
import org.joinmastodon.android.utils.ObjectIdComparator;
|
import org.joinmastodon.android.utils.ObjectIdComparator;
|
||||||
import org.parceler.Parcels;
|
import org.parceler.Parcels;
|
||||||
|
|
||||||
@@ -43,7 +45,6 @@ import androidx.annotation.NonNull;
|
|||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import me.grishka.appkit.api.SimpleCallback;
|
import me.grishka.appkit.api.SimpleCallback;
|
||||||
import me.grishka.appkit.utils.MergeRecyclerAdapter;
|
import me.grishka.appkit.utils.MergeRecyclerAdapter;
|
||||||
import me.grishka.appkit.views.FragmentRootLinearLayout;
|
|
||||||
|
|
||||||
public class NotificationsListFragment extends BaseStatusListFragment<Notification> {
|
public class NotificationsListFragment extends BaseStatusListFragment<Notification> {
|
||||||
private boolean onlyMentions;
|
private boolean onlyMentions;
|
||||||
@@ -99,7 +100,9 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
|
|||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
if(n.status!=null){
|
if(n.status!=null){
|
||||||
int flags=titleItem==null ? 0 : (StatusDisplayItem.FLAG_NO_FOOTER | StatusDisplayItem.FLAG_INSET); // | StatusDisplayItem.FLAG_NO_HEADER);
|
int flags=titleItem==null ? 0 : (StatusDisplayItem.FLAG_NO_FOOTER | StatusDisplayItem.FLAG_INSET | StatusDisplayItem.FLAG_NO_EMOJI_REACTIONS); // | StatusDisplayItem.FLAG_NO_HEADER);
|
||||||
|
if (GlobalUserPreferences.spectatorMode)
|
||||||
|
flags |= StatusDisplayItem.FLAG_NO_FOOTER;
|
||||||
ArrayList<StatusDisplayItem> items=StatusDisplayItem.buildItems(this, n.status, accountID, n, knownAccounts, null, flags);
|
ArrayList<StatusDisplayItem> items=StatusDisplayItem.buildItems(this, n.status, accountID, n, knownAccounts, null, flags);
|
||||||
if(titleItem!=null)
|
if(titleItem!=null)
|
||||||
items.add(0, titleItem);
|
items.add(0, titleItem);
|
||||||
@@ -244,8 +247,7 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
|
|||||||
@Subscribe
|
@Subscribe
|
||||||
public void onStatusCountersUpdated(StatusCountersUpdatedEvent ev){
|
public void onStatusCountersUpdated(StatusCountersUpdatedEvent ev){
|
||||||
for(Notification n:data){
|
for(Notification n:data){
|
||||||
if (n.status == null) continue;
|
if(n.status!=null && n.status.getContentStatus().id.equals(ev.id)){
|
||||||
if(n.status.getContentStatus().id.equals(ev.id)){
|
|
||||||
n.status.getContentStatus().update(ev);
|
n.status.getContentStatus().update(ev);
|
||||||
AccountSessionManager.get(accountID).getCacheController().updateNotification(n);
|
AccountSessionManager.get(accountID).getCacheController().updateNotification(n);
|
||||||
for(int i=0;i<list.getChildCount();i++){
|
for(int i=0;i<list.getChildCount();i++){
|
||||||
@@ -259,8 +261,31 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for(Notification n:preloadedData){
|
for(Notification n:preloadedData){
|
||||||
if (n.status == null) continue;
|
if(n.status!=null && n.status.getContentStatus().id.equals(ev.id)){
|
||||||
if(n.status.getContentStatus().id.equals(ev.id)){
|
n.status.getContentStatus().update(ev);
|
||||||
|
AccountSessionManager.get(accountID).getCacheController().updateNotification(n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void onEmojiReactionsChanged(EmojiReactionsUpdatedEvent ev){
|
||||||
|
for(Notification n : data){
|
||||||
|
if(n.status!=null && n.status.getContentStatus().id.equals(ev.id)){
|
||||||
|
n.status.getContentStatus().update(ev);
|
||||||
|
AccountSessionManager.get(accountID).getCacheController().updateNotification(n);
|
||||||
|
for(int i=0; i<list.getChildCount(); i++){
|
||||||
|
RecyclerView.ViewHolder holder=list.getChildViewHolder(list.getChildAt(i));
|
||||||
|
if(holder instanceof EmojiReactionsStatusDisplayItem.Holder reactions && reactions.getItem().status==n.status.getContentStatus() && ev.viewHolder!=holder){
|
||||||
|
reactions.rebind();
|
||||||
|
}else if(holder instanceof TextStatusDisplayItem.Holder text && text.getItem().parentID.equals(n.getID())){
|
||||||
|
text.rebind();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(Notification n : preloadedData){
|
||||||
|
if(n.status!=null && n.status.getContentStatus().id.equals(ev.id)){
|
||||||
n.status.getContentStatus().update(ev);
|
n.status.getContentStatus().update(ev);
|
||||||
AccountSessionManager.get(accountID).getCacheController().updateNotification(n);
|
AccountSessionManager.get(accountID).getCacheController().updateNotification(n);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
package org.joinmastodon.android.fragments;
|
package org.joinmastodon.android.fragments;
|
||||||
|
|
||||||
import static org.joinmastodon.android.fragments.ProfileAboutFragment.MAX_FIELDS;
|
|
||||||
|
|
||||||
import android.animation.Animator;
|
import android.animation.Animator;
|
||||||
import android.animation.AnimatorListenerAdapter;
|
import android.animation.AnimatorListenerAdapter;
|
||||||
import android.animation.AnimatorSet;
|
import android.animation.AnimatorSet;
|
||||||
import android.animation.ObjectAnimator;
|
import android.animation.ObjectAnimator;
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.Fragment;
|
import android.app.Fragment;
|
||||||
import android.app.assist.AssistContent;
|
import android.app.assist.AssistContent;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.res.ColorStateList;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.content.res.TypedArray;
|
import android.content.res.TypedArray;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
@@ -133,6 +133,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
private CoverImageView cover;
|
private CoverImageView cover;
|
||||||
private View avatarBorder;
|
private View avatarBorder;
|
||||||
private TextView name, username, bio, followersCount, followersLabel, followingCount, followingLabel;
|
private TextView name, username, bio, followersCount, followersLabel, followingCount, followingLabel;
|
||||||
|
private ImageView lockIcon, botIcon;
|
||||||
private ProgressBarButton actionButton, notifyButton;
|
private ProgressBarButton actionButton, notifyButton;
|
||||||
private ViewPager2 pager;
|
private ViewPager2 pager;
|
||||||
private NestedRecyclerScrollView scrollView;
|
private NestedRecyclerScrollView scrollView;
|
||||||
@@ -175,6 +176,8 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
private MenuItem editSaveMenuItem;
|
private MenuItem editSaveMenuItem;
|
||||||
private boolean savingEdits;
|
private boolean savingEdits;
|
||||||
|
|
||||||
|
private int maxFields = ProfileAboutFragment.MAX_FIELDS;
|
||||||
|
|
||||||
// from ProfileAboutFragment
|
// from ProfileAboutFragment
|
||||||
public UsableRecyclerView list;
|
public UsableRecyclerView list;
|
||||||
private AboutAdapter adapter;
|
private AboutAdapter adapter;
|
||||||
@@ -200,6 +203,9 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
loaded=true;
|
loaded=true;
|
||||||
if(!isOwnProfile)
|
if(!isOwnProfile)
|
||||||
loadRelationship();
|
loadRelationship();
|
||||||
|
else if (isInstanceAkkoma()) {
|
||||||
|
maxFields = getInstance().get().pleroma.metadata.fieldsLimits.maxFields;
|
||||||
|
}
|
||||||
}else{
|
}else{
|
||||||
profileAccountID=getArguments().getString("profileAccountID");
|
profileAccountID=getArguments().getString("profileAccountID");
|
||||||
if(!getArguments().getBoolean("noAutoLoad", false))
|
if(!getArguments().getBoolean("noAutoLoad", false))
|
||||||
@@ -227,6 +233,8 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
avatarBorder=content.findViewById(R.id.avatar_border);
|
avatarBorder=content.findViewById(R.id.avatar_border);
|
||||||
name=content.findViewById(R.id.name);
|
name=content.findViewById(R.id.name);
|
||||||
username=content.findViewById(R.id.username);
|
username=content.findViewById(R.id.username);
|
||||||
|
lockIcon=content.findViewById(R.id.lock_icon);
|
||||||
|
botIcon=content.findViewById(R.id.bot_icon);
|
||||||
bio=content.findViewById(R.id.bio);
|
bio=content.findViewById(R.id.bio);
|
||||||
followersCount=content.findViewById(R.id.followers_count);
|
followersCount=content.findViewById(R.id.followers_count);
|
||||||
followersLabel=content.findViewById(R.id.followers_label);
|
followersLabel=content.findViewById(R.id.followers_label);
|
||||||
@@ -255,6 +263,8 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
list=content.findViewById(R.id.metadata);
|
list=content.findViewById(R.id.metadata);
|
||||||
rolesView=content.findViewById(R.id.roles);
|
rolesView=content.findViewById(R.id.roles);
|
||||||
|
|
||||||
|
avatarBorder.setOutlineProvider(OutlineProviders.roundedRect(26));
|
||||||
|
avatarBorder.setClipToOutline(true);
|
||||||
avatar.setOutlineProvider(OutlineProviders.roundedRect(24));
|
avatar.setOutlineProvider(OutlineProviders.roundedRect(24));
|
||||||
avatar.setClipToOutline(true);
|
avatar.setClipToOutline(true);
|
||||||
|
|
||||||
@@ -342,7 +352,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
followersBtn.setOnClickListener(this::onFollowersOrFollowingClick);
|
followersBtn.setOnClickListener(this::onFollowersOrFollowingClick);
|
||||||
followingBtn.setOnClickListener(this::onFollowersOrFollowingClick);
|
followingBtn.setOnClickListener(this::onFollowersOrFollowingClick);
|
||||||
|
|
||||||
username.setOnClickListener(v->{
|
content.findViewById(R.id.username_wrap).setOnClickListener(v->{
|
||||||
try {
|
try {
|
||||||
new GetInstance()
|
new GetInstance()
|
||||||
.setCallback(new Callback<>(){
|
.setCallback(new Callback<>(){
|
||||||
@@ -363,7 +373,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
.execRemote(Uri.parse(account.url).getHost());
|
.execRemote(Uri.parse(account.url).getHost());
|
||||||
} catch (NullPointerException ignored) {
|
} catch (NullPointerException ignored) {
|
||||||
// maybe the url was malformed?
|
// maybe the url was malformed?
|
||||||
Toast.makeText(getContext(), R.string.error, Toast.LENGTH_SHORT);
|
Toast.makeText(getContext(), R.string.error, Toast.LENGTH_SHORT).show();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -587,6 +597,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("SetTextI18n")
|
||||||
private void bindHeaderView(){
|
private void bindHeaderView(){
|
||||||
setTitle(account.displayName);
|
setTitle(account.displayName);
|
||||||
setSubtitle(getResources().getQuantityString(R.plurals.x_posts, (int)(account.statusesCount%1000), account.statusesCount));
|
setSubtitle(getResources().getQuantityString(R.plurals.x_posts, (int)(account.statusesCount%1000), account.statusesCount));
|
||||||
@@ -619,19 +630,15 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
String acct = ((isSelf || account.isRemote)
|
String acct = ((isSelf || account.isRemote)
|
||||||
? account.getFullyQualifiedName()
|
? account.getFullyQualifiedName()
|
||||||
: account.acct);
|
: account.acct);
|
||||||
if(account.locked){
|
|
||||||
ssb=new SpannableStringBuilder("@");
|
username.setText('@'+acct);
|
||||||
ssb.append(acct);
|
|
||||||
ssb.append(" ");
|
lockIcon.setVisibility(account.locked ? View.VISIBLE : View.GONE);
|
||||||
Drawable lock=username.getResources().getDrawable(R.drawable.ic_lock, getActivity().getTheme()).mutate();
|
lockIcon.setImageTintList(ColorStateList.valueOf(username.getCurrentTextColor()));
|
||||||
lock.setBounds(0, 0, lock.getIntrinsicWidth(), lock.getIntrinsicHeight());
|
|
||||||
lock.setTint(username.getCurrentTextColor());
|
botIcon.setVisibility(account.bot ? View.VISIBLE : View.GONE);
|
||||||
ssb.append(getString(R.string.manually_approves_followers), new ImageSpan(lock, ImageSpan.ALIGN_BASELINE), 0);
|
botIcon.setImageTintList(ColorStateList.valueOf(username.getCurrentTextColor()));
|
||||||
username.setText(ssb);
|
|
||||||
}else{
|
|
||||||
// noinspection SetTextI18n
|
|
||||||
username.setText('@'+acct);
|
|
||||||
}
|
|
||||||
CharSequence parsedBio=HtmlParser.parse(account.note, account.emojis, Collections.emptyList(), Collections.emptyList(), accountID);
|
CharSequence parsedBio=HtmlParser.parse(account.note, account.emojis, Collections.emptyList(), Collections.emptyList(), accountID);
|
||||||
if(TextUtils.isEmpty(parsedBio)){
|
if(TextUtils.isEmpty(parsedBio)){
|
||||||
bio.setVisibility(View.GONE);
|
bio.setVisibility(View.GONE);
|
||||||
@@ -1054,7 +1061,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
actionButton.setText(R.string.save_changes);
|
actionButton.setText(R.string.save_changes);
|
||||||
pager.setVisibility(View.GONE);
|
pager.setVisibility(View.GONE);
|
||||||
tabbar.setVisibility(View.GONE);
|
tabbar.setVisibility(View.GONE);
|
||||||
Drawable overlay=getResources().getDrawable(R.drawable.edit_avatar_overlay).mutate();
|
Drawable overlay=getResources().getDrawable(R.drawable.edit_avatar_overlay, getActivity().getTheme()).mutate();
|
||||||
avatar.setForeground(overlay);
|
avatar.setForeground(overlay);
|
||||||
updateMetadataHeight();
|
updateMetadataHeight();
|
||||||
|
|
||||||
@@ -1073,6 +1080,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
);
|
);
|
||||||
|
|
||||||
name.setVisibility(View.GONE);
|
name.setVisibility(View.GONE);
|
||||||
|
rolesView.setVisibility(View.GONE);
|
||||||
username.setVisibility(View.GONE);
|
username.setVisibility(View.GONE);
|
||||||
bio.setVisibility(View.GONE);
|
bio.setVisibility(View.GONE);
|
||||||
countersLayout.setVisibility(View.GONE);
|
countersLayout.setVisibility(View.GONE);
|
||||||
@@ -1121,6 +1129,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
nameEditWrap.setVisibility(View.GONE);
|
nameEditWrap.setVisibility(View.GONE);
|
||||||
bioEditWrap.setVisibility(View.GONE);
|
bioEditWrap.setVisibility(View.GONE);
|
||||||
name.setVisibility(View.VISIBLE);
|
name.setVisibility(View.VISIBLE);
|
||||||
|
rolesView.setVisibility(View.VISIBLE);
|
||||||
username.setVisibility(View.VISIBLE);
|
username.setVisibility(View.VISIBLE);
|
||||||
bio.setVisibility(View.VISIBLE);
|
bio.setVisibility(View.VISIBLE);
|
||||||
countersLayout.setVisibility(View.VISIBLE);
|
countersLayout.setVisibility(View.VISIBLE);
|
||||||
@@ -1385,7 +1394,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
public int getItemCount(){
|
public int getItemCount(){
|
||||||
if(isInEditMode){
|
if(isInEditMode){
|
||||||
int size=fields.size();
|
int size=fields.size();
|
||||||
if(size<MAX_FIELDS)
|
if(size<maxFields)
|
||||||
size++;
|
size++;
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
@@ -1421,16 +1430,14 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class AboutViewHolder extends BaseViewHolder implements ImageLoaderViewHolder {
|
private class AboutViewHolder extends BaseViewHolder implements ImageLoaderViewHolder{
|
||||||
private final TextView title;
|
private final TextView title;
|
||||||
private final LinkedTextView value;
|
private final LinkedTextView value;
|
||||||
// private final ImageView verifiedIcon;
|
|
||||||
|
|
||||||
public AboutViewHolder(){
|
public AboutViewHolder(){
|
||||||
super(R.layout.item_profile_about);
|
super(R.layout.item_profile_about);
|
||||||
title=findViewById(R.id.title);
|
title=findViewById(R.id.title);
|
||||||
value=findViewById(R.id.value);
|
value=findViewById(R.id.value);
|
||||||
// verifiedIcon=findViewById(R.id.verified_icon);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -1438,7 +1445,18 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
super.onBind(item);
|
super.onBind(item);
|
||||||
title.setText(item.parsedName);
|
title.setText(item.parsedName);
|
||||||
value.setText(item.parsedValue);
|
value.setText(item.parsedValue);
|
||||||
// verifiedIcon.setVisibility(item.verifiedAt!=null ? View.VISIBLE : View.GONE);
|
if(item.verifiedAt!=null){
|
||||||
|
int textColor=UiUtils.isDarkTheme() ? 0xFF89bb9c : 0xFF5b8e63;
|
||||||
|
value.setTextColor(textColor);
|
||||||
|
value.setLinkTextColor(textColor);
|
||||||
|
Drawable check=getResources().getDrawable(R.drawable.ic_fluent_checkmark_starburst_20_regular, getActivity().getTheme()).mutate();
|
||||||
|
check.setTint(textColor);
|
||||||
|
value.setCompoundDrawablesRelativeWithIntrinsicBounds(null, null, check, null);
|
||||||
|
}else{
|
||||||
|
value.setTextColor(UiUtils.getThemeColor(getActivity(), android.R.attr.textColorPrimary));
|
||||||
|
value.setLinkTextColor(UiUtils.getThemeColor(getActivity(), android.R.attr.colorAccent));
|
||||||
|
value.setCompoundDrawables(null, null, null, null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -1510,7 +1528,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
@Override
|
@Override
|
||||||
public void onClick(){
|
public void onClick(){
|
||||||
fields.add(new AccountField());
|
fields.add(new AccountField());
|
||||||
if(fields.size()==MAX_FIELDS){ // replace this row with new row
|
if(fields.size()==maxFields){ // replace this row with new row
|
||||||
adapter.notifyItemChanged(fields.size()-1);
|
adapter.notifyItemChanged(fields.size()-1);
|
||||||
}else{
|
}else{
|
||||||
adapter.notifyItemInserted(fields.size()-1);
|
adapter.notifyItemInserted(fields.size()-1);
|
||||||
|
|||||||
@@ -80,7 +80,10 @@ public class ScheduledStatusListFragment extends BaseStatusListFragment<Schedule
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected List<StatusDisplayItem> buildDisplayItems(ScheduledStatus s) {
|
protected List<StatusDisplayItem> buildDisplayItems(ScheduledStatus s) {
|
||||||
return StatusDisplayItem.buildItems(this, s.toStatus(), accountID, s, knownAccounts, false, false, true, null);
|
return StatusDisplayItem.buildItems(this, s.toStatus(), accountID, s, knownAccounts, null,
|
||||||
|
StatusDisplayItem.FLAG_NO_EMOJI_REACTIONS |
|
||||||
|
StatusDisplayItem.FLAG_NO_FOOTER |
|
||||||
|
StatusDisplayItem.FLAG_NO_TRANSLATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ public class StatusEditHistoryFragment extends StatusListFragment{
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected List<StatusDisplayItem> buildDisplayItems(Status s){
|
protected List<StatusDisplayItem> buildDisplayItems(Status s){
|
||||||
List<StatusDisplayItem> items=StatusDisplayItem.buildItems(this, s, accountID, s, knownAccounts, null, StatusDisplayItem.FLAG_NO_FOOTER | StatusDisplayItem.FLAG_INSET);
|
List<StatusDisplayItem> items=StatusDisplayItem.buildItems(this, s, accountID, s, knownAccounts, null, StatusDisplayItem.FLAG_NO_FOOTER | StatusDisplayItem.FLAG_INSET | StatusDisplayItem.FLAG_NO_EMOJI_REACTIONS);
|
||||||
int idx=data.indexOf(s);
|
int idx=data.indexOf(s);
|
||||||
if(idx>=0){
|
if(idx>=0){
|
||||||
String date=UiUtils.DATE_TIME_FORMATTER.format(s.createdAt.atZone(ZoneId.systemDefault()));
|
String date=UiUtils.DATE_TIME_FORMATTER.format(s.createdAt.atZone(ZoneId.systemDefault()));
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package org.joinmastodon.android.fragments;
|
package org.joinmastodon.android.fragments;
|
||||||
|
|
||||||
|
import static org.joinmastodon.android.api.session.AccountLocalPreferences.ShowEmojiReactions.ONLY_OPENED;
|
||||||
|
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
|
||||||
@@ -7,7 +9,9 @@ import com.squareup.otto.Subscribe;
|
|||||||
|
|
||||||
import org.joinmastodon.android.E;
|
import org.joinmastodon.android.E;
|
||||||
import org.joinmastodon.android.GlobalUserPreferences;
|
import org.joinmastodon.android.GlobalUserPreferences;
|
||||||
|
import org.joinmastodon.android.api.session.AccountLocalPreferences;
|
||||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||||
|
import org.joinmastodon.android.events.EmojiReactionsUpdatedEvent;
|
||||||
import org.joinmastodon.android.events.PollUpdatedEvent;
|
import org.joinmastodon.android.events.PollUpdatedEvent;
|
||||||
import org.joinmastodon.android.events.RemoveAccountPostsEvent;
|
import org.joinmastodon.android.events.RemoveAccountPostsEvent;
|
||||||
import org.joinmastodon.android.events.StatusCountersUpdatedEvent;
|
import org.joinmastodon.android.events.StatusCountersUpdatedEvent;
|
||||||
@@ -16,9 +20,11 @@ import org.joinmastodon.android.events.StatusDeletedEvent;
|
|||||||
import org.joinmastodon.android.events.StatusUpdatedEvent;
|
import org.joinmastodon.android.events.StatusUpdatedEvent;
|
||||||
import org.joinmastodon.android.model.FilterContext;
|
import org.joinmastodon.android.model.FilterContext;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
|
import org.joinmastodon.android.ui.displayitems.EmojiReactionsStatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.displayitems.ExtendedFooterStatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.ExtendedFooterStatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.displayitems.FooterStatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.FooterStatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
|
||||||
|
import org.joinmastodon.android.ui.displayitems.TextStatusDisplayItem;
|
||||||
import org.parceler.Parcels;
|
import org.parceler.Parcels;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -33,9 +39,14 @@ public abstract class StatusListFragment extends BaseStatusListFragment<Status>
|
|||||||
protected EventListener eventListener=new EventListener();
|
protected EventListener eventListener=new EventListener();
|
||||||
|
|
||||||
protected List<StatusDisplayItem> buildDisplayItems(Status s){
|
protected List<StatusDisplayItem> buildDisplayItems(Status s){
|
||||||
boolean addFooter = !GlobalUserPreferences.spectatorMode ||
|
boolean isMainThreadStatus = this instanceof ThreadFragment t && s.id.equals(t.mainStatus.id);
|
||||||
(this instanceof ThreadFragment t && s.id.equals(t.mainStatus.id));
|
int flags = 0;
|
||||||
return StatusDisplayItem.buildItems(this, s, accountID, s, knownAccounts, getFilterContext(), addFooter ? 0 : StatusDisplayItem.FLAG_NO_FOOTER);
|
AccountLocalPreferences lp=getLocalPrefs();
|
||||||
|
if (GlobalUserPreferences.spectatorMode)
|
||||||
|
flags |= StatusDisplayItem.FLAG_NO_FOOTER;
|
||||||
|
if (!lp.emojiReactionsEnabled || lp.showEmojiReactions==ONLY_OPENED)
|
||||||
|
flags |= StatusDisplayItem.FLAG_NO_EMOJI_REACTIONS;
|
||||||
|
return StatusDisplayItem.buildItems(this, s, accountID, s, knownAccounts, getFilterContext(), isMainThreadStatus ? 0 : flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract FilterContext getFilterContext();
|
protected abstract FilterContext getFilterContext();
|
||||||
@@ -230,6 +241,30 @@ public abstract class StatusListFragment extends BaseStatusListFragment<Status>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void onEmojiReactionsChanged(EmojiReactionsUpdatedEvent ev){
|
||||||
|
for(Status s:data){
|
||||||
|
if(s.getContentStatus().id.equals(ev.id)){
|
||||||
|
s.getContentStatus().update(ev);
|
||||||
|
AccountSessionManager.get(accountID).getCacheController().updateStatus(s);
|
||||||
|
for(int i=0;i<list.getChildCount();i++){
|
||||||
|
RecyclerView.ViewHolder holder=list.getChildViewHolder(list.getChildAt(i));
|
||||||
|
if(holder instanceof EmojiReactionsStatusDisplayItem.Holder reactions && reactions.getItem().status==s.getContentStatus() && ev.viewHolder!=holder){
|
||||||
|
reactions.rebind();
|
||||||
|
}else if(holder instanceof TextStatusDisplayItem.Holder text && text.getItem().parentID.equals(s.getID())){
|
||||||
|
text.rebind();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(Status s:preloadedData){
|
||||||
|
if(s.getContentStatus().id.equals(ev.id)){
|
||||||
|
s.getContentStatus().update(ev);
|
||||||
|
AccountSessionManager.get(accountID).getCacheController().updateStatus(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Subscribe
|
@Subscribe
|
||||||
public void onStatusDeleted(StatusDeletedEvent ev){
|
public void onStatusDeleted(StatusDeletedEvent ev){
|
||||||
if(!ev.accountID.equals(accountID))
|
if(!ev.accountID.equals(accountID))
|
||||||
|
|||||||
@@ -417,6 +417,10 @@ public class ThreadFragment extends StatusListFragment implements ProvidesAssist
|
|||||||
adapter.notifyDataSetChanged();
|
adapter.notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Status getMainStatus(){
|
||||||
|
return mainStatus;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isItemEnabled(String id){
|
public boolean isItemEnabled(String id){
|
||||||
return !id.equals(mainStatus.id) || !mainStatus.filterRevealed;
|
return !id.equals(mainStatus.id) || !mainStatus.filterRevealed;
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import org.joinmastodon.android.model.viewmodel.AccountViewModel;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import me.grishka.appkit.api.Callback;
|
import me.grishka.appkit.api.Callback;
|
||||||
@@ -136,6 +137,10 @@ public abstract class PaginatedAccountListFragment<T> extends BaseAccountListFra
|
|||||||
List<AccountViewModel> items = result.stream()
|
List<AccountViewModel> items = result.stream()
|
||||||
.filter(a -> d.size() > 1000 || d.stream()
|
.filter(a -> d.size() > 1000 || d.stream()
|
||||||
.noneMatch(i -> i.account.url.equals(a.url)))
|
.noneMatch(i -> i.account.url.equals(a.url)))
|
||||||
|
.peek(account ->{
|
||||||
|
if (account.getDomainFromURL().equals(getRemoteDomain()))
|
||||||
|
account.acct=account.getFullyQualifiedName();
|
||||||
|
})
|
||||||
.map(a->new AccountViewModel(a, accountID))
|
.map(a->new AccountViewModel(a, accountID))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,97 @@
|
|||||||
|
package org.joinmastodon.android.fragments.account_list;
|
||||||
|
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.text.SpannableStringBuilder;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.R;
|
||||||
|
import org.joinmastodon.android.api.requests.statuses.PleromaGetStatusReactions;
|
||||||
|
import org.joinmastodon.android.model.Emoji;
|
||||||
|
import org.joinmastodon.android.model.EmojiReaction;
|
||||||
|
import org.joinmastodon.android.model.viewmodel.AccountViewModel;
|
||||||
|
import org.joinmastodon.android.ui.text.HtmlParser;
|
||||||
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import me.grishka.appkit.api.ErrorResponse;
|
||||||
|
import me.grishka.appkit.api.SimpleCallback;
|
||||||
|
|
||||||
|
public class StatusEmojiReactionsListFragment extends BaseAccountListFragment {
|
||||||
|
private String id;
|
||||||
|
private String emojiName;
|
||||||
|
private String url;
|
||||||
|
private int count;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState){
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
id = getArguments().getString("statusID");
|
||||||
|
emojiName = getArguments().getString("emoji");
|
||||||
|
url = getArguments().getString("url");
|
||||||
|
count = getArguments().getInt("count");
|
||||||
|
|
||||||
|
SpannableStringBuilder title = new SpannableStringBuilder(getResources().getQuantityString(R.plurals.sk_users_reacted_with, count,
|
||||||
|
count, url == null ? emojiName : ":"+emojiName+":"));
|
||||||
|
if (url != null) {
|
||||||
|
Emoji emoji = new Emoji();
|
||||||
|
emoji.shortcode = emojiName;
|
||||||
|
emoji.url = url;
|
||||||
|
HtmlParser.parseCustomEmoji(title, Collections.singletonList(emoji));
|
||||||
|
}
|
||||||
|
setTitle(title);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||||
|
super.onViewCreated(view, savedInstanceState);
|
||||||
|
if (url != null) {
|
||||||
|
UiUtils.loadCustomEmojiInTextView(toolbarTitleView);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dataLoaded() {
|
||||||
|
super.dataLoaded();
|
||||||
|
footerProgress.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doLoadData(int offset, int count){
|
||||||
|
currentRequest = new PleromaGetStatusReactions(id, emojiName)
|
||||||
|
.setCallback(new SimpleCallback<>(StatusEmojiReactionsListFragment.this){
|
||||||
|
@Override
|
||||||
|
public void onSuccess(List<EmojiReaction> result) {
|
||||||
|
if (getActivity() == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
List<AccountViewModel> items = result.get(0).accounts.stream()
|
||||||
|
.map(a -> new AccountViewModel(a, accountID))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
onDataLoaded(items);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(ErrorResponse error) {
|
||||||
|
super.onError(error);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.exec(accountID);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume(){
|
||||||
|
super.onResume();
|
||||||
|
if(!loaded && !dataLoading)
|
||||||
|
loadData();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Uri getWebUri(Uri.Builder base) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -197,7 +197,7 @@ public class DiscoverAccountsFragment extends MastodonRecyclerFragment<DiscoverA
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class AccountViewHolder extends BindableViewHolder<AccountWrapper> implements ImageLoaderViewHolder, UsableRecyclerView.Clickable{
|
private class AccountViewHolder extends BindableViewHolder<AccountWrapper> implements ImageLoaderViewHolder, UsableRecyclerView.DisableableClickable{
|
||||||
private final ImageView cover, avatar;
|
private final ImageView cover, avatar;
|
||||||
private final TextView name, username, bio, followersCount, followingCount, postsCount, followersLabel, followingLabel, postsLabel;
|
private final TextView name, username, bio, followersCount, followingCount, postsCount, followersLabel, followingLabel, postsLabel;
|
||||||
private final ProgressBarButton actionButton;
|
private final ProgressBarButton actionButton;
|
||||||
@@ -223,13 +223,22 @@ public class DiscoverAccountsFragment extends MastodonRecyclerFragment<DiscoverA
|
|||||||
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));
|
avatar.setOutlineProvider(OutlineProviders.roundedRect(15));
|
||||||
itemView.setClipToOutline(true);
|
|
||||||
avatar.setOutlineProvider(OutlineProviders.roundedRect(12));
|
|
||||||
avatar.setClipToOutline(true);
|
avatar.setClipToOutline(true);
|
||||||
cover.setOutlineProvider(OutlineProviders.roundedRect(3));
|
View border=findViewById(R.id.avatar_border);
|
||||||
|
border.setOutlineProvider(OutlineProviders.roundedRect(17));
|
||||||
|
border.setClipToOutline(true);
|
||||||
|
cover.setOutlineProvider(OutlineProviders.roundedRect(9));
|
||||||
cover.setClipToOutline(true);
|
cover.setClipToOutline(true);
|
||||||
|
itemView.setOutlineProvider(OutlineProviders.roundedRect(12));
|
||||||
|
itemView.setClipToOutline(true);
|
||||||
actionButton.setOnClickListener(this::onActionButtonClick);
|
actionButton.setOnClickListener(this::onActionButtonClick);
|
||||||
|
itemView.setOnClickListener(v->this.onClick());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabled(){
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -242,12 +251,14 @@ public class DiscoverAccountsFragment extends MastodonRecyclerFragment<DiscoverA
|
|||||||
postsCount.setText(UiUtils.abbreviateNumber(item.account.statusesCount));
|
postsCount.setText(UiUtils.abbreviateNumber(item.account.statusesCount));
|
||||||
followersLabel.setText(getResources().getQuantityString(R.plurals.followers, (int)Math.min(999, item.account.followersCount)));
|
followersLabel.setText(getResources().getQuantityString(R.plurals.followers, (int)Math.min(999, item.account.followersCount)));
|
||||||
followingLabel.setText(getResources().getQuantityString(R.plurals.following, (int)Math.min(999, item.account.followingCount)));
|
followingLabel.setText(getResources().getQuantityString(R.plurals.following, (int)Math.min(999, item.account.followingCount)));
|
||||||
postsLabel.setText(getResources().getQuantityString(R.plurals.x_posts, (int)(item.account.statusesCount%1000), item.account.statusesCount));
|
postsLabel.setText(getResources().getQuantityString(R.plurals.sk_posts_count_label, (int)(item.account.statusesCount%1000), item.account.statusesCount));
|
||||||
followersCount.setVisibility(item.account.followersCount < 0 ? View.GONE : View.VISIBLE);
|
followersCount.setVisibility(item.account.followersCount < 0 ? View.GONE : View.VISIBLE);
|
||||||
followersLabel.setVisibility(item.account.followersCount < 0 ? View.GONE : View.VISIBLE);
|
followersLabel.setVisibility(item.account.followersCount < 0 ? View.GONE : View.VISIBLE);
|
||||||
followingCount.setVisibility(item.account.followingCount < 0 ? View.GONE : View.VISIBLE);
|
followingCount.setVisibility(item.account.followingCount < 0 ? View.GONE : View.VISIBLE);
|
||||||
followingLabel.setVisibility(item.account.followingCount < 0 ? View.GONE : View.VISIBLE);
|
followingLabel.setVisibility(item.account.followingCount < 0 ? View.GONE : View.VISIBLE);
|
||||||
relationship=relationships.get(item.account.id);
|
relationship=relationships.get(item.account.id);
|
||||||
|
UiUtils.setExtraTextInfo(getContext(), null, findViewById(R.id.pronouns), true, false, false, item.account);
|
||||||
|
|
||||||
if(relationship==null){
|
if(relationship==null){
|
||||||
actionWrap.setVisibility(View.GONE);
|
actionWrap.setVisibility(View.GONE);
|
||||||
}else{
|
}else{
|
||||||
|
|||||||
@@ -54,6 +54,8 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
|
|||||||
private String accountID;
|
private String accountID;
|
||||||
private String currentQuery;
|
private String currentQuery;
|
||||||
|
|
||||||
|
private boolean disableDiscover;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState){
|
public void onCreate(Bundle savedInstanceState){
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
@@ -155,6 +157,7 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
disableDiscover=getArguments().getBoolean("disableDiscover");
|
||||||
searchView=view.findViewById(R.id.search_fragment);
|
searchView=view.findViewById(R.id.search_fragment);
|
||||||
if(searchFragment==null){
|
if(searchFragment==null){
|
||||||
searchFragment=new SearchFragment();
|
searchFragment=new SearchFragment();
|
||||||
@@ -170,8 +173,9 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
|
|||||||
searchBack.setOnClickListener(v->{
|
searchBack.setOnClickListener(v->{
|
||||||
if(searchActive) exitSearch(); else openSearch();
|
if(searchActive) exitSearch(); else openSearch();
|
||||||
});
|
});
|
||||||
if(searchActive){
|
if(searchActive) searchBack.setImageResource(R.drawable.ic_fluent_arrow_left_24_regular);
|
||||||
searchBack.setImageResource(R.drawable.ic_fluent_arrow_left_24_regular);
|
else searchBack.setEnabled(false);
|
||||||
|
if(searchActive || disableDiscover){
|
||||||
pager.setVisibility(View.GONE);
|
pager.setVisibility(View.GONE);
|
||||||
tabLayout.setVisibility(View.GONE);
|
tabLayout.setVisibility(View.GONE);
|
||||||
searchView.setVisibility(View.VISIBLE);
|
searchView.setVisibility(View.VISIBLE);
|
||||||
@@ -211,8 +215,8 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void loadData(){
|
public void loadData(){
|
||||||
if(postsFragment!=null && !postsFragment.loaded && !postsFragment.dataLoading)
|
if(hashtagsFragment!=null && !hashtagsFragment.loaded && !hashtagsFragment.dataLoading)
|
||||||
postsFragment.loadData();
|
hashtagsFragment.loadData();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void enterSearch(){
|
private void enterSearch(){
|
||||||
@@ -232,15 +236,18 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
|
|||||||
if(!searchActive)
|
if(!searchActive)
|
||||||
return;
|
return;
|
||||||
searchActive=false;
|
searchActive=false;
|
||||||
pager.setVisibility(View.VISIBLE);
|
|
||||||
tabLayout.setVisibility(View.VISIBLE);
|
|
||||||
searchView.setVisibility(View.GONE);
|
|
||||||
searchText.setText(R.string.sk_search_fediverse);
|
searchText.setText(R.string.sk_search_fediverse);
|
||||||
searchBack.setImageResource(R.drawable.ic_fluent_search_24_regular);
|
searchBack.setImageResource(R.drawable.ic_fluent_search_24_regular);
|
||||||
searchBack.setEnabled(false);
|
searchBack.setEnabled(false);
|
||||||
searchBack.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
|
searchBack.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
|
||||||
tabsDivider.setVisibility(View.VISIBLE);
|
|
||||||
currentQuery=null;
|
currentQuery=null;
|
||||||
|
searchFragment.clear();
|
||||||
|
|
||||||
|
if(disableDiscover) return;
|
||||||
|
pager.setVisibility(View.VISIBLE);
|
||||||
|
tabLayout.setVisibility(View.VISIBLE);
|
||||||
|
searchView.setVisibility(View.GONE);
|
||||||
|
tabsDivider.setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Fragment getFragmentForPage(int page){
|
private Fragment getFragmentForPage(int page){
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import android.os.Bundle;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.inputmethod.InputMethodManager;
|
import android.view.inputmethod.InputMethodManager;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.GlobalUserPreferences;
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.requests.search.GetSearchResults;
|
import org.joinmastodon.android.api.requests.search.GetSearchResults;
|
||||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||||
@@ -54,7 +55,7 @@ public class SearchFragment extends BaseStatusListFragment<SearchResult>{
|
|||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.N)
|
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.N)
|
||||||
setRetainInstance(true);
|
setRetainInstance(true);
|
||||||
setEmptyText(R.string.no_search_results);
|
setEmptyText(R.string.sk_recent_searches_placeholder);
|
||||||
loadData();
|
loadData();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,13 +174,16 @@ public class SearchFragment extends BaseStatusListFragment<SearchResult>{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setQuery(String q, SearchResult.Type filter){
|
public void setQuery(String q, SearchResult.Type filter){
|
||||||
if(q.isBlank())
|
if(q.isBlank()) {
|
||||||
|
setEmptyText(R.string.sk_recent_searches_placeholder);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
if(currentRequest!=null){
|
if(currentRequest!=null){
|
||||||
currentRequest.cancel();
|
currentRequest.cancel();
|
||||||
currentRequest=null;
|
currentRequest=null;
|
||||||
}
|
}
|
||||||
currentQuery=q;
|
currentQuery=q;
|
||||||
|
setEmptyText(R.string.no_search_results);
|
||||||
if(filter==null)
|
if(filter==null)
|
||||||
currentFilter=EnumSet.allOf(SearchResult.Type.class);
|
currentFilter=EnumSet.allOf(SearchResult.Type.class);
|
||||||
else
|
else
|
||||||
@@ -221,6 +225,13 @@ public class SearchFragment extends BaseStatusListFragment<SearchResult>{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void clear() {
|
||||||
|
data.clear();
|
||||||
|
preloadedData.clear();
|
||||||
|
adapter.notifyDataSetChanged();
|
||||||
|
V.setVisibilityAnimated(content, View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Uri getWebUri(Uri.Builder base) {
|
public Uri getWebUri(Uri.Builder base) {
|
||||||
Uri.Builder searchUri = base.path("/search");
|
Uri.Builder searchUri = base.path("/search");
|
||||||
|
|||||||
@@ -139,7 +139,9 @@ public class CustomWelcomeFragment extends InstanceCatalogFragment {
|
|||||||
headerView.findViewById(R.id.more).setVisibility(View.GONE);
|
headerView.findViewById(R.id.more).setVisibility(View.GONE);
|
||||||
headerView.findViewById(R.id.visibility).setVisibility(View.GONE);
|
headerView.findViewById(R.id.visibility).setVisibility(View.GONE);
|
||||||
headerView.findViewById(R.id.unread_indicator).setVisibility(View.GONE);
|
headerView.findViewById(R.id.unread_indicator).setVisibility(View.GONE);
|
||||||
((TextView) headerView.findViewById(R.id.time_and_username)).setText(R.string.sk_app_username);
|
headerView.findViewById(R.id.separator).setVisibility(View.GONE);
|
||||||
|
headerView.findViewById(R.id.time).setVisibility(View.GONE);
|
||||||
|
((TextView) headerView.findViewById(R.id.username)).setText(R.string.sk_app_username);
|
||||||
((TextView) headerView.findViewById(R.id.name)).setText(R.string.sk_app_name);
|
((TextView) headerView.findViewById(R.id.name)).setText(R.string.sk_app_name);
|
||||||
((ImageView) headerView.findViewById(R.id.avatar)).setImageDrawable(getActivity().getDrawable(R.mipmap.ic_launcher));
|
((ImageView) headerView.findViewById(R.id.avatar)).setImageDrawable(getActivity().getDrawable(R.mipmap.ic_launcher));
|
||||||
((FragmentStackActivity) getActivity()).invalidateSystemBarColors(this);
|
((FragmentStackActivity) getActivity()).invalidateSystemBarColors(this);
|
||||||
|
|||||||
@@ -102,7 +102,6 @@ public class ReportAddPostsChoiceFragment extends StatusListFragment{
|
|||||||
selectedIDs.remove(id);
|
selectedIDs.remove(id);
|
||||||
else
|
else
|
||||||
selectedIDs.add(id);
|
selectedIDs.add(id);
|
||||||
btn.setEnabled(!selectedIDs.isEmpty());
|
|
||||||
CheckableHeaderStatusDisplayItem.Holder holder=findHolderOfType(id, CheckableHeaderStatusDisplayItem.Holder.class);
|
CheckableHeaderStatusDisplayItem.Holder holder=findHolderOfType(id, CheckableHeaderStatusDisplayItem.Holder.class);
|
||||||
if(holder!=null)
|
if(holder!=null)
|
||||||
holder.rebind();
|
holder.rebind();
|
||||||
@@ -112,7 +111,6 @@ public class ReportAddPostsChoiceFragment extends StatusListFragment{
|
|||||||
public void onViewCreated(View view, Bundle savedInstanceState){
|
public void onViewCreated(View view, Bundle savedInstanceState){
|
||||||
super.onViewCreated(view, savedInstanceState);
|
super.onViewCreated(view, savedInstanceState);
|
||||||
btn=view.findViewById(R.id.btn_next);
|
btn=view.findViewById(R.id.btn_next);
|
||||||
btn.setEnabled(!selectedIDs.isEmpty());
|
|
||||||
btn.setOnClickListener(this::onButtonClick);
|
btn.setOnClickListener(this::onButtonClick);
|
||||||
buttonBar=view.findViewById(R.id.button_bar);
|
buttonBar=view.findViewById(R.id.button_bar);
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import org.joinmastodon.android.model.viewmodel.ListItem;
|
|||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import me.grishka.appkit.imageloader.ImageCache;
|
import me.grishka.appkit.imageloader.ImageCache;
|
||||||
@@ -32,7 +33,7 @@ public class SettingsAboutAppFragment extends BaseSettingsFragment<Void>{
|
|||||||
setTitle(getString(R.string.about_app, getString(R.string.sk_app_name)));
|
setTitle(getString(R.string.about_app, getString(R.string.sk_app_name)));
|
||||||
AccountSession s=AccountSessionManager.get(accountID);
|
AccountSession s=AccountSessionManager.get(accountID);
|
||||||
onDataLoaded(List.of(
|
onDataLoaded(List.of(
|
||||||
new ListItem<>(R.string.sk_settings_donate, 0, R.drawable.ic_fluent_heart_24_regular, ()->UiUtils.launchWebBrowser(getActivity(), getString(R.string.donate_url))),
|
new ListItem<>(R.string.sk_settings_donate, 0, R.drawable.ic_fluent_heart_24_regular, ()->UiUtils.openHashtagTimeline(getActivity(), accountID, getString(R.string.donate_hashtag), null)),
|
||||||
new ListItem<>(R.string.sk_settings_contribute, 0, R.drawable.ic_fluent_open_24_regular, ()->UiUtils.launchWebBrowser(getActivity(), getString(R.string.repo_url))),
|
new ListItem<>(R.string.sk_settings_contribute, 0, R.drawable.ic_fluent_open_24_regular, ()->UiUtils.launchWebBrowser(getActivity(), getString(R.string.repo_url))),
|
||||||
new ListItem<>(R.string.settings_tos, 0, R.drawable.ic_fluent_open_24_regular, ()->UiUtils.launchWebBrowser(getActivity(), "https://"+s.domain+"/terms")),
|
new ListItem<>(R.string.settings_tos, 0, R.drawable.ic_fluent_open_24_regular, ()->UiUtils.launchWebBrowser(getActivity(), "https://"+s.domain+"/terms")),
|
||||||
new ListItem<>(R.string.settings_privacy_policy, 0, R.drawable.ic_fluent_open_24_regular, ()->UiUtils.launchWebBrowser(getActivity(), getString(R.string.privacy_policy_url)), 0, true),
|
new ListItem<>(R.string.settings_privacy_policy, 0, R.drawable.ic_fluent_open_24_regular, ()->UiUtils.launchWebBrowser(getActivity(), getString(R.string.privacy_policy_url)), 0, true),
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ public class SettingsBehaviorFragment extends BaseSettingsFragment<Void> impleme
|
|||||||
// MEGALODON
|
// MEGALODON
|
||||||
private MastodonLanguage.LanguageResolver languageResolver;
|
private MastodonLanguage.LanguageResolver languageResolver;
|
||||||
private ListItem<Void> prefixRepliesItem, replyVisibilityItem;
|
private ListItem<Void> prefixRepliesItem, replyVisibilityItem;
|
||||||
private CheckableListItem<Void> forwardReportsItem, remoteLoadingItem, showBoostsItem, showRepliesItem, loadNewPostsItem, seeNewPostsBtnItem;
|
private CheckableListItem<Void> forwardReportsItem, remoteLoadingItem, showBoostsItem, showRepliesItem, loadNewPostsItem, seeNewPostsBtnItem, overlayMediaItem;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState){
|
public void onCreate(Bundle savedInstanceState){
|
||||||
@@ -47,6 +47,7 @@ public class SettingsBehaviorFragment extends BaseSettingsFragment<Void> impleme
|
|||||||
languageItem=new ListItem<>(getString(R.string.default_post_language), postLanguage!=null ? postLanguage.getDisplayName(getContext()) : null, R.drawable.ic_fluent_local_language_24_regular, this::onDefaultLanguageClick),
|
languageItem=new ListItem<>(getString(R.string.default_post_language), postLanguage!=null ? postLanguage.getDisplayName(getContext()) : null, R.drawable.ic_fluent_local_language_24_regular, this::onDefaultLanguageClick),
|
||||||
altTextItem=new CheckableListItem<>(R.string.settings_alt_text_reminders, 0, CheckableListItem.Style.SWITCH, GlobalUserPreferences.altTextReminders, R.drawable.ic_fluent_image_alt_text_24_regular, ()->toggleCheckableItem(altTextItem)),
|
altTextItem=new CheckableListItem<>(R.string.settings_alt_text_reminders, 0, CheckableListItem.Style.SWITCH, GlobalUserPreferences.altTextReminders, R.drawable.ic_fluent_image_alt_text_24_regular, ()->toggleCheckableItem(altTextItem)),
|
||||||
playGifsItem=new CheckableListItem<>(R.string.settings_gif, 0, CheckableListItem.Style.SWITCH, GlobalUserPreferences.playGifs, R.drawable.ic_fluent_gif_24_regular, ()->toggleCheckableItem(playGifsItem)),
|
playGifsItem=new CheckableListItem<>(R.string.settings_gif, 0, CheckableListItem.Style.SWITCH, GlobalUserPreferences.playGifs, R.drawable.ic_fluent_gif_24_regular, ()->toggleCheckableItem(playGifsItem)),
|
||||||
|
overlayMediaItem=new CheckableListItem<>(R.string.sk_settings_continues_playback, R.string.sk_settings_continues_playback_summary, CheckableListItem.Style.SWITCH, GlobalUserPreferences.overlayMedia, R.drawable.ic_fluent_play_circle_hint_24_regular, ()->toggleCheckableItem(overlayMediaItem)),
|
||||||
customTabsItem=new CheckableListItem<>(R.string.settings_custom_tabs, 0, CheckableListItem.Style.SWITCH, GlobalUserPreferences.useCustomTabs, R.drawable.ic_fluent_link_24_regular, ()->toggleCheckableItem(customTabsItem)),
|
customTabsItem=new CheckableListItem<>(R.string.settings_custom_tabs, 0, CheckableListItem.Style.SWITCH, GlobalUserPreferences.useCustomTabs, R.drawable.ic_fluent_link_24_regular, ()->toggleCheckableItem(customTabsItem)),
|
||||||
confirmUnfollowItem=new CheckableListItem<>(R.string.settings_confirm_unfollow, 0, CheckableListItem.Style.SWITCH, GlobalUserPreferences.confirmUnfollow, R.drawable.ic_fluent_person_delete_24_regular, ()->toggleCheckableItem(confirmUnfollowItem)),
|
confirmUnfollowItem=new CheckableListItem<>(R.string.settings_confirm_unfollow, 0, CheckableListItem.Style.SWITCH, GlobalUserPreferences.confirmUnfollow, R.drawable.ic_fluent_person_delete_24_regular, ()->toggleCheckableItem(confirmUnfollowItem)),
|
||||||
confirmBoostItem=new CheckableListItem<>(R.string.settings_confirm_boost, 0, CheckableListItem.Style.SWITCH, GlobalUserPreferences.confirmBoost, R.drawable.ic_fluent_arrow_repeat_all_24_regular, ()->toggleCheckableItem(confirmBoostItem)),
|
confirmBoostItem=new CheckableListItem<>(R.string.settings_confirm_boost, 0, CheckableListItem.Style.SWITCH, GlobalUserPreferences.confirmBoost, R.drawable.ic_fluent_arrow_repeat_all_24_regular, ()->toggleCheckableItem(confirmBoostItem)),
|
||||||
@@ -162,6 +163,7 @@ public class SettingsBehaviorFragment extends BaseSettingsFragment<Void> impleme
|
|||||||
protected void onHidden(){
|
protected void onHidden(){
|
||||||
super.onHidden();
|
super.onHidden();
|
||||||
GlobalUserPreferences.playGifs=playGifsItem.checked;
|
GlobalUserPreferences.playGifs=playGifsItem.checked;
|
||||||
|
GlobalUserPreferences.overlayMedia=overlayMediaItem.checked;
|
||||||
GlobalUserPreferences.useCustomTabs=customTabsItem.checked;
|
GlobalUserPreferences.useCustomTabs=customTabsItem.checked;
|
||||||
GlobalUserPreferences.altTextReminders=altTextItem.checked;
|
GlobalUserPreferences.altTextReminders=altTextItem.checked;
|
||||||
GlobalUserPreferences.confirmUnfollow=customTabsItem.checked;
|
GlobalUserPreferences.confirmUnfollow=customTabsItem.checked;
|
||||||
|
|||||||
@@ -37,8 +37,9 @@ public class SettingsDisplayFragment extends BaseSettingsFragment<Void>{
|
|||||||
private CheckableListItem<Void> revealCWsItem, hideSensitiveMediaItem, interactionCountsItem, emojiInNamesItem;
|
private CheckableListItem<Void> revealCWsItem, hideSensitiveMediaItem, interactionCountsItem, emojiInNamesItem;
|
||||||
|
|
||||||
// MEGALODON
|
// MEGALODON
|
||||||
private CheckableListItem<Void> trueBlackModeItem, marqueeItem, disableSwipeItem, reduceMotionItem, altIndicatorItem, noAltIndicatorItem, collapsePostsItem, spectatorModeItem, hideFabItem, translateOpenedItem, disablePillItem;
|
private CheckableListItem<Void> trueBlackModeItem, marqueeItem, disableSwipeItem, reduceMotionItem, altIndicatorItem, noAltIndicatorItem, collapsePostsItem, spectatorModeItem, hideFabItem, translateOpenedItem, disablePillItem, showNavigationLabelsItem;
|
||||||
private ListItem<Void> colorItem, publishTextItem, autoRevealCWsItem;
|
private ListItem<Void> colorItem, publishTextItem, autoRevealCWsItem;
|
||||||
|
private CheckableListItem<Void> pronounsInUserListingsItem, pronounsInTimelinesItem, pronounsInThreadsItem;
|
||||||
|
|
||||||
private AccountLocalPreferences lp;
|
private AccountLocalPreferences lp;
|
||||||
|
|
||||||
@@ -67,7 +68,11 @@ public class SettingsDisplayFragment extends BaseSettingsFragment<Void>{
|
|||||||
spectatorModeItem=new CheckableListItem<>(R.string.sk_settings_hide_interaction, 0, CheckableListItem.Style.SWITCH, GlobalUserPreferences.spectatorMode, R.drawable.ic_fluent_star_off_24_regular, ()->toggleCheckableItem(spectatorModeItem)),
|
spectatorModeItem=new CheckableListItem<>(R.string.sk_settings_hide_interaction, 0, CheckableListItem.Style.SWITCH, GlobalUserPreferences.spectatorMode, R.drawable.ic_fluent_star_off_24_regular, ()->toggleCheckableItem(spectatorModeItem)),
|
||||||
hideFabItem=new CheckableListItem<>(R.string.sk_settings_hide_fab, 0, CheckableListItem.Style.SWITCH, GlobalUserPreferences.autoHideFab, R.drawable.ic_fluent_edit_24_regular, ()->toggleCheckableItem(hideFabItem)),
|
hideFabItem=new CheckableListItem<>(R.string.sk_settings_hide_fab, 0, CheckableListItem.Style.SWITCH, GlobalUserPreferences.autoHideFab, R.drawable.ic_fluent_edit_24_regular, ()->toggleCheckableItem(hideFabItem)),
|
||||||
translateOpenedItem=new CheckableListItem<>(R.string.sk_settings_translate_only_opened, 0, CheckableListItem.Style.SWITCH, GlobalUserPreferences.translateButtonOpenedOnly, R.drawable.ic_fluent_translate_24_regular, ()->toggleCheckableItem(translateOpenedItem)),
|
translateOpenedItem=new CheckableListItem<>(R.string.sk_settings_translate_only_opened, 0, CheckableListItem.Style.SWITCH, GlobalUserPreferences.translateButtonOpenedOnly, R.drawable.ic_fluent_translate_24_regular, ()->toggleCheckableItem(translateOpenedItem)),
|
||||||
disablePillItem=new CheckableListItem<>(R.string.sk_disable_pill_shaped_active_indicator, 0, CheckableListItem.Style.SWITCH, GlobalUserPreferences.disableM3PillActiveIndicator, R.drawable.ic_fluent_pill_24_regular, ()->toggleCheckableItem(disablePillItem))
|
disablePillItem=new CheckableListItem<>(R.string.sk_disable_pill_shaped_active_indicator, 0, CheckableListItem.Style.SWITCH, GlobalUserPreferences.disableM3PillActiveIndicator, R.drawable.ic_fluent_pill_24_regular, ()->toggleCheckableItem(disablePillItem)),
|
||||||
|
showNavigationLabelsItem=new CheckableListItem<>(R.string.sk_settings_show_labels_in_navigation_bar, 0, CheckableListItem.Style.SWITCH, GlobalUserPreferences.showNavigationLabels, R.drawable.ic_fluent_tag_24_regular, ()->toggleCheckableItem(showNavigationLabelsItem), true),
|
||||||
|
pronounsInTimelinesItem=new CheckableListItem<>(R.string.sk_settings_display_pronouns_in_timelines, 0, CheckableListItem.Style.CHECKBOX, GlobalUserPreferences.displayPronounsInTimelines, 0, ()->toggleCheckableItem(pronounsInTimelinesItem)),
|
||||||
|
pronounsInThreadsItem=new CheckableListItem<>(R.string.sk_settings_display_pronouns_in_threads, 0, CheckableListItem.Style.CHECKBOX, GlobalUserPreferences.displayPronounsInThreads, 0, ()->toggleCheckableItem(pronounsInThreadsItem)),
|
||||||
|
pronounsInUserListingsItem=new CheckableListItem<>(R.string.sk_settings_display_pronouns_in_user_listings, 0, CheckableListItem.Style.CHECKBOX, GlobalUserPreferences.displayPronounsInUserListings, 0, ()->toggleCheckableItem(pronounsInUserListingsItem))
|
||||||
));
|
));
|
||||||
trueBlackModeItem.checkedChangeListener=checked->onTrueBlackModeClick();
|
trueBlackModeItem.checkedChangeListener=checked->onTrueBlackModeClick();
|
||||||
}
|
}
|
||||||
@@ -90,7 +95,8 @@ public class SettingsDisplayFragment extends BaseSettingsFragment<Void>{
|
|||||||
super.onHidden();
|
super.onHidden();
|
||||||
|
|
||||||
boolean restartPlease=
|
boolean restartPlease=
|
||||||
GlobalUserPreferences.disableM3PillActiveIndicator!=disablePillItem.checked;
|
GlobalUserPreferences.disableM3PillActiveIndicator!=disablePillItem.checked ||
|
||||||
|
GlobalUserPreferences.showNavigationLabels!=showNavigationLabelsItem.checked;
|
||||||
|
|
||||||
lp.revealCWs=revealCWsItem.checked;
|
lp.revealCWs=revealCWsItem.checked;
|
||||||
lp.hideSensitiveMedia=hideSensitiveMediaItem.checked;
|
lp.hideSensitiveMedia=hideSensitiveMediaItem.checked;
|
||||||
@@ -107,6 +113,10 @@ public class SettingsDisplayFragment extends BaseSettingsFragment<Void>{
|
|||||||
GlobalUserPreferences.autoHideFab=hideFabItem.checked;
|
GlobalUserPreferences.autoHideFab=hideFabItem.checked;
|
||||||
GlobalUserPreferences.translateButtonOpenedOnly=translateOpenedItem.checked;
|
GlobalUserPreferences.translateButtonOpenedOnly=translateOpenedItem.checked;
|
||||||
GlobalUserPreferences.disableM3PillActiveIndicator=disablePillItem.checked;
|
GlobalUserPreferences.disableM3PillActiveIndicator=disablePillItem.checked;
|
||||||
|
GlobalUserPreferences.showNavigationLabels=showNavigationLabelsItem.checked;
|
||||||
|
GlobalUserPreferences.displayPronounsInTimelines=pronounsInTimelinesItem.checked;
|
||||||
|
GlobalUserPreferences.displayPronounsInThreads=pronounsInThreadsItem.checked;
|
||||||
|
GlobalUserPreferences.displayPronounsInUserListings=pronounsInUserListingsItem.checked;
|
||||||
GlobalUserPreferences.save();
|
GlobalUserPreferences.save();
|
||||||
if(restartPlease) restartActivityToApplyNewTheme();
|
if(restartPlease) restartActivityToApplyNewTheme();
|
||||||
else E.post(new StatusDisplaySettingsChangedEvent(accountID));
|
else E.post(new StatusDisplaySettingsChangedEvent(accountID));
|
||||||
|
|||||||
@@ -2,10 +2,14 @@ package org.joinmastodon.android.fragments.settings;
|
|||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import androidx.annotation.StringRes;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.E;
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.session.AccountLocalPreferences;
|
import org.joinmastodon.android.api.session.AccountLocalPreferences;
|
||||||
import org.joinmastodon.android.api.session.AccountSession;
|
import org.joinmastodon.android.api.session.AccountSession;
|
||||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||||
|
import org.joinmastodon.android.events.StatusDisplaySettingsChangedEvent;
|
||||||
import org.joinmastodon.android.fragments.HasAccountID;
|
import org.joinmastodon.android.fragments.HasAccountID;
|
||||||
import org.joinmastodon.android.model.ContentType;
|
import org.joinmastodon.android.model.ContentType;
|
||||||
import org.joinmastodon.android.model.viewmodel.CheckableListItem;
|
import org.joinmastodon.android.model.viewmodel.CheckableListItem;
|
||||||
@@ -15,12 +19,13 @@ import org.joinmastodon.android.ui.utils.UiUtils;
|
|||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
|
||||||
import me.grishka.appkit.Nav;
|
import me.grishka.appkit.Nav;
|
||||||
|
|
||||||
public class SettingsInstanceFragment extends BaseSettingsFragment<Void> implements HasAccountID{
|
public class SettingsInstanceFragment extends BaseSettingsFragment<Void> implements HasAccountID{
|
||||||
private CheckableListItem<Void> contentTypesItem, localOnlyItem, glitchModeItem;
|
private CheckableListItem<Void> contentTypesItem, emojiReactionsItem, localOnlyItem, glitchModeItem;
|
||||||
private ListItem<Void> defaultContentTypeItem;
|
private ListItem<Void> defaultContentTypeItem, showEmojiReactionsItem;
|
||||||
private AccountLocalPreferences lp;
|
private AccountLocalPreferences lp;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -35,12 +40,16 @@ public class SettingsInstanceFragment extends BaseSettingsFragment<Void> impleme
|
|||||||
new ListItem<>(R.string.sk_settings_posting, 0, R.drawable.ic_fluent_open_24_regular, ()->UiUtils.launchWebBrowser(getActivity(), "https://"+s.domain+"/settings/preferences/other")),
|
new ListItem<>(R.string.sk_settings_posting, 0, R.drawable.ic_fluent_open_24_regular, ()->UiUtils.launchWebBrowser(getActivity(), "https://"+s.domain+"/settings/preferences/other")),
|
||||||
new ListItem<>(R.string.sk_settings_auth, 0, R.drawable.ic_fluent_open_24_regular, ()->UiUtils.launchWebBrowser(getActivity(), "https://"+s.domain+"/auth/edit"), 0, true),
|
new ListItem<>(R.string.sk_settings_auth, 0, R.drawable.ic_fluent_open_24_regular, ()->UiUtils.launchWebBrowser(getActivity(), "https://"+s.domain+"/auth/edit"), 0, true),
|
||||||
contentTypesItem=new CheckableListItem<>(R.string.sk_settings_content_types, R.string.sk_settings_content_types_explanation, CheckableListItem.Style.SWITCH, lp.contentTypesEnabled, R.drawable.ic_fluent_text_edit_style_24_regular, this::onContentTypeClick),
|
contentTypesItem=new CheckableListItem<>(R.string.sk_settings_content_types, R.string.sk_settings_content_types_explanation, CheckableListItem.Style.SWITCH, lp.contentTypesEnabled, R.drawable.ic_fluent_text_edit_style_24_regular, this::onContentTypeClick),
|
||||||
defaultContentTypeItem=new ListItem<>(R.string.sk_settings_default_content_type, lp.defaultContentType.getName(), R.drawable.ic_fluent_text_bold_24_regular, this::onDefaultContentTypeClick),
|
defaultContentTypeItem=new ListItem<>(R.string.sk_settings_default_content_type, lp.defaultContentType.getName(), R.drawable.ic_fluent_text_bold_24_regular, this::onDefaultContentTypeClick, 0, true),
|
||||||
|
emojiReactionsItem=new CheckableListItem<>(R.string.sk_settings_emoji_reactions, R.string.sk_settings_emoji_reactions_explanation, CheckableListItem.Style.SWITCH, lp.emojiReactionsEnabled, R.drawable.ic_fluent_emoji_laugh_24_regular, this::onEmojiReactionsClick),
|
||||||
|
showEmojiReactionsItem=new ListItem<>(R.string.sk_settings_show_emoji_reactions, getShowEmojiReactionsString(), R.drawable.ic_fluent_emoji_24_regular, this::onShowEmojiReactionsClick, 0, true),
|
||||||
localOnlyItem=new CheckableListItem<>(R.string.sk_settings_support_local_only, R.string.sk_settings_local_only_explanation, CheckableListItem.Style.SWITCH, lp.localOnlySupported, R.drawable.ic_fluent_eye_24_regular, this::onLocalOnlyClick),
|
localOnlyItem=new CheckableListItem<>(R.string.sk_settings_support_local_only, R.string.sk_settings_local_only_explanation, CheckableListItem.Style.SWITCH, lp.localOnlySupported, R.drawable.ic_fluent_eye_24_regular, this::onLocalOnlyClick),
|
||||||
glitchModeItem=new CheckableListItem<>(R.string.sk_settings_glitch_instance, R.string.sk_settings_glitch_mode_explanation, CheckableListItem.Style.SWITCH, lp.glitchInstance, R.drawable.ic_fluent_eye_24_filled, ()->toggleCheckableItem(glitchModeItem))
|
glitchModeItem=new CheckableListItem<>(R.string.sk_settings_glitch_instance, R.string.sk_settings_glitch_mode_explanation, CheckableListItem.Style.SWITCH, lp.glitchInstance, R.drawable.ic_fluent_eye_24_filled, ()->toggleCheckableItem(glitchModeItem))
|
||||||
));
|
));
|
||||||
contentTypesItem.checkedChangeListener=checked->onContentTypeClick();
|
contentTypesItem.checkedChangeListener=checked->onContentTypeClick();
|
||||||
defaultContentTypeItem.isEnabled=contentTypesItem.checked;
|
defaultContentTypeItem.isEnabled=contentTypesItem.checked;
|
||||||
|
emojiReactionsItem.checkedChangeListener=checked->onEmojiReactionsClick();
|
||||||
|
showEmojiReactionsItem.isEnabled=emojiReactionsItem.checked;
|
||||||
localOnlyItem.checkedChangeListener=checked->onLocalOnlyClick();
|
localOnlyItem.checkedChangeListener=checked->onLocalOnlyClick();
|
||||||
glitchModeItem.isEnabled=localOnlyItem.checked;
|
glitchModeItem.isEnabled=localOnlyItem.checked;
|
||||||
}
|
}
|
||||||
@@ -52,9 +61,11 @@ public class SettingsInstanceFragment extends BaseSettingsFragment<Void> impleme
|
|||||||
protected void onHidden(){
|
protected void onHidden(){
|
||||||
super.onHidden();
|
super.onHidden();
|
||||||
lp.contentTypesEnabled=contentTypesItem.checked;
|
lp.contentTypesEnabled=contentTypesItem.checked;
|
||||||
|
lp.emojiReactionsEnabled=emojiReactionsItem.checked;
|
||||||
lp.localOnlySupported=localOnlyItem.checked;
|
lp.localOnlySupported=localOnlyItem.checked;
|
||||||
lp.glitchInstance=glitchModeItem.checked;
|
lp.glitchInstance=glitchModeItem.checked;
|
||||||
lp.save();
|
lp.save();
|
||||||
|
E.post(new StatusDisplaySettingsChangedEvent(accountID));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onServerClick(){
|
private void onServerClick(){
|
||||||
@@ -101,6 +112,36 @@ public class SettingsInstanceFragment extends BaseSettingsFragment<Void> impleme
|
|||||||
.show();
|
.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void onShowEmojiReactionsClick(){
|
||||||
|
int selected=lp.showEmojiReactions.ordinal();
|
||||||
|
int[] newSelected={selected};
|
||||||
|
new M3AlertDialogBuilder(getActivity())
|
||||||
|
.setTitle(R.string.sk_settings_show_emoji_reactions)
|
||||||
|
.setSingleChoiceItems((String[]) IntStream.of(R.string.sk_settings_show_emoji_reactions_hide_empty, R.string.sk_settings_show_emoji_reactions_only_opened, R.string.sk_settings_show_emoji_reactions_always).mapToObj(this::getString).toArray(String[]::new),
|
||||||
|
selected, (dlg, item)->newSelected[0]=item)
|
||||||
|
.setPositiveButton(R.string.ok, (dlg, item)->{
|
||||||
|
lp.showEmojiReactions=AccountLocalPreferences.ShowEmojiReactions.values()[newSelected[0]];
|
||||||
|
showEmojiReactionsItem.subtitleRes=getShowEmojiReactionsString();
|
||||||
|
rebindItem(showEmojiReactionsItem);
|
||||||
|
})
|
||||||
|
.setNegativeButton(R.string.cancel, null)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private @StringRes int getShowEmojiReactionsString(){
|
||||||
|
return switch(lp.showEmojiReactions){
|
||||||
|
case HIDE_EMPTY -> R.string.sk_settings_show_emoji_reactions_hide_empty;
|
||||||
|
case ONLY_OPENED -> R.string.sk_settings_show_emoji_reactions_only_opened;
|
||||||
|
case ALWAYS -> R.string.sk_settings_show_emoji_reactions_always;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onEmojiReactionsClick(){
|
||||||
|
toggleCheckableItem(emojiReactionsItem);
|
||||||
|
showEmojiReactionsItem.isEnabled=emojiReactionsItem.checked;
|
||||||
|
rebindItem(showEmojiReactionsItem);
|
||||||
|
}
|
||||||
|
|
||||||
private void onLocalOnlyClick(){
|
private void onLocalOnlyClick(){
|
||||||
toggleCheckableItem(localOnlyItem);
|
toggleCheckableItem(localOnlyItem);
|
||||||
glitchModeItem.checked=localOnlyItem.checked && !isInstanceAkkoma();
|
glitchModeItem.checked=localOnlyItem.checked && !isInstanceAkkoma();
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import org.joinmastodon.android.R;
|
|||||||
import org.joinmastodon.android.api.session.AccountSession;
|
import org.joinmastodon.android.api.session.AccountSession;
|
||||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||||
import org.joinmastodon.android.events.SelfUpdateStateChangedEvent;
|
import org.joinmastodon.android.events.SelfUpdateStateChangedEvent;
|
||||||
|
import org.joinmastodon.android.model.Instance;
|
||||||
import org.joinmastodon.android.model.viewmodel.ListItem;
|
import org.joinmastodon.android.model.viewmodel.ListItem;
|
||||||
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
|
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
|
||||||
import org.joinmastodon.android.ui.utils.HideableSingleViewRecyclerAdapter;
|
import org.joinmastodon.android.ui.utils.HideableSingleViewRecyclerAdapter;
|
||||||
@@ -29,6 +30,7 @@ import me.grishka.appkit.Nav;
|
|||||||
import me.grishka.appkit.utils.MergeRecyclerAdapter;
|
import me.grishka.appkit.utils.MergeRecyclerAdapter;
|
||||||
|
|
||||||
public class SettingsMainFragment extends BaseSettingsFragment<Void>{
|
public class SettingsMainFragment extends BaseSettingsFragment<Void>{
|
||||||
|
private AccountSession account;
|
||||||
private boolean loggedOut;
|
private boolean loggedOut;
|
||||||
private HideableSingleViewRecyclerAdapter bannerAdapter;
|
private HideableSingleViewRecyclerAdapter bannerAdapter;
|
||||||
private Button updateButton1, updateButton2;
|
private Button updateButton1, updateButton2;
|
||||||
@@ -47,23 +49,27 @@ public class SettingsMainFragment extends BaseSettingsFragment<Void>{
|
|||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState){
|
public void onCreate(Bundle savedInstanceState){
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
account = AccountSessionManager.get(accountID);
|
||||||
setTitle(R.string.settings);
|
setTitle(R.string.settings);
|
||||||
setSubtitle(AccountSessionManager.get(accountID).getFullUsername());
|
setSubtitle(account.getFullUsername());
|
||||||
onDataLoaded(List.of(
|
onDataLoaded(List.of(
|
||||||
new ListItem<>(R.string.settings_behavior, 0, R.drawable.ic_fluent_settings_24_regular, this::onBehaviorClick),
|
new ListItem<>(R.string.settings_behavior, 0, R.drawable.ic_fluent_settings_24_regular, this::onBehaviorClick),
|
||||||
new ListItem<>(R.string.settings_display, 0, R.drawable.ic_fluent_color_24_regular, this::onDisplayClick),
|
new ListItem<>(R.string.settings_display, 0, R.drawable.ic_fluent_color_24_regular, this::onDisplayClick),
|
||||||
new ListItem<>(R.string.settings_filters, 0, R.drawable.ic_fluent_filter_24_regular, this::onFiltersClick),
|
|
||||||
new ListItem<>(R.string.settings_notifications, 0, R.drawable.ic_fluent_alert_24_regular, this::onNotificationsClick),
|
new ListItem<>(R.string.settings_notifications, 0, R.drawable.ic_fluent_alert_24_regular, this::onNotificationsClick),
|
||||||
new ListItem<>(R.string.sk_settings_instance, 0, R.drawable.ic_fluent_server_24_regular, this::onInstanceClick),
|
new ListItem<>(R.string.sk_settings_instance, 0, R.drawable.ic_fluent_server_24_regular, this::onInstanceClick),
|
||||||
new ListItem<>(getString(R.string.about_app, getString(R.string.sk_app_name)), null, R.drawable.ic_fluent_info_24_regular, this::onAboutClick, null, 0, true),
|
new ListItem<>(getString(R.string.about_app, getString(R.string.sk_app_name)), null, R.drawable.ic_fluent_info_24_regular, this::onAboutClick, null, 0, true),
|
||||||
new ListItem<>(R.string.log_out, 0, R.drawable.ic_fluent_sign_out_24_regular, this::onLogOutClick, R.attr.colorM3Error, false)
|
new ListItem<>(R.string.log_out, 0, R.drawable.ic_fluent_sign_out_24_regular, this::onLogOutClick, R.attr.colorM3Error, false)
|
||||||
));
|
));
|
||||||
|
|
||||||
|
Instance instance = AccountSessionManager.getInstance().getInstanceInfo(account.domain);
|
||||||
|
if (!instance.isAkkoma())
|
||||||
|
data.add(2, new ListItem<>(R.string.settings_filters, 0, R.drawable.ic_fluent_filter_24_regular, this::onFiltersClick));
|
||||||
|
|
||||||
if(BuildConfig.DEBUG || BuildConfig.BUILD_TYPE.equals("appcenterPrivateBeta")){
|
if(BuildConfig.DEBUG || BuildConfig.BUILD_TYPE.equals("appcenterPrivateBeta")){
|
||||||
data.add(0, new ListItem<>("Debug settings", null, R.drawable.ic_fluent_wrench_screwdriver_24_regular, ()->Nav.go(getActivity(), SettingsDebugFragment.class, makeFragmentArgs()), null, 0, true));
|
data.add(0, new ListItem<>("Debug settings", null, R.drawable.ic_fluent_wrench_screwdriver_24_regular, ()->Nav.go(getActivity(), SettingsDebugFragment.class, makeFragmentArgs()), null, 0, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
AccountSessionManager.get(accountID).reloadPreferences(null);
|
account.reloadPreferences(null);
|
||||||
E.register(this);
|
E.register(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,7 +86,7 @@ public class SettingsMainFragment extends BaseSettingsFragment<Void>{
|
|||||||
protected void onHidden(){
|
protected void onHidden(){
|
||||||
super.onHidden();
|
super.onHidden();
|
||||||
if(!loggedOut)
|
if(!loggedOut)
|
||||||
AccountSessionManager.get(accountID).savePreferencesIfPending();
|
account.savePreferencesIfPending();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -147,7 +153,7 @@ public class SettingsMainFragment extends BaseSettingsFragment<Void>{
|
|||||||
AccountSession session=AccountSessionManager.getInstance().getAccount(accountID);
|
AccountSession session=AccountSessionManager.getInstance().getAccount(accountID);
|
||||||
new M3AlertDialogBuilder(getActivity())
|
new M3AlertDialogBuilder(getActivity())
|
||||||
.setMessage(getString(R.string.confirm_log_out, session.getFullUsername()))
|
.setMessage(getString(R.string.confirm_log_out, session.getFullUsername()))
|
||||||
.setPositiveButton(R.string.log_out, (dialog, which)->AccountSessionManager.get(accountID).logOut(getActivity(), ()->{
|
.setPositiveButton(R.string.log_out, (dialog, which)->account.logOut(getActivity(), ()->{
|
||||||
loggedOut=true;
|
loggedOut=true;
|
||||||
getActivity().finish();
|
getActivity().finish();
|
||||||
Intent intent=new Intent(getActivity(), MainActivity.class);
|
Intent intent=new Intent(getActivity(), MainActivity.class);
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package org.joinmastodon.android.fragments.settings;
|
package org.joinmastodon.android.fragments.settings;
|
||||||
|
|
||||||
|
import static org.unifiedpush.android.connector.UnifiedPush.getDistributor;
|
||||||
|
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
@@ -25,8 +27,11 @@ import org.joinmastodon.android.model.viewmodel.ListItem;
|
|||||||
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
|
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
|
||||||
import org.joinmastodon.android.ui.utils.HideableSingleViewRecyclerAdapter;
|
import org.joinmastodon.android.ui.utils.HideableSingleViewRecyclerAdapter;
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
|
import org.unifiedpush.android.connector.RegistrationDialogContent;
|
||||||
|
import org.unifiedpush.android.connector.UnifiedPush;
|
||||||
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
@@ -52,7 +57,8 @@ public class SettingsNotificationsFragment extends BaseSettingsFragment<Void>{
|
|||||||
private boolean notificationsAllowed=true;
|
private boolean notificationsAllowed=true;
|
||||||
|
|
||||||
// MEGALODON
|
// MEGALODON
|
||||||
private CheckableListItem<Void> uniformIconItem, deleteItem, onlyLatestItem;
|
private boolean useUnifiedPush = false;
|
||||||
|
private CheckableListItem<Void> uniformIconItem, deleteItem, onlyLatestItem, unifiedPushItem;
|
||||||
private CheckableListItem<Void> postsItem, updateItem;
|
private CheckableListItem<Void> postsItem, updateItem;
|
||||||
|
|
||||||
private AccountLocalPreferences lp;
|
private AccountLocalPreferences lp;
|
||||||
@@ -64,6 +70,7 @@ public class SettingsNotificationsFragment extends BaseSettingsFragment<Void>{
|
|||||||
lp=AccountSessionManager.get(accountID).getLocalPreferences();
|
lp=AccountSessionManager.get(accountID).getLocalPreferences();
|
||||||
|
|
||||||
getPushSubscription();
|
getPushSubscription();
|
||||||
|
useUnifiedPush=!getDistributor(getContext()).isEmpty();
|
||||||
|
|
||||||
onDataLoaded(List.of(
|
onDataLoaded(List.of(
|
||||||
pauseItem=new CheckableListItem<>(getString(R.string.pause_all_notifications), getPauseItemSubtitle(), CheckableListItem.Style.SWITCH, false, R.drawable.ic_fluent_alert_snooze_24_regular, ()->onPauseNotificationsClick(false)),
|
pauseItem=new CheckableListItem<>(getString(R.string.pause_all_notifications), getPauseItemSubtitle(), CheckableListItem.Style.SWITCH, false, R.drawable.ic_fluent_alert_snooze_24_regular, ()->onPauseNotificationsClick(false)),
|
||||||
@@ -79,11 +86,19 @@ public class SettingsNotificationsFragment extends BaseSettingsFragment<Void>{
|
|||||||
|
|
||||||
uniformIconItem=new CheckableListItem<>(R.string.sk_settings_uniform_icon_for_notifications, 0, CheckableListItem.Style.SWITCH, GlobalUserPreferences.uniformNotificationIcon, R.drawable.ic_ntf_logo, ()->toggleCheckableItem(uniformIconItem)),
|
uniformIconItem=new CheckableListItem<>(R.string.sk_settings_uniform_icon_for_notifications, 0, CheckableListItem.Style.SWITCH, GlobalUserPreferences.uniformNotificationIcon, R.drawable.ic_ntf_logo, ()->toggleCheckableItem(uniformIconItem)),
|
||||||
deleteItem=new CheckableListItem<>(R.string.sk_settings_enable_delete_notifications, 0, CheckableListItem.Style.SWITCH, GlobalUserPreferences.enableDeleteNotifications, R.drawable.ic_fluent_mail_inbox_dismiss_24_regular, ()->toggleCheckableItem(deleteItem)),
|
deleteItem=new CheckableListItem<>(R.string.sk_settings_enable_delete_notifications, 0, CheckableListItem.Style.SWITCH, GlobalUserPreferences.enableDeleteNotifications, R.drawable.ic_fluent_mail_inbox_dismiss_24_regular, ()->toggleCheckableItem(deleteItem)),
|
||||||
onlyLatestItem=new CheckableListItem<>(R.string.sk_settings_single_notification, 0, CheckableListItem.Style.SWITCH, lp.keepOnlyLatestNotification, R.drawable.ic_fluent_convert_range_24_regular, ()->toggleCheckableItem(onlyLatestItem), true)
|
onlyLatestItem=new CheckableListItem<>(R.string.sk_settings_single_notification, 0, CheckableListItem.Style.SWITCH, lp.keepOnlyLatestNotification, R.drawable.ic_fluent_convert_range_24_regular, ()->toggleCheckableItem(onlyLatestItem), true),
|
||||||
|
unifiedPushItem=new CheckableListItem<>(R.string.sk_settings_unifiedpush, 0, CheckableListItem.Style.SWITCH, useUnifiedPush, R.drawable.ic_fluent_alert_arrow_up_24_regular, this::onUnifiedPush, true)
|
||||||
));
|
));
|
||||||
|
|
||||||
|
//only enable when distributors, who can receive notifications, are available
|
||||||
|
unifiedPushItem.isEnabled=!UnifiedPush.getDistributors(getContext(), new ArrayList<>()).isEmpty();
|
||||||
|
if (!unifiedPushItem.isEnabled) {
|
||||||
|
unifiedPushItem.subtitleRes=R.string.sk_settings_unifiedpush_no_distributor_body;
|
||||||
|
}
|
||||||
|
|
||||||
typeItems=List.of(mentionsItem, boostsItem, favoritesItem, followersItem, pollsItem, updateItem, postsItem);
|
typeItems=List.of(mentionsItem, boostsItem, favoritesItem, followersItem, pollsItem, updateItem, postsItem);
|
||||||
pauseItem.checkedChangeListener=checked->onPauseNotificationsClick(true);
|
pauseItem.checkedChangeListener=checked->onPauseNotificationsClick(true);
|
||||||
|
unifiedPushItem.checkedChangeListener=checked->onUnifiedPush();
|
||||||
updatePolicyItem(null);
|
updatePolicyItem(null);
|
||||||
updatePauseItem();
|
updatePauseItem();
|
||||||
}
|
}
|
||||||
@@ -312,4 +327,38 @@ public class SettingsNotificationsFragment extends BaseSettingsFragment<Void>{
|
|||||||
bannerAdapter.setVisible(false);
|
bannerAdapter.setVisible(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
private void onUnifiedPush(){
|
||||||
|
if(getDistributor(getContext()).isEmpty()){
|
||||||
|
List<String> distributors = UnifiedPush.getDistributors(getContext(), new ArrayList<>());
|
||||||
|
showUnifiedPushRegisterDialog(distributors);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
UnifiedPush.unregisterApp(
|
||||||
|
getContext(),
|
||||||
|
accountID
|
||||||
|
);
|
||||||
|
|
||||||
|
//re-register to fcm
|
||||||
|
AccountSessionManager.getInstance().getAccount(accountID).getPushSubscriptionManager().registerAccountForPush(getPushSubscription());
|
||||||
|
unifiedPushItem.toggle();
|
||||||
|
rebindItem(unifiedPushItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showUnifiedPushRegisterDialog(List<String> distributors){
|
||||||
|
new M3AlertDialogBuilder(getContext()).setTitle(R.string.sk_settings_unifiedpush_choose).setItems(distributors.toArray(String[]::new),
|
||||||
|
(dialog, which)->{
|
||||||
|
String userDistrib = distributors.get(which);
|
||||||
|
UnifiedPush.saveDistributor(getContext(), userDistrib);
|
||||||
|
UnifiedPush.registerApp(
|
||||||
|
getContext(),
|
||||||
|
accountID,
|
||||||
|
new ArrayList<>(),
|
||||||
|
getContext().getPackageName()
|
||||||
|
);
|
||||||
|
unifiedPushItem.toggle();
|
||||||
|
rebindItem(unifiedPushItem);
|
||||||
|
}).setOnCancelListener(d->rebindItem(unifiedPushItem)).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ import android.net.Uri;
|
|||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
import android.util.Log;
|
||||||
import android.view.Gravity;
|
import android.view.Gravity;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@@ -24,6 +25,7 @@ import android.widget.Toast;
|
|||||||
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.requests.instance.GetInstanceExtendedDescription;
|
import org.joinmastodon.android.api.requests.instance.GetInstanceExtendedDescription;
|
||||||
|
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||||
import org.joinmastodon.android.model.Instance;
|
import org.joinmastodon.android.model.Instance;
|
||||||
import org.joinmastodon.android.model.viewmodel.AccountViewModel;
|
import org.joinmastodon.android.model.viewmodel.AccountViewModel;
|
||||||
import org.joinmastodon.android.model.viewmodel.ListItem;
|
import org.joinmastodon.android.model.viewmodel.ListItem;
|
||||||
@@ -126,6 +128,8 @@ public class SettingsServerAboutFragment extends LoaderFragment{
|
|||||||
hlp.leftMargin=hlp.rightMargin=V.dp(16);
|
hlp.leftMargin=hlp.rightMargin=V.dp(16);
|
||||||
scrollingLayout.addView(heading, hlp);
|
scrollingLayout.addView(heading, hlp);
|
||||||
|
|
||||||
|
// if a remote instance is shown, the account is remote and need to be loaded accordingly when shown
|
||||||
|
instance.contactAccount.isRemote=!AccountSessionManager.get(accountID).domain.equals(instance.normalizedUri);
|
||||||
AccountViewModel model=new AccountViewModel(instance.contactAccount, accountID);
|
AccountViewModel model=new AccountViewModel(instance.contactAccount, accountID);
|
||||||
AccountViewHolder holder=new AccountViewHolder(this, scrollingLayout, null);
|
AccountViewHolder holder=new AccountViewHolder(this, scrollingLayout, null);
|
||||||
holder.setStyle(AccountViewHolder.AccessoryType.NONE, false);
|
holder.setStyle(AccountViewHolder.AccessoryType.NONE, false);
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
package org.joinmastodon.android.model;
|
package org.joinmastodon.android.model;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.api.ObjectValidationException;
|
||||||
import org.joinmastodon.android.api.RequiredField;
|
import org.joinmastodon.android.api.RequiredField;
|
||||||
import org.parceler.Parcel;
|
import org.parceler.Parcel;
|
||||||
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@Parcel
|
@Parcel
|
||||||
@@ -20,6 +22,7 @@ public class Announcement extends BaseModel implements DisplayItemsParent {
|
|||||||
public Instant updatedAt;
|
public Instant updatedAt;
|
||||||
public boolean read;
|
public boolean read;
|
||||||
public List<Emoji> emojis;
|
public List<Emoji> emojis;
|
||||||
|
public List<EmojiReaction> reactions;
|
||||||
public List<Mention> mentions;
|
public List<Mention> mentions;
|
||||||
public List<Hashtag> tags;
|
public List<Hashtag> tags;
|
||||||
|
|
||||||
@@ -41,10 +44,17 @@ public class Announcement extends BaseModel implements DisplayItemsParent {
|
|||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
|
|
||||||
public Status toStatus() {
|
@Override
|
||||||
Status s = Status.ofFake(id, content, publishedAt);
|
public void postprocess() throws ObjectValidationException{
|
||||||
s.createdAt = startsAt != null ? startsAt : publishedAt;
|
super.postprocess();
|
||||||
if (updatedAt != null) s.editedAt = updatedAt;
|
if(reactions==null) reactions=new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Status toStatus() {
|
||||||
|
Status s=Status.ofFake(id, content, publishedAt);
|
||||||
|
s.createdAt=startsAt != null ? startsAt : publishedAt;
|
||||||
|
s.reactions=reactions;
|
||||||
|
if(updatedAt != null) s.editedAt=updatedAt;
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package org.joinmastodon.android.model;
|
|||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.PointF;
|
import android.graphics.PointF;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.media.MediaMetadataRetriever;
|
||||||
|
|
||||||
import com.google.gson.annotations.SerializedName;
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
@@ -45,26 +46,26 @@ public class Attachment extends BaseModel{
|
|||||||
|
|
||||||
public int getWidth(){
|
public int getWidth(){
|
||||||
if(meta==null)
|
if(meta==null)
|
||||||
return 1920;
|
return 0;
|
||||||
if(meta.width>0)
|
if(meta.width>0)
|
||||||
return meta.width;
|
return meta.width;
|
||||||
if(meta.original!=null && meta.original.width>0)
|
if(meta.original!=null && meta.original.width>0)
|
||||||
return meta.original.width;
|
return meta.original.width;
|
||||||
if(meta.small!=null && meta.small.width>0)
|
if(meta.small!=null && meta.small.width>0)
|
||||||
return meta.small.width;
|
return meta.small.width;
|
||||||
return 1920;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getHeight(){
|
public int getHeight(){
|
||||||
if(meta==null)
|
if(meta==null)
|
||||||
return 1080;
|
return 0;
|
||||||
if(meta.height>0)
|
if(meta.height>0)
|
||||||
return meta.height;
|
return meta.height;
|
||||||
if(meta.original!=null && meta.original.height>0)
|
if(meta.original!=null && meta.original.height>0)
|
||||||
return meta.original.height;
|
return meta.original.height;
|
||||||
if(meta.small!=null && meta.small.height>0)
|
if(meta.small!=null && meta.small.height>0)
|
||||||
return meta.small.height;
|
return meta.small.height;
|
||||||
return 1080;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public double getDuration(){
|
public double getDuration(){
|
||||||
@@ -77,6 +78,13 @@ public class Attachment extends BaseModel{
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean hasSound() {
|
||||||
|
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
|
||||||
|
retriever.setDataSource(url);
|
||||||
|
String hasAudioStr = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_HAS_AUDIO);
|
||||||
|
return "yes".equals(hasAudioStr);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void postprocess() throws ObjectValidationException{
|
public void postprocess() throws ObjectValidationException{
|
||||||
super.postprocess();
|
super.postprocess();
|
||||||
|
|||||||
@@ -41,6 +41,12 @@ public class Emoji extends BaseModel{
|
|||||||
this.staticUrl = staticUrl;
|
this.staticUrl = staticUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getUrl(boolean playGifs){
|
||||||
|
String idealUrl=playGifs ? url : staticUrl;
|
||||||
|
if(idealUrl==null) return url==null ? staticUrl : url;
|
||||||
|
return idealUrl;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString(){
|
public String toString(){
|
||||||
return "Emoji{"+
|
return "Emoji{"+
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
package org.joinmastodon.android.model;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.GlobalUserPreferences;
|
||||||
|
import org.parceler.Parcel;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
|
||||||
|
import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest;
|
||||||
|
import me.grishka.appkit.utils.V;
|
||||||
|
|
||||||
|
@Parcel
|
||||||
|
public class EmojiReaction {
|
||||||
|
public List<Account> accounts;
|
||||||
|
public List<String> accountIds;
|
||||||
|
public int count;
|
||||||
|
public boolean me;
|
||||||
|
public String name;
|
||||||
|
public String url;
|
||||||
|
public String staticUrl;
|
||||||
|
|
||||||
|
public transient ImageLoaderRequest request;
|
||||||
|
|
||||||
|
public String getUrl(boolean playGifs){
|
||||||
|
String idealUrl=playGifs ? url : staticUrl;
|
||||||
|
if(idealUrl==null) return url==null ? staticUrl : url;
|
||||||
|
return idealUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static EmojiReaction of(Emoji info, Account me){
|
||||||
|
EmojiReaction reaction=new EmojiReaction();
|
||||||
|
reaction.me=true;
|
||||||
|
reaction.count=1;
|
||||||
|
reaction.name=info.shortcode;
|
||||||
|
reaction.url=info.url;
|
||||||
|
reaction.staticUrl=info.staticUrl;
|
||||||
|
reaction.accounts=new ArrayList<>(Collections.singleton(me));
|
||||||
|
reaction.accountIds=new ArrayList<>(Collections.singleton(me.id));
|
||||||
|
reaction.request=new UrlImageLoaderRequest(info.url, V.sp(24), V.sp(24));
|
||||||
|
return reaction;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static EmojiReaction of(String emoji, Account me){
|
||||||
|
EmojiReaction reaction=new EmojiReaction();
|
||||||
|
reaction.me=true;
|
||||||
|
reaction.count=1;
|
||||||
|
reaction.name=emoji;
|
||||||
|
reaction.accounts=new ArrayList<>(Collections.singleton(me));
|
||||||
|
reaction.accountIds=new ArrayList<>(Collections.singleton(me.id));
|
||||||
|
return reaction;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(Account self){
|
||||||
|
if(accounts==null) accounts=new ArrayList<>();
|
||||||
|
if(accountIds==null) accountIds=new ArrayList<>();
|
||||||
|
count++;
|
||||||
|
me=true;
|
||||||
|
accounts.add(self);
|
||||||
|
accountIds.add(self.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,6 +14,7 @@ public class FilterResult extends BaseModel {
|
|||||||
@Override
|
@Override
|
||||||
public void postprocess() throws ObjectValidationException {
|
public void postprocess() throws ObjectValidationException {
|
||||||
super.postprocess();
|
super.postprocess();
|
||||||
if (filter != null) filter.postprocess();
|
if(filter!=null) filter.postprocess();
|
||||||
|
if(keywordMatches==null) keywordMatches=List.of();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ public class Instance extends BaseModel{
|
|||||||
ci.domain=uri;
|
ci.domain=uri;
|
||||||
ci.normalizedDomain=IDN.toUnicode(uri);
|
ci.normalizedDomain=IDN.toUnicode(uri);
|
||||||
ci.description=Html.fromHtml(shortDescription).toString().trim();
|
ci.description=Html.fromHtml(shortDescription).toString().trim();
|
||||||
if(languages!=null && languages.size() > 0){
|
if(languages!=null&&languages.size()>0){
|
||||||
ci.language=languages.get(0);
|
ci.language=languages.get(0);
|
||||||
ci.languages=languages;
|
ci.languages=languages;
|
||||||
}else{
|
}else{
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
package org.joinmastodon.android.model;
|
package org.joinmastodon.android.model;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
import com.google.gson.annotations.SerializedName;
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.RequiredField;
|
import org.joinmastodon.android.api.RequiredField;
|
||||||
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
|
|
||||||
import androidx.annotation.StringRes;
|
import androidx.annotation.StringRes;
|
||||||
|
|
||||||
@@ -20,6 +23,40 @@ public class PushNotification extends BaseModel{
|
|||||||
@RequiredField
|
@RequiredField
|
||||||
public String body;
|
public String body;
|
||||||
|
|
||||||
|
public static PushNotification fromNotification(Context context, Notification notification){
|
||||||
|
PushNotification pushNotification = new PushNotification();
|
||||||
|
pushNotification.notificationType = switch(notification.type) {
|
||||||
|
case FOLLOW -> PushNotification.Type.FOLLOW;
|
||||||
|
case MENTION -> PushNotification.Type.MENTION;
|
||||||
|
case REBLOG -> PushNotification.Type.REBLOG;
|
||||||
|
case FAVORITE -> PushNotification.Type.FAVORITE;
|
||||||
|
case POLL -> PushNotification.Type.POLL;
|
||||||
|
case STATUS -> PushNotification.Type.STATUS;
|
||||||
|
case UPDATE -> PushNotification.Type.UPDATE;
|
||||||
|
case SIGN_UP -> PushNotification.Type.SIGN_UP;
|
||||||
|
case REPORT -> PushNotification.Type.REPORT;
|
||||||
|
//Follow request, and reactions are not supported by the API
|
||||||
|
default -> throw new IllegalStateException("Unexpected value: "+notification.type);
|
||||||
|
};
|
||||||
|
|
||||||
|
String notificationTitle = context.getString(switch(notification.type){
|
||||||
|
case FOLLOW -> R.string.user_followed_you;
|
||||||
|
case MENTION -> R.string.sk_notification_mention;
|
||||||
|
case REBLOG -> R.string.notification_boosted;
|
||||||
|
case FAVORITE -> R.string.user_favorited;
|
||||||
|
case POLL -> R.string.poll_ended;
|
||||||
|
case UPDATE -> R.string.sk_post_edited;
|
||||||
|
case SIGN_UP -> R.string.sk_signed_up;
|
||||||
|
case REPORT -> R.string.sk_reported;
|
||||||
|
default -> throw new IllegalStateException("Unexpected value: "+notification.type);
|
||||||
|
});
|
||||||
|
|
||||||
|
pushNotification.title = UiUtils.generateFormattedString(notificationTitle, notification.account.displayName).toString();
|
||||||
|
pushNotification.icon = notification.status.account.avatarStatic;
|
||||||
|
pushNotification.body = notification.status.getStrippedText();
|
||||||
|
return pushNotification;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString(){
|
public String toString(){
|
||||||
return "PushNotification{"+
|
return "PushNotification{"+
|
||||||
|
|||||||
@@ -13,16 +13,17 @@ import com.google.gson.JsonElement;
|
|||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import com.google.gson.JsonParseException;
|
import com.google.gson.JsonParseException;
|
||||||
|
|
||||||
import org.joinmastodon.android.GlobalUserPreferences;
|
|
||||||
import org.joinmastodon.android.api.ObjectValidationException;
|
import org.joinmastodon.android.api.ObjectValidationException;
|
||||||
import org.joinmastodon.android.api.RequiredField;
|
import org.joinmastodon.android.api.RequiredField;
|
||||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||||
|
import org.joinmastodon.android.events.EmojiReactionsUpdatedEvent;
|
||||||
import org.joinmastodon.android.events.StatusCountersUpdatedEvent;
|
import org.joinmastodon.android.events.StatusCountersUpdatedEvent;
|
||||||
import org.joinmastodon.android.ui.text.HtmlParser;
|
import org.joinmastodon.android.ui.text.HtmlParser;
|
||||||
import org.parceler.Parcel;
|
import org.parceler.Parcel;
|
||||||
|
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@Parcel
|
@Parcel
|
||||||
@@ -74,6 +75,9 @@ public class Status extends BaseModel implements DisplayItemsParent, Searchable{
|
|||||||
|
|
||||||
public Status quote; // can be boolean in calckey
|
public Status quote; // can be boolean in calckey
|
||||||
|
|
||||||
|
public List<EmojiReaction> reactions;
|
||||||
|
protected List<EmojiReaction> emojiReactions; // akkoma
|
||||||
|
|
||||||
public transient boolean filterRevealed;
|
public transient boolean filterRevealed;
|
||||||
public transient boolean spoilerRevealed;
|
public transient boolean spoilerRevealed;
|
||||||
public transient boolean sensitiveRevealed;
|
public transient boolean sensitiveRevealed;
|
||||||
@@ -96,7 +100,7 @@ public class Status extends BaseModel implements DisplayItemsParent, Searchable{
|
|||||||
t.postprocess();
|
t.postprocess();
|
||||||
for(Emoji e:emojis)
|
for(Emoji e:emojis)
|
||||||
e.postprocess();
|
e.postprocess();
|
||||||
if (mediaAttachments == null) mediaAttachments = List.of();
|
if (mediaAttachments == null) mediaAttachments=List.of();
|
||||||
for(Attachment a:mediaAttachments)
|
for(Attachment a:mediaAttachments)
|
||||||
a.postprocess();
|
a.postprocess();
|
||||||
account.postprocess();
|
account.postprocess();
|
||||||
@@ -110,10 +114,12 @@ public class Status extends BaseModel implements DisplayItemsParent, Searchable{
|
|||||||
for(FilterResult fr:filtered)
|
for(FilterResult fr:filtered)
|
||||||
fr.postprocess();
|
fr.postprocess();
|
||||||
|
|
||||||
if (!TextUtils.isEmpty(spoilerText)) sensitive = true;
|
spoilerRevealed=!hasSpoiler();
|
||||||
spoilerRevealed=TextUtils.isEmpty(spoilerText);
|
if(!spoilerRevealed) sensitive=true;
|
||||||
sensitiveRevealed=!sensitive;
|
sensitiveRevealed=!sensitive;
|
||||||
if (visibility.equals(StatusPrivacy.LOCAL)) localOnly = true;
|
if(visibility.equals(StatusPrivacy.LOCAL)) localOnly=true;
|
||||||
|
if(emojiReactions!=null) reactions=emojiReactions;
|
||||||
|
if(reactions==null) reactions=new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -171,6 +177,10 @@ public class Status extends BaseModel implements DisplayItemsParent, Searchable{
|
|||||||
pinned=ev.pinned;
|
pinned=ev.pinned;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void update(EmojiReactionsUpdatedEvent ev){
|
||||||
|
reactions=ev.reactions;
|
||||||
|
}
|
||||||
|
|
||||||
public Status getContentStatus(){
|
public Status getContentStatus(){
|
||||||
return reblog!=null ? reblog : this;
|
return reblog!=null ? reblog : this;
|
||||||
}
|
}
|
||||||
@@ -181,6 +191,10 @@ public class Status extends BaseModel implements DisplayItemsParent, Searchable{
|
|||||||
return strippedText;
|
return strippedText;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean hasSpoiler(){
|
||||||
|
return !TextUtils.isEmpty(spoilerText);
|
||||||
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public Status clone(){
|
public Status clone(){
|
||||||
@@ -194,17 +208,18 @@ public class Status extends BaseModel implements DisplayItemsParent, Searchable{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static Status ofFake(String id, String text, Instant createdAt) {
|
public static Status ofFake(String id, String text, Instant createdAt) {
|
||||||
Status s = new Status();
|
Status s=new Status();
|
||||||
s.id = id;
|
s.id=id;
|
||||||
s.mediaAttachments = List.of();
|
s.mediaAttachments=List.of();
|
||||||
s.createdAt = createdAt;
|
s.createdAt=createdAt;
|
||||||
s.content = s.text = text;
|
s.content=s.text=text;
|
||||||
s.spoilerText = "";
|
s.spoilerText="";
|
||||||
s.visibility = StatusPrivacy.PUBLIC;
|
s.visibility=StatusPrivacy.PUBLIC;
|
||||||
s.mentions = List.of();
|
s.reactions=List.of();
|
||||||
s.tags = List.of();
|
s.mentions=List.of();
|
||||||
s.emojis = List.of();
|
s.tags =List.of();
|
||||||
s.filtered = List.of();
|
s.emojis=List.of();
|
||||||
|
s.filtered=List.of();
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,21 +231,21 @@ public class Status extends BaseModel implements DisplayItemsParent, Searchable{
|
|||||||
public static class StatusDeserializer implements JsonDeserializer<Status> {
|
public static class StatusDeserializer implements JsonDeserializer<Status> {
|
||||||
@Override
|
@Override
|
||||||
public Status deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
|
public Status deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
|
||||||
JsonObject obj = json.getAsJsonObject();
|
JsonObject obj=json.getAsJsonObject();
|
||||||
|
|
||||||
Status quote = null;
|
Status quote=null;
|
||||||
if (obj.has("quote") && obj.get("quote").isJsonObject())
|
if (obj.has("quote") && obj.get("quote").isJsonObject())
|
||||||
quote = gson.fromJson(obj.get("quote"), Status.class);
|
quote=gson.fromJson(obj.get("quote"), Status.class);
|
||||||
obj.remove("quote");
|
obj.remove("quote");
|
||||||
|
|
||||||
Status reblog = null;
|
Status reblog=null;
|
||||||
if (obj.has("reblog"))
|
if (obj.has("reblog"))
|
||||||
reblog = gson.fromJson(obj.get("reblog"), Status.class);
|
reblog=gson.fromJson(obj.get("reblog"), Status.class);
|
||||||
obj.remove("reblog");
|
obj.remove("reblog");
|
||||||
|
|
||||||
Status status = gsonWithoutDeserializer.fromJson(json, Status.class);
|
Status status=gsonWithoutDeserializer.fromJson(json, Status.class);
|
||||||
status.quote = quote;
|
status.quote=quote;
|
||||||
status.reblog = reblog;
|
status.reblog=reblog;
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -37,6 +37,16 @@ public class OutlineProviders{
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private final static int BUTTON_BG_HEIGHT=V.dp(40);
|
||||||
|
public static final ViewOutlineProvider M3_BUTTON=new ViewOutlineProvider(){
|
||||||
|
@Override
|
||||||
|
public void getOutline(View view, Outline outline){
|
||||||
|
int viewHeight=view.getHeight();
|
||||||
|
int top=Math.floorDiv(viewHeight - BUTTON_BG_HEIGHT, 2);
|
||||||
|
outline.setRoundRect(0, top, view.getWidth(), top + BUTTON_BG_HEIGHT, V.dp(20));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
public static ViewOutlineProvider roundedRect(int dp){
|
public static ViewOutlineProvider roundedRect(int dp){
|
||||||
ViewOutlineProvider provider=roundedRects.get(dp);
|
ViewOutlineProvider provider=roundedRects.get(dp);
|
||||||
if(provider!=null)
|
if(provider!=null)
|
||||||
|
|||||||
@@ -111,15 +111,24 @@ public class AccountCardStatusDisplayItem extends StatusDisplayItem{
|
|||||||
rejectWrap=findViewById(R.id.reject_btn_wrap);
|
rejectWrap=findViewById(R.id.reject_btn_wrap);
|
||||||
|
|
||||||
View card=findViewById(R.id.card);
|
View card=findViewById(R.id.card);
|
||||||
card.setOutlineProvider(OutlineProviders.roundedRect(6));
|
card.setOutlineProvider(OutlineProviders.roundedRect(12));
|
||||||
card.setClipToOutline(true);
|
card.setClipToOutline(true);
|
||||||
avatar.setOutlineProvider(OutlineProviders.roundedRect(12));
|
avatar.setOutlineProvider(OutlineProviders.roundedRect(15));
|
||||||
avatar.setClipToOutline(true);
|
avatar.setClipToOutline(true);
|
||||||
cover.setOutlineProvider(OutlineProviders.roundedRect(3));
|
View border=findViewById(R.id.avatar_border);
|
||||||
|
border.setOutlineProvider(OutlineProviders.roundedRect(17));
|
||||||
|
border.setClipToOutline(true);
|
||||||
|
cover.setOutlineProvider(OutlineProviders.roundedRect(9));
|
||||||
cover.setClipToOutline(true);
|
cover.setClipToOutline(true);
|
||||||
actionButton.setOnClickListener(this::onActionButtonClick);
|
actionButton.setOnClickListener(this::onActionButtonClick);
|
||||||
acceptButton.setOnClickListener(this::onFollowRequestButtonClick);
|
acceptButton.setOnClickListener(this::onFollowRequestButtonClick);
|
||||||
rejectButton.setOnClickListener(this::onFollowRequestButtonClick);
|
rejectButton.setOnClickListener(this::onFollowRequestButtonClick);
|
||||||
|
card.setOnClickListener(v->onClick());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabled(){
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -132,18 +141,19 @@ public class AccountCardStatusDisplayItem extends StatusDisplayItem{
|
|||||||
postsCount.setText(UiUtils.abbreviateNumber(item.account.statusesCount));
|
postsCount.setText(UiUtils.abbreviateNumber(item.account.statusesCount));
|
||||||
followersLabel.setText(item.parentFragment.getResources().getQuantityString(R.plurals.followers, (int)Math.min(999, item.account.followersCount)));
|
followersLabel.setText(item.parentFragment.getResources().getQuantityString(R.plurals.followers, (int)Math.min(999, item.account.followersCount)));
|
||||||
followingLabel.setText(item.parentFragment.getResources().getQuantityString(R.plurals.following, (int)Math.min(999, item.account.followingCount)));
|
followingLabel.setText(item.parentFragment.getResources().getQuantityString(R.plurals.following, (int)Math.min(999, item.account.followingCount)));
|
||||||
postsLabel.setText(item.parentFragment.getResources().getQuantityString(R.plurals.x_posts, (int)(item.account.statusesCount%1000), item.account.statusesCount));
|
postsLabel.setText(item.parentFragment.getResources().getQuantityString(R.plurals.sk_posts_count_label, (int)(item.account.statusesCount%1000), item.account.statusesCount));
|
||||||
followersCount.setVisibility(item.account.followersCount < 0 ? View.GONE : View.VISIBLE);
|
followersCount.setVisibility(item.account.followersCount < 0 ? View.GONE : View.VISIBLE);
|
||||||
followersLabel.setVisibility(item.account.followersCount < 0 ? View.GONE : View.VISIBLE);
|
followersLabel.setVisibility(item.account.followersCount < 0 ? View.GONE : View.VISIBLE);
|
||||||
followingCount.setVisibility(item.account.followingCount < 0 ? View.GONE : View.VISIBLE);
|
followingCount.setVisibility(item.account.followingCount < 0 ? View.GONE : View.VISIBLE);
|
||||||
followingLabel.setVisibility(item.account.followingCount < 0 ? View.GONE : View.VISIBLE);
|
followingLabel.setVisibility(item.account.followingCount < 0 ? View.GONE : View.VISIBLE);
|
||||||
relationship=item.parentFragment.getRelationship(item.account.id);
|
relationship=item.parentFragment.getRelationship(item.account.id);
|
||||||
if(item.notification.type == Notification.Type.FOLLOW_REQUEST && (relationship == null || !relationship.followedBy)){
|
UiUtils.setExtraTextInfo(item.parentFragment.getContext(), null, findViewById(R.id.pronouns), true, false, false, item.account);
|
||||||
|
|
||||||
|
if(item.notification.type==Notification.Type.FOLLOW_REQUEST && (relationship==null || !relationship.followedBy)){
|
||||||
actionWrap.setVisibility(View.GONE);
|
actionWrap.setVisibility(View.GONE);
|
||||||
acceptWrap.setVisibility(View.VISIBLE);
|
acceptWrap.setVisibility(View.VISIBLE);
|
||||||
rejectWrap.setVisibility(View.VISIBLE);
|
rejectWrap.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
// i hate that i wasn't able to do this in xml
|
|
||||||
acceptButton.setCompoundDrawableTintList(acceptButton.getTextColors());
|
acceptButton.setCompoundDrawableTintList(acceptButton.getTextColors());
|
||||||
acceptProgress.setIndeterminateTintList(acceptButton.getTextColors());
|
acceptProgress.setIndeterminateTintList(acceptButton.getTextColors());
|
||||||
rejectButton.setCompoundDrawableTintList(rejectButton.getTextColors());
|
rejectButton.setCompoundDrawableTintList(rejectButton.getTextColors());
|
||||||
@@ -163,7 +173,7 @@ public class AccountCardStatusDisplayItem extends StatusDisplayItem{
|
|||||||
private void onFollowRequestButtonClick(View v) {
|
private void onFollowRequestButtonClick(View v) {
|
||||||
itemView.setHasTransientState(true);
|
itemView.setHasTransientState(true);
|
||||||
UiUtils.handleFollowRequest((Activity) v.getContext(), item.account, item.parentFragment.getAccountID(), null, v == acceptButton, relationship, rel -> {
|
UiUtils.handleFollowRequest((Activity) v.getContext(), item.account, item.parentFragment.getAccountID(), null, v == acceptButton, relationship, rel -> {
|
||||||
if(v.getContext()==null) return;
|
if(v.getContext()==null || rel==null) return;
|
||||||
itemView.setHasTransientState(false);
|
itemView.setHasTransientState(false);
|
||||||
item.parentFragment.putRelationship(item.account.id, rel);
|
item.parentFragment.putRelationship(item.account.id, rel);
|
||||||
RecyclerView.Adapter<? extends RecyclerView.ViewHolder> adapter = getBindingAdapter();
|
RecyclerView.Adapter<? extends RecyclerView.ViewHolder> adapter = getBindingAdapter();
|
||||||
|
|||||||
@@ -3,18 +3,17 @@ package org.joinmastodon.android.ui.displayitems;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.Space;
|
import android.widget.Space;
|
||||||
import android.widget.TextView;
|
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
||||||
|
|
||||||
import me.grishka.appkit.utils.V;
|
import me.grishka.appkit.utils.V;
|
||||||
|
|
||||||
public class InsetDummyStatusDisplayItem extends StatusDisplayItem {
|
public class DummyStatusDisplayItem extends StatusDisplayItem {
|
||||||
private final boolean addMediaGridMargin;
|
|
||||||
|
|
||||||
public InsetDummyStatusDisplayItem(String parentID, BaseStatusListFragment<?> parentFragment, boolean addMediaGridMargin) {
|
public DummyStatusDisplayItem(String parentID, BaseStatusListFragment<?> parentFragment) {
|
||||||
super(parentID, parentFragment);
|
super(parentID, parentFragment);
|
||||||
this.addMediaGridMargin = addMediaGridMargin;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -22,20 +21,22 @@ public class InsetDummyStatusDisplayItem extends StatusDisplayItem {
|
|||||||
return Type.DUMMY;
|
return Type.DUMMY;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Holder extends StatusDisplayItem.Holder<InsetDummyStatusDisplayItem> {
|
public static class Holder extends StatusDisplayItem.Holder<DummyStatusDisplayItem> {
|
||||||
|
private final RecyclerView.LayoutParams params;
|
||||||
|
|
||||||
public Holder(Context context) {
|
public Holder(Context context) {
|
||||||
super(new Space(context));
|
super(new Space(context));
|
||||||
}
|
params=new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0);
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBind(InsetDummyStatusDisplayItem item) {
|
|
||||||
// BetterItemAnimator appears not to handle InsetStatusItemDecoration's getItemOffsets
|
// BetterItemAnimator appears not to handle InsetStatusItemDecoration's getItemOffsets
|
||||||
// correctly, causing removed inset views to jump while animating. i don't quite
|
// correctly, causing removed inset views to jump while animating. i don't quite
|
||||||
// understand it, but this workaround appears to work.
|
// understand it, but this workaround appears to work.
|
||||||
// see InsetStatusItemDecoration#getItemOffsets
|
// see InsetStatusItemDecoration#getItemOffsets
|
||||||
ViewGroup.MarginLayoutParams params = new ViewGroup.MarginLayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0);
|
params.setMargins(0, 0, 0, V.dp(16));
|
||||||
params.setMargins(0, item.addMediaGridMargin ? V.dp(4) : 0, 0, V.dp(16));
|
|
||||||
itemView.setLayoutParams(params);
|
itemView.setLayoutParams(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBind(DummyStatusDisplayItem item) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,417 @@
|
|||||||
|
package org.joinmastodon.android.ui.displayitems;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.graphics.drawable.Animatable;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.util.DisplayMetrics;
|
||||||
|
import android.util.Pair;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.ImageButton;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.ProgressBar;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
import androidx.recyclerview.widget.LinearSmoothScroller;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.E;
|
||||||
|
import org.joinmastodon.android.GlobalUserPreferences;
|
||||||
|
import org.joinmastodon.android.R;
|
||||||
|
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||||
|
import org.joinmastodon.android.api.requests.announcements.AddAnnouncementReaction;
|
||||||
|
import org.joinmastodon.android.api.requests.announcements.DeleteAnnouncementReaction;
|
||||||
|
import org.joinmastodon.android.api.requests.statuses.AddStatusReaction;
|
||||||
|
import org.joinmastodon.android.api.requests.statuses.DeleteStatusReaction;
|
||||||
|
import org.joinmastodon.android.api.requests.statuses.PleromaAddStatusReaction;
|
||||||
|
import org.joinmastodon.android.api.requests.statuses.PleromaDeleteStatusReaction;
|
||||||
|
import org.joinmastodon.android.api.session.AccountSession;
|
||||||
|
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||||
|
import org.joinmastodon.android.events.EmojiReactionsUpdatedEvent;
|
||||||
|
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
||||||
|
import org.joinmastodon.android.fragments.account_list.StatusEmojiReactionsListFragment;
|
||||||
|
import org.joinmastodon.android.model.Account;
|
||||||
|
import org.joinmastodon.android.model.Emoji;
|
||||||
|
import org.joinmastodon.android.model.EmojiReaction;
|
||||||
|
import org.joinmastodon.android.model.Status;
|
||||||
|
import org.joinmastodon.android.ui.CustomEmojiPopupKeyboard;
|
||||||
|
import org.joinmastodon.android.ui.utils.TextDrawable;
|
||||||
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
|
import org.joinmastodon.android.ui.views.ProgressBarButton;
|
||||||
|
|
||||||
|
import me.grishka.appkit.Nav;
|
||||||
|
import me.grishka.appkit.api.Callback;
|
||||||
|
import me.grishka.appkit.api.ErrorResponse;
|
||||||
|
import me.grishka.appkit.imageloader.ImageLoaderRecyclerAdapter;
|
||||||
|
import me.grishka.appkit.imageloader.ImageLoaderViewHolder;
|
||||||
|
import me.grishka.appkit.imageloader.ListImageLoaderWrapper;
|
||||||
|
import me.grishka.appkit.imageloader.RecyclerViewDelegate;
|
||||||
|
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
|
||||||
|
import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest;
|
||||||
|
import me.grishka.appkit.utils.BindableViewHolder;
|
||||||
|
import me.grishka.appkit.utils.V;
|
||||||
|
import me.grishka.appkit.views.UsableRecyclerView;
|
||||||
|
|
||||||
|
public class EmojiReactionsStatusDisplayItem extends StatusDisplayItem {
|
||||||
|
public final Status status;
|
||||||
|
private final Drawable placeholder;
|
||||||
|
private final boolean hideEmpty, forAnnouncement, playGifs;
|
||||||
|
private final String accountID;
|
||||||
|
private static final float ALPHA_DISABLED=0.55f;
|
||||||
|
|
||||||
|
public EmojiReactionsStatusDisplayItem(String parentID, BaseStatusListFragment<?> parentFragment, Status status, String accountID, boolean hideEmpty, boolean forAnnouncement) {
|
||||||
|
super(parentID, parentFragment);
|
||||||
|
this.status=status;
|
||||||
|
this.hideEmpty=hideEmpty;
|
||||||
|
this.forAnnouncement=forAnnouncement;
|
||||||
|
this.accountID=accountID;
|
||||||
|
placeholder=parentFragment.getContext().getDrawable(R.drawable.image_placeholder).mutate();
|
||||||
|
placeholder.setBounds(0, 0, V.sp(24), V.sp(24));
|
||||||
|
playGifs=GlobalUserPreferences.playGifs;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getImageCount(){
|
||||||
|
return (int) status.reactions.stream().filter(r->r.getUrl(playGifs)!=null).count();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ImageLoaderRequest getImageRequest(int index){
|
||||||
|
return status.reactions.get(index).request;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type getType(){
|
||||||
|
return Type.EMOJI_REACTIONS;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isHidden(){
|
||||||
|
return status.reactions.isEmpty() && hideEmpty;
|
||||||
|
}
|
||||||
|
|
||||||
|
// borrowed from ProfileFragment
|
||||||
|
private void setActionProgressVisible(Holder.EmojiReactionViewHolder vh, boolean visible){
|
||||||
|
if(vh==null) return;
|
||||||
|
vh.progress.setVisibility(visible ? View.VISIBLE : View.GONE);
|
||||||
|
vh.btn.setClickable(!visible);
|
||||||
|
vh.btn.setAlpha(visible ? ALPHA_DISABLED : 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private MastodonAPIRequest<?> createRequest(String name, int count, boolean delete, Holder.EmojiReactionViewHolder vh, Runnable cb, Runnable err){
|
||||||
|
setActionProgressVisible(vh, true);
|
||||||
|
boolean ak=parentFragment.isInstanceAkkoma();
|
||||||
|
boolean keepSpinning=delete && count == 1;
|
||||||
|
if(forAnnouncement){
|
||||||
|
MastodonAPIRequest<Object> req=delete
|
||||||
|
? new DeleteAnnouncementReaction(status.id, name)
|
||||||
|
: new AddAnnouncementReaction(status.id, name);
|
||||||
|
return req.setCallback(new Callback<>(){
|
||||||
|
@Override
|
||||||
|
public void onSuccess(Object result){
|
||||||
|
if(!keepSpinning) setActionProgressVisible(vh, false);
|
||||||
|
cb.run();
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void onError(ErrorResponse error){
|
||||||
|
setActionProgressVisible(vh, false);
|
||||||
|
error.showToast(parentFragment.getContext());
|
||||||
|
if(err!=null) err.run();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}else{
|
||||||
|
MastodonAPIRequest<Status> req=delete
|
||||||
|
? (ak ? new PleromaDeleteStatusReaction(status.id, name) : new DeleteStatusReaction(status.id, name))
|
||||||
|
: (ak ? new PleromaAddStatusReaction(status.id, name) : new AddStatusReaction(status.id, name));
|
||||||
|
return req.setCallback(new Callback<>(){
|
||||||
|
@Override
|
||||||
|
public void onSuccess(Status result){
|
||||||
|
if(!keepSpinning) setActionProgressVisible(vh, false);
|
||||||
|
cb.run();
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void onError(ErrorResponse error){
|
||||||
|
setActionProgressVisible(vh, false);
|
||||||
|
error.showToast(parentFragment.getContext());
|
||||||
|
if(err!=null) err.run();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Holder extends StatusDisplayItem.Holder<EmojiReactionsStatusDisplayItem> implements ImageLoaderViewHolder, CustomEmojiPopupKeyboard.Listener {
|
||||||
|
private final UsableRecyclerView list;
|
||||||
|
private final LinearLayout root, line;
|
||||||
|
private CustomEmojiPopupKeyboard emojiKeyboard;
|
||||||
|
private final View space;
|
||||||
|
private final ImageButton addButton;
|
||||||
|
private final ProgressBar progress;
|
||||||
|
private final EmojiReactionsAdapter adapter;
|
||||||
|
private final ListImageLoaderWrapper imgLoader;
|
||||||
|
|
||||||
|
public Holder(Activity activity, ViewGroup parent) {
|
||||||
|
super(activity, R.layout.display_item_emoji_reactions, parent);
|
||||||
|
root=(LinearLayout) itemView;
|
||||||
|
line=findViewById(R.id.line);
|
||||||
|
list=findViewById(R.id.list);
|
||||||
|
imgLoader=new ListImageLoaderWrapper(activity, list, new RecyclerViewDelegate(list), null);
|
||||||
|
list.setAdapter(adapter=new EmojiReactionsAdapter(this, imgLoader));
|
||||||
|
addButton=findViewById(R.id.add_btn);
|
||||||
|
progress=findViewById(R.id.progress);
|
||||||
|
addButton.setOnClickListener(this::onReactClick);
|
||||||
|
space=findViewById(R.id.space);
|
||||||
|
list.setLayoutManager(new LinearLayoutManager(activity, LinearLayoutManager.HORIZONTAL, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBind(EmojiReactionsStatusDisplayItem item) {
|
||||||
|
if(emojiKeyboard != null) root.removeView(emojiKeyboard.getView());
|
||||||
|
AccountSession session=item.parentFragment.getSession();
|
||||||
|
item.status.reactions.forEach(r->r.request=r.getUrl(item.playGifs)!=null
|
||||||
|
? new UrlImageLoaderRequest(r.getUrl(item.playGifs), V.sp(24), V.sp(24))
|
||||||
|
: null);
|
||||||
|
emojiKeyboard=new CustomEmojiPopupKeyboard(
|
||||||
|
(Activity) item.parentFragment.getContext(),
|
||||||
|
AccountSessionManager.getInstance().getCustomEmojis(session.domain),
|
||||||
|
session.domain, true);
|
||||||
|
emojiKeyboard.setListener(this);
|
||||||
|
space.setVisibility(View.GONE);
|
||||||
|
root.addView(emojiKeyboard.getView());
|
||||||
|
boolean hidden=item.isHidden();
|
||||||
|
root.setVisibility(hidden ? View.GONE : View.VISIBLE);
|
||||||
|
line.setVisibility(hidden ? View.GONE : View.VISIBLE);
|
||||||
|
line.setPadding(
|
||||||
|
list.getPaddingLeft(),
|
||||||
|
hidden ? 0 : V.dp(8),
|
||||||
|
list.getPaddingRight(),
|
||||||
|
item.forAnnouncement ? V.dp(8) : 0
|
||||||
|
);
|
||||||
|
imgLoader.updateImages();
|
||||||
|
adapter.notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void hideEmojiKeyboard(){
|
||||||
|
space.setVisibility(View.GONE);
|
||||||
|
addButton.setSelected(false);
|
||||||
|
if(emojiKeyboard.isVisible()) emojiKeyboard.toggleKeyboardPopup(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEmojiSelected(Emoji emoji) {
|
||||||
|
addEmojiReaction(emoji.shortcode, emoji);
|
||||||
|
hideEmojiKeyboard();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEmojiSelected(String emoji){
|
||||||
|
addEmojiReaction(emoji, null);
|
||||||
|
hideEmojiKeyboard();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addEmojiReaction(String emoji, Emoji info) {
|
||||||
|
int countBefore=item.status.reactions.size();
|
||||||
|
for(int i=0; i<item.status.reactions.size(); i++){
|
||||||
|
EmojiReaction r=item.status.reactions.get(i);
|
||||||
|
if(r.name.equals(emoji) && r.me){
|
||||||
|
RecyclerView.SmoothScroller scroller=new LinearSmoothScroller(list.getContext());
|
||||||
|
scroller.setTargetPosition(i);
|
||||||
|
list.getLayoutManager().startSmoothScroll(scroller);
|
||||||
|
return; // nothing to do, already added
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
progress.setVisibility(View.VISIBLE);
|
||||||
|
addButton.setClickable(false);
|
||||||
|
addButton.setAlpha(ALPHA_DISABLED);
|
||||||
|
|
||||||
|
Runnable resetBtn=()->{
|
||||||
|
progress.setVisibility(View.GONE);
|
||||||
|
addButton.setClickable(true);
|
||||||
|
addButton.setAlpha(1f);
|
||||||
|
};
|
||||||
|
Account me=AccountSessionManager.get(item.accountID).self;
|
||||||
|
EmojiReaction existing=null;
|
||||||
|
for(int i=0; i<item.status.reactions.size(); i++){
|
||||||
|
EmojiReaction r=item.status.reactions.get(i);
|
||||||
|
if(r.name.equals(emoji)){
|
||||||
|
existing=r;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EmojiReaction finalExisting=existing;
|
||||||
|
item.createRequest(emoji, existing==null ? 1 : existing.count, false, null, ()->{
|
||||||
|
resetBtn.run();
|
||||||
|
if(finalExisting==null){
|
||||||
|
int pos=item.status.reactions.size();
|
||||||
|
item.status.reactions.add(pos, info!=null ? EmojiReaction.of(info, me) : EmojiReaction.of(emoji, me));
|
||||||
|
adapter.notifyItemRangeInserted(pos, 1);
|
||||||
|
RecyclerView.SmoothScroller scroller=new LinearSmoothScroller(list.getContext());
|
||||||
|
scroller.setTargetPosition(pos);
|
||||||
|
list.getLayoutManager().startSmoothScroll(scroller);
|
||||||
|
}else{
|
||||||
|
finalExisting.add(me);
|
||||||
|
adapter.notifyItemChanged(item.status.reactions.indexOf(finalExisting));
|
||||||
|
}
|
||||||
|
E.post(new EmojiReactionsUpdatedEvent(item.status.id, item.status.reactions, countBefore==0, adapter.parentHolder));
|
||||||
|
}, resetBtn).exec(item.accountID);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBackspace() {}
|
||||||
|
|
||||||
|
private void onReactClick(View v){
|
||||||
|
emojiKeyboard.toggleKeyboardPopup(null);
|
||||||
|
v.setSelected(emojiKeyboard.isVisible());
|
||||||
|
space.setVisibility(emojiKeyboard.isVisible() ? View.VISIBLE : View.GONE);
|
||||||
|
DisplayMetrics displayMetrics = new DisplayMetrics();
|
||||||
|
int[] locationOnScreen = new int[2];
|
||||||
|
((Activity) v.getContext()).getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
|
||||||
|
v.getLocationOnScreen(locationOnScreen);
|
||||||
|
double fromScreenTop = (double) locationOnScreen[1] / displayMetrics.heightPixels;
|
||||||
|
if (fromScreenTop > 0.75) {
|
||||||
|
item.parentFragment.scrollBy(0, (int) (displayMetrics.heightPixels * 0.3));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setImage(int index, Drawable image){
|
||||||
|
View child=list.getChildAt(index);
|
||||||
|
if(child==null) return;
|
||||||
|
((EmojiReactionViewHolder) list.getChildViewHolder(child)).setImage(index, image);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clearImage(int index){
|
||||||
|
if(item.status.reactions.get(index).getUrl(item.playGifs)==null) return;
|
||||||
|
setImage(index, item.placeholder);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class EmojiReactionsAdapter extends UsableRecyclerView.Adapter<EmojiReactionViewHolder> implements ImageLoaderRecyclerAdapter{
|
||||||
|
ListImageLoaderWrapper imgLoader;
|
||||||
|
Holder parentHolder;
|
||||||
|
|
||||||
|
public EmojiReactionsAdapter(Holder parentHolder, ListImageLoaderWrapper imgLoader){
|
||||||
|
super(imgLoader);
|
||||||
|
this.parentHolder=parentHolder;
|
||||||
|
this.imgLoader=imgLoader;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public EmojiReactionViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
|
||||||
|
return new EmojiReactionViewHolder(parent.getContext(), list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(EmojiReactionViewHolder holder, int position){
|
||||||
|
holder.bind(Pair.create(item, item.status.reactions.get(position)));
|
||||||
|
super.onBindViewHolder(holder, position);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount(){
|
||||||
|
return item.status.reactions.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getImageCountForItem(int position){
|
||||||
|
return item.status.reactions.get(position).getUrl(item.playGifs)==null ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ImageLoaderRequest getImageRequest(int position, int image){
|
||||||
|
return item.status.reactions.get(position).request;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class EmojiReactionViewHolder extends BindableViewHolder<Pair<EmojiReactionsStatusDisplayItem, EmojiReaction>> implements ImageLoaderViewHolder{
|
||||||
|
private final ProgressBarButton btn;
|
||||||
|
private final ProgressBar progress;
|
||||||
|
|
||||||
|
public EmojiReactionViewHolder(Context context, RecyclerView list){
|
||||||
|
super(context, R.layout.item_emoji_reaction, list);
|
||||||
|
btn=findViewById(R.id.btn);
|
||||||
|
progress=findViewById(R.id.progress);
|
||||||
|
itemView.setClickable(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setImage(int index, Drawable drawable){
|
||||||
|
drawable.setBounds(0, 0, V.sp(24), V.sp(24));
|
||||||
|
btn.setCompoundDrawablesRelative(drawable, null, null, null);
|
||||||
|
if(drawable instanceof Animatable) ((Animatable) drawable).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clearImage(int index){
|
||||||
|
setImage(index, item.first.placeholder);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBind(Pair<EmojiReactionsStatusDisplayItem, EmojiReaction> item){
|
||||||
|
item.first.setActionProgressVisible(this, false);
|
||||||
|
EmojiReactionsStatusDisplayItem parent=item.first;
|
||||||
|
EmojiReaction reaction=item.second;
|
||||||
|
btn.setText(UiUtils.abbreviateNumber(reaction.count));
|
||||||
|
btn.setContentDescription(reaction.name);
|
||||||
|
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) btn.setTooltipText(reaction.name);
|
||||||
|
if(reaction.getUrl(parent.playGifs)==null){
|
||||||
|
Paint p=new Paint();
|
||||||
|
p.setTextSize(V.sp(18));
|
||||||
|
TextDrawable drawable=new TextDrawable(p, reaction.name);
|
||||||
|
btn.setCompoundDrawablesRelative(drawable, null, null, null);
|
||||||
|
}else{
|
||||||
|
btn.setCompoundDrawablesRelative(item.first.placeholder, null, null, null);
|
||||||
|
}
|
||||||
|
btn.setSelected(reaction.me);
|
||||||
|
btn.setOnClickListener(e->{
|
||||||
|
boolean deleting=reaction.me;
|
||||||
|
parent.createRequest(reaction.name, reaction.count, deleting, this, ()->{
|
||||||
|
EmojiReactionsAdapter adapter = (EmojiReactionsAdapter) getBindingAdapter();
|
||||||
|
for(int i=0; i<parent.status.reactions.size(); i++){
|
||||||
|
EmojiReaction r=parent.status.reactions.get(i);
|
||||||
|
if(!r.name.equals(reaction.name)) continue;
|
||||||
|
if(deleting && r.count==1) {
|
||||||
|
parent.status.reactions.remove(i);
|
||||||
|
adapter.notifyItemRemoved(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
r.me=!deleting;
|
||||||
|
if(deleting) r.count--;
|
||||||
|
else r.count++;
|
||||||
|
adapter.notifyItemChanged(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(parent.isHidden()){
|
||||||
|
adapter.parentHolder.root.setVisibility(View.GONE);
|
||||||
|
adapter.parentHolder.line.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
E.post(new EmojiReactionsUpdatedEvent(parent.status.id, parent.status.reactions, parent.status.reactions.isEmpty(), adapter.parentHolder));
|
||||||
|
adapter.parentHolder.imgLoader.updateImages();
|
||||||
|
}, null).exec(parent.parentFragment.getAccountID());
|
||||||
|
});
|
||||||
|
|
||||||
|
if (parent.parentFragment.isInstanceAkkoma()) {
|
||||||
|
// glitch-soc doesn't have this, afaik
|
||||||
|
btn.setOnLongClickListener(e->{
|
||||||
|
EmojiReaction emojiReaction=parent.status.reactions.get(getAbsoluteAdapterPosition());
|
||||||
|
Bundle args=new Bundle();
|
||||||
|
args.putString("account", parent.parentFragment.getAccountID());
|
||||||
|
args.putString("statusID", parent.status.id);
|
||||||
|
int atSymbolIndex = emojiReaction.name.indexOf("@");
|
||||||
|
args.putString("emoji", atSymbolIndex != -1 ? emojiReaction.name.substring(0, atSymbolIndex) : emojiReaction.name);
|
||||||
|
args.putString("url", emojiReaction.getUrl(parent.playGifs));
|
||||||
|
args.putInt("count", emojiReaction.count);
|
||||||
|
Nav.go(parent.parentFragment.getActivity(), StatusEmojiReactionsListFragment.class, args);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -77,18 +77,21 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private static final float ALPHA_PRESSED=0.55f;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
opacityOut = new AlphaAnimation(1, 0.55f);
|
opacityOut = new AlphaAnimation(1, ALPHA_PRESSED);
|
||||||
opacityOut.setDuration(300);
|
opacityOut.setDuration(300);
|
||||||
opacityOut.setInterpolator(CubicBezierInterpolator.DEFAULT);
|
opacityOut.setInterpolator(CubicBezierInterpolator.DEFAULT);
|
||||||
opacityOut.setFillAfter(true);
|
opacityOut.setFillAfter(true);
|
||||||
opacityIn = new AlphaAnimation(0.55f, 1);
|
opacityIn = new AlphaAnimation(ALPHA_PRESSED, 1);
|
||||||
opacityIn.setDuration(400);
|
opacityIn.setDuration(400);
|
||||||
opacityIn.setInterpolator(CubicBezierInterpolator.DEFAULT);
|
opacityIn.setInterpolator(CubicBezierInterpolator.DEFAULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Holder(Activity activity, ViewGroup parent){
|
public Holder(Activity activity, ViewGroup parent){
|
||||||
super(activity, R.layout.display_item_footer, parent);
|
super(activity, R.layout.display_item_footer, parent);
|
||||||
|
|
||||||
replies=findViewById(R.id.reply);
|
replies=findViewById(R.id.reply);
|
||||||
boosts=findViewById(R.id.boost);
|
boosts=findViewById(R.id.boost);
|
||||||
favorites=findViewById(R.id.favorite);
|
favorites=findViewById(R.id.favorite);
|
||||||
|
|||||||
@@ -132,9 +132,9 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static class Holder extends StatusDisplayItem.Holder<HeaderStatusDisplayItem> implements ImageLoaderViewHolder{
|
public static class Holder extends StatusDisplayItem.Holder<HeaderStatusDisplayItem> implements ImageLoaderViewHolder{
|
||||||
private final TextView name, timeAndUsername, extraText, pronouns;
|
private final TextView name, time, username, extraText, pronouns;
|
||||||
private final View collapseBtn;
|
private final View collapseBtn, timeUsernameSeparator;
|
||||||
private final ImageView avatar, more, visibility, deleteNotification, unreadIndicator, markAsRead, collapseBtnIcon;
|
private final ImageView avatar, more, visibility, deleteNotification, unreadIndicator, markAsRead, collapseBtnIcon, botIcon;
|
||||||
private final PopupMenu optionsMenu;
|
private final PopupMenu optionsMenu;
|
||||||
private Relationship relationship;
|
private Relationship relationship;
|
||||||
private APIRequest<?> currentRelationshipRequest;
|
private APIRequest<?> currentRelationshipRequest;
|
||||||
@@ -146,7 +146,10 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
|
|||||||
protected Holder(Activity activity, @LayoutRes int layout, ViewGroup parent){
|
protected Holder(Activity activity, @LayoutRes int layout, ViewGroup parent){
|
||||||
super(activity, layout, parent);
|
super(activity, layout, parent);
|
||||||
name=findViewById(R.id.name);
|
name=findViewById(R.id.name);
|
||||||
timeAndUsername=findViewById(R.id.time_and_username);
|
time=findViewById(R.id.time);
|
||||||
|
username=findViewById(R.id.username);
|
||||||
|
botIcon=findViewById(R.id.bot_icon);
|
||||||
|
timeUsernameSeparator=findViewById(R.id.separator);
|
||||||
avatar=findViewById(R.id.avatar);
|
avatar=findViewById(R.id.avatar);
|
||||||
more=findViewById(R.id.more);
|
more=findViewById(R.id.more);
|
||||||
visibility=findViewById(R.id.visibility);
|
visibility=findViewById(R.id.visibility);
|
||||||
@@ -171,7 +174,7 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
|
|||||||
|
|
||||||
optionsMenu=new PopupMenu(activity, more);
|
optionsMenu=new PopupMenu(activity, more);
|
||||||
optionsMenu.inflate(R.menu.post);
|
optionsMenu.inflate(R.menu.post);
|
||||||
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.P)
|
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.P && !UiUtils.isEMUI())
|
||||||
optionsMenu.getMenu().setGroupDividerEnabled(true);
|
optionsMenu.getMenu().setGroupDividerEnabled(true);
|
||||||
optionsMenu.setOnMenuItemClickListener(menuItem->{
|
optionsMenu.setOnMenuItemClickListener(menuItem->{
|
||||||
Account account=item.user;
|
Account account=item.user;
|
||||||
@@ -190,7 +193,7 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
boolean isPixelfed = item.parentFragment.isInstancePixelfed();
|
boolean isPixelfed = item.parentFragment.isInstancePixelfed();
|
||||||
boolean textEmpty = TextUtils.isEmpty(item.status.content) && TextUtils.isEmpty(item.status.spoilerText);
|
boolean textEmpty = TextUtils.isEmpty(item.status.content) && !item.status.hasSpoiler();
|
||||||
if(!redraft && (isPixelfed || textEmpty)){
|
if(!redraft && (isPixelfed || textEmpty)){
|
||||||
// pixelfed doesn't support /statuses/:id/source :/
|
// pixelfed doesn't support /statuses/:id/source :/
|
||||||
if (isPixelfed) {
|
if (isPixelfed) {
|
||||||
@@ -315,18 +318,26 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
|
|||||||
else if (item.status != null && item.status.editedAt != null)
|
else if (item.status != null && item.status.editedAt != null)
|
||||||
time=item.parentFragment.getString(R.string.edited_timestamp, UiUtils.formatRelativeTimestamp(itemView.getContext(), item.status.editedAt));
|
time=item.parentFragment.getString(R.string.edited_timestamp, UiUtils.formatRelativeTimestamp(itemView.getContext(), item.status.editedAt));
|
||||||
|
|
||||||
String sepp = item.parentFragment.getString(R.string.sk_separator);
|
this.username.setText(item.user.getDisplayUsername());
|
||||||
String username = "@" + item.user.acct;
|
this.timeUsernameSeparator.setVisibility(time==null ? View.GONE : View.VISIBLE);
|
||||||
timeAndUsername.setText(time == null ? username :
|
this.time.setVisibility(time==null ? View.GONE : View.VISIBLE);
|
||||||
username + " " + sepp + " " + time);
|
if(time!=null) this.time.setText(time);
|
||||||
|
|
||||||
|
botIcon.setVisibility(item.user.bot ? View.VISIBLE : View.GONE);
|
||||||
|
botIcon.setColorFilter(username.getCurrentTextColor());
|
||||||
|
|
||||||
deleteNotification.setVisibility(GlobalUserPreferences.enableDeleteNotifications && item.notification!=null && !item.inset ? View.VISIBLE : View.GONE);
|
deleteNotification.setVisibility(GlobalUserPreferences.enableDeleteNotifications && item.notification!=null && !item.inset ? View.VISIBLE : View.GONE);
|
||||||
if (item.hasVisibilityToggle){
|
if (item.hasVisibilityToggle){
|
||||||
boolean disabled = !item.status.sensitiveRevealed ||
|
boolean hidden = !item.status.sensitiveRevealed || (item.status.hasSpoiler() && !item.status.spoilerRevealed);
|
||||||
(!TextUtils.isEmpty(item.status.spoilerText) &&
|
|
||||||
!item.status.spoilerRevealed);
|
// doing this because V.setVisibilityAnimated ignores changes between INVISIBLE and GONE
|
||||||
visibility.setEnabled(!disabled);
|
int newVis=hidden ? View.INVISIBLE : View.VISIBLE;
|
||||||
V.setVisibilityAnimated(visibility, disabled ? View.INVISIBLE : View.VISIBLE);
|
if(newVis==View.INVISIBLE && visibility.getVisibility()==View.GONE)
|
||||||
|
visibility.setVisibility(newVis);
|
||||||
|
else
|
||||||
|
V.setVisibilityAnimated(visibility, newVis);
|
||||||
|
|
||||||
|
visibility.setEnabled(!hidden);
|
||||||
visibility.setContentDescription(item.parentFragment.getString(item.status.sensitiveRevealed ? R.string.spoiler_hide : R.string.spoiler_show));
|
visibility.setContentDescription(item.parentFragment.getString(item.status.sensitiveRevealed ? R.string.spoiler_hide : R.string.spoiler_show));
|
||||||
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.O){
|
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.O){
|
||||||
visibility.setTooltipText(visibility.getContentDescription());
|
visibility.setTooltipText(visibility.getContentDescription());
|
||||||
@@ -337,7 +348,8 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
|
|||||||
itemView.setPadding(itemView.getPaddingLeft(), itemView.getPaddingTop(), itemView.getPaddingRight(), item.needBottomPadding ? V.dp(16) : 0);
|
itemView.setPadding(itemView.getPaddingLeft(), itemView.getPaddingTop(), itemView.getPaddingRight(), item.needBottomPadding ? V.dp(16) : 0);
|
||||||
if(TextUtils.isEmpty(item.extraText)){
|
if(TextUtils.isEmpty(item.extraText)){
|
||||||
if (item.status != null) {
|
if (item.status != null) {
|
||||||
UiUtils.setExtraTextInfo(item.parentFragment.getContext(), extraText, pronouns, item.status.visibility==StatusPrivacy.DIRECT, item.status.localOnly || item.status.visibility==StatusPrivacy.LOCAL, item.status.account);
|
boolean displayPronouns=item.parentFragment instanceof ThreadFragment ? GlobalUserPreferences.displayPronounsInThreads : GlobalUserPreferences.displayPronounsInTimelines;
|
||||||
|
UiUtils.setExtraTextInfo(item.parentFragment.getContext(), extraText, pronouns, displayPronouns, item.status.visibility==StatusPrivacy.DIRECT, item.status.localOnly || item.status.visibility==StatusPrivacy.LOCAL, item.status.account);
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
extraText.setVisibility(View.VISIBLE);
|
extraText.setVisibility(View.VISIBLE);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import android.net.Uri;
|
|||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
@@ -13,12 +14,14 @@ import org.joinmastodon.android.R;
|
|||||||
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
||||||
import org.joinmastodon.android.model.Card;
|
import org.joinmastodon.android.model.Card;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
|
import org.joinmastodon.android.ui.OutlineProviders;
|
||||||
import org.joinmastodon.android.ui.drawables.BlurhashCrossfadeDrawable;
|
import org.joinmastodon.android.ui.drawables.BlurhashCrossfadeDrawable;
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
|
|
||||||
import me.grishka.appkit.imageloader.ImageLoaderViewHolder;
|
import me.grishka.appkit.imageloader.ImageLoaderViewHolder;
|
||||||
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
|
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
|
||||||
import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest;
|
import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest;
|
||||||
|
import me.grishka.appkit.utils.V;
|
||||||
|
|
||||||
public class LinkCardStatusDisplayItem extends StatusDisplayItem{
|
public class LinkCardStatusDisplayItem extends StatusDisplayItem{
|
||||||
private final Status status;
|
private final Status status;
|
||||||
@@ -51,6 +54,7 @@ public class LinkCardStatusDisplayItem extends StatusDisplayItem{
|
|||||||
public static class Holder extends StatusDisplayItem.Holder<LinkCardStatusDisplayItem> implements ImageLoaderViewHolder{
|
public static class Holder extends StatusDisplayItem.Holder<LinkCardStatusDisplayItem> implements ImageLoaderViewHolder{
|
||||||
private final TextView title, description, domain;
|
private final TextView title, description, domain;
|
||||||
private final ImageView photo;
|
private final ImageView photo;
|
||||||
|
private final View inner;
|
||||||
private BlurhashCrossfadeDrawable crossfadeDrawable=new BlurhashCrossfadeDrawable();
|
private BlurhashCrossfadeDrawable crossfadeDrawable=new BlurhashCrossfadeDrawable();
|
||||||
private boolean didClear;
|
private boolean didClear;
|
||||||
|
|
||||||
@@ -60,7 +64,8 @@ public class LinkCardStatusDisplayItem extends StatusDisplayItem{
|
|||||||
description=findViewById(R.id.description);
|
description=findViewById(R.id.description);
|
||||||
domain=findViewById(R.id.domain);
|
domain=findViewById(R.id.domain);
|
||||||
photo=findViewById(R.id.photo);
|
photo=findViewById(R.id.photo);
|
||||||
findViewById(R.id.inner).setOnClickListener(this::onClick);
|
inner=findViewById(R.id.inner);
|
||||||
|
inner.setOnClickListener(this::onClick);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -84,6 +89,15 @@ public class LinkCardStatusDisplayItem extends StatusDisplayItem{
|
|||||||
photo.setImageDrawable(crossfadeDrawable);
|
photo.setImageDrawable(crossfadeDrawable);
|
||||||
didClear=false;
|
didClear=false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if there's no image, we don't want to cover the inset borders
|
||||||
|
FrameLayout.LayoutParams params=(FrameLayout.LayoutParams) inner.getLayoutParams();
|
||||||
|
int margin=item.inset && item.imgRequest == null ? V.dp(1) : 0;
|
||||||
|
params.setMargins(margin, 0, margin, margin);
|
||||||
|
|
||||||
|
boolean insetAndLast=item.inset && isLastDisplayItemForStatus();
|
||||||
|
inner.setClipToOutline(insetAndLast);
|
||||||
|
inner.setOutlineProvider(insetAndLast ? OutlineProviders.bottomRoundedRect(12) : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import org.joinmastodon.android.R;
|
|||||||
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
||||||
import org.joinmastodon.android.model.Attachment;
|
import org.joinmastodon.android.model.Attachment;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
|
import org.joinmastodon.android.ui.OutlineProviders;
|
||||||
import org.joinmastodon.android.ui.PhotoLayoutHelper;
|
import org.joinmastodon.android.ui.PhotoLayoutHelper;
|
||||||
import org.joinmastodon.android.ui.drawables.SpoilerStripesDrawable;
|
import org.joinmastodon.android.ui.drawables.SpoilerStripesDrawable;
|
||||||
import org.joinmastodon.android.ui.photoviewer.PhotoViewerHost;
|
import org.joinmastodon.android.ui.photoviewer.PhotoViewerHost;
|
||||||
@@ -210,6 +211,10 @@ public class MediaGridStatusDisplayItem extends StatusDisplayItem{
|
|||||||
sensitiveText.setText(R.string.media_hidden);
|
sensitiveText.setText(R.string.media_hidden);
|
||||||
else
|
else
|
||||||
sensitiveText.setText(R.string.sensitive_content_explain);
|
sensitiveText.setText(R.string.sensitive_content_explain);
|
||||||
|
|
||||||
|
boolean insetAndLast=item.inset && isLastDisplayItemForStatus();
|
||||||
|
wrapper.setClipToOutline(insetAndLast);
|
||||||
|
wrapper.setOutlineProvider(insetAndLast ? OutlineProviders.bottomRoundedRect(12) : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -242,6 +247,7 @@ public class MediaGridStatusDisplayItem extends StatusDisplayItem{
|
|||||||
if(altTextAnimator!=null)
|
if(altTextAnimator!=null)
|
||||||
altTextAnimator.cancel();
|
altTextAnimator.cancel();
|
||||||
// V.setVisibilityAnimated(hideSensitiveButton, View.GONE);
|
// V.setVisibilityAnimated(hideSensitiveButton, View.GONE);
|
||||||
|
V.cancelVisibilityAnimation(altTextWrapper);
|
||||||
v.setVisibility(View.INVISIBLE);
|
v.setVisibility(View.INVISIBLE);
|
||||||
int index=(Integer)v.getTag();
|
int index=(Integer)v.getTag();
|
||||||
altTextIndex=index;
|
altTextIndex=index;
|
||||||
@@ -254,7 +260,7 @@ public class MediaGridStatusDisplayItem extends StatusDisplayItem{
|
|||||||
noAltText.setVisibility(!hasAltText && showNoAltIndicator ? 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_text_overlay : R.drawable.bg_image_no_alt_overlay);
|
||||||
altTextWrapper.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener(){
|
altTextWrapper.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener(){
|
||||||
@Override
|
@Override
|
||||||
public boolean onPreDraw(){
|
public boolean onPreDraw(){
|
||||||
@@ -317,6 +323,7 @@ public class MediaGridStatusDisplayItem extends StatusDisplayItem{
|
|||||||
altTextAnimator.cancel();
|
altTextAnimator.cancel();
|
||||||
|
|
||||||
// V.setVisibilityAnimated(hideSensitiveButton, item.status.sensitive ? View.VISIBLE : View.GONE);
|
// V.setVisibilityAnimated(hideSensitiveButton, item.status.sensitive ? View.VISIBLE : View.GONE);
|
||||||
|
V.cancelVisibilityAnimation(altTextWrapper);
|
||||||
View btn=controllers.get(altTextIndex).btnsWrap;
|
View btn=controllers.get(altTextIndex).btnsWrap;
|
||||||
int i=0;
|
int i=0;
|
||||||
for(MediaAttachmentViewController c:controllers){
|
for(MediaAttachmentViewController c:controllers){
|
||||||
@@ -365,8 +372,8 @@ public class MediaGridStatusDisplayItem extends StatusDisplayItem{
|
|||||||
@Override
|
@Override
|
||||||
public void onAnimationEnd(Animator animation){
|
public void onAnimationEnd(Animator animation){
|
||||||
altTextAnimator=null;
|
altTextAnimator=null;
|
||||||
altTextWrapper.setVisibility(View.GONE);
|
V.setVisibilityAnimated(altTextWrapper, View.GONE);
|
||||||
btn.setVisibility(View.VISIBLE);
|
V.setVisibilityAnimated(btn, View.VISIBLE);
|
||||||
btn.setAlpha(1);
|
btn.setAlpha(1);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ public class PollOptionStatusDisplayItem extends StatusDisplayItem{
|
|||||||
progressBg=activity.getResources().getDrawable(R.drawable.bg_poll_option_voted, activity.getTheme()).mutate();
|
progressBg=activity.getResources().getDrawable(R.drawable.bg_poll_option_voted, activity.getTheme()).mutate();
|
||||||
progressBgInset=activity.getResources().getDrawable(R.drawable.bg_poll_option_voted_inset, activity.getTheme()).mutate();
|
progressBgInset=activity.getResources().getDrawable(R.drawable.bg_poll_option_voted_inset, activity.getTheme()).mutate();
|
||||||
itemView.setOnClickListener(this::onButtonClick);
|
itemView.setOnClickListener(this::onButtonClick);
|
||||||
button.setOutlineProvider(OutlineProviders.roundedRect(24));
|
button.setOutlineProvider(OutlineProviders.M3_BUTTON);
|
||||||
button.setClipToOutline(true);
|
button.setClipToOutline(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ public class ReblogOrReplyLineStatusDisplayItem extends StatusDisplayItem{
|
|||||||
private int iconEnd;
|
private int iconEnd;
|
||||||
private CustomEmojiHelper emojiHelper=new CustomEmojiHelper(), fullTextEmojiHelper;
|
private CustomEmojiHelper emojiHelper=new CustomEmojiHelper(), fullTextEmojiHelper;
|
||||||
private View.OnClickListener handleClick;
|
private View.OnClickListener handleClick;
|
||||||
boolean belowHeader, needBottomPadding;
|
public boolean needBottomPadding;
|
||||||
ReblogOrReplyLineStatusDisplayItem extra;
|
ReblogOrReplyLineStatusDisplayItem extra;
|
||||||
CharSequence fullText;
|
CharSequence fullText;
|
||||||
|
|
||||||
@@ -131,7 +131,6 @@ public class ReblogOrReplyLineStatusDisplayItem extends StatusDisplayItem{
|
|||||||
if (visibilityText != 0) text.setContentDescription(item.text + " (" + ctx.getString(visibilityText) + ")");
|
if (visibilityText != 0) text.setContentDescription(item.text + " (" + ctx.getString(visibilityText) + ")");
|
||||||
if(Build.VERSION.SDK_INT<Build.VERSION_CODES.N)
|
if(Build.VERSION.SDK_INT<Build.VERSION_CODES.N)
|
||||||
UiUtils.fixCompoundDrawableTintOnAndroid6(text);
|
UiUtils.fixCompoundDrawableTintOnAndroid6(text);
|
||||||
text.setTextAppearance(item.belowHeader ? R.style.m3_label_large : R.style.m3_title_small);
|
|
||||||
text.setCompoundDrawableTintList(text.getTextColors());
|
text.setCompoundDrawableTintList(text.getTextColors());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,10 +140,6 @@ public class ReblogOrReplyLineStatusDisplayItem extends StatusDisplayItem{
|
|||||||
if (item.extra != null) bindLine(item.extra, extraText);
|
if (item.extra != null) bindLine(item.extra, extraText);
|
||||||
extraText.setVisibility(item.extra == null ? View.GONE : View.VISIBLE);
|
extraText.setVisibility(item.extra == null ? View.GONE : View.VISIBLE);
|
||||||
separator.setVisibility(item.extra == null ? View.GONE : View.VISIBLE);
|
separator.setVisibility(item.extra == null ? View.GONE : View.VISIBLE);
|
||||||
ViewGroup.MarginLayoutParams params = new ViewGroup.MarginLayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
|
||||||
params.bottomMargin = item.belowHeader ? V.dp(-6) : V.dp(-12);
|
|
||||||
params.topMargin = item.belowHeader ? V.dp(-6) : 0;
|
|
||||||
itemView.setLayoutParams(params);
|
|
||||||
itemView.setPadding(itemView.getPaddingLeft(), itemView.getPaddingTop(), itemView.getPaddingRight(), item.needBottomPadding ? V.dp(16) : 0);
|
itemView.setPadding(itemView.getPaddingLeft(), itemView.getPaddingTop(), itemView.getPaddingRight(), item.needBottomPadding ? V.dp(16) : 0);
|
||||||
layoutLine();
|
layoutLine();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ public class SpoilerStatusDisplayItem extends StatusDisplayItem{
|
|||||||
itemView.getPaddingLeft(),
|
itemView.getPaddingLeft(),
|
||||||
itemView.getPaddingTop(),
|
itemView.getPaddingTop(),
|
||||||
itemView.getPaddingRight(),
|
itemView.getPaddingRight(),
|
||||||
item.inset || GlobalUserPreferences.spectatorMode ? itemView.getPaddingTop() : 0
|
item.inset ? itemView.getPaddingTop() : 0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
package org.joinmastodon.android.ui.displayitems;
|
package org.joinmastodon.android.ui.displayitems;
|
||||||
|
|
||||||
|
import static org.joinmastodon.android.api.session.AccountLocalPreferences.ShowEmojiReactions.ALWAYS;
|
||||||
|
import static org.joinmastodon.android.api.session.AccountLocalPreferences.ShowEmojiReactions.ONLY_OPENED;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.Fragment;
|
import android.app.Fragment;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.graphics.drawable.ColorDrawable;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.SpannableStringBuilder;
|
import android.text.SpannableStringBuilder;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
@@ -11,6 +15,7 @@ import android.view.ViewGroup;
|
|||||||
|
|
||||||
import org.joinmastodon.android.GlobalUserPreferences;
|
import org.joinmastodon.android.GlobalUserPreferences;
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
|
import org.joinmastodon.android.api.session.AccountLocalPreferences;
|
||||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||||
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
||||||
import org.joinmastodon.android.fragments.HashtagTimelineFragment;
|
import org.joinmastodon.android.fragments.HashtagTimelineFragment;
|
||||||
@@ -25,12 +30,12 @@ import org.joinmastodon.android.model.LegacyFilter;
|
|||||||
import org.joinmastodon.android.model.FilterAction;
|
import org.joinmastodon.android.model.FilterAction;
|
||||||
import org.joinmastodon.android.model.FilterContext;
|
import org.joinmastodon.android.model.FilterContext;
|
||||||
import org.joinmastodon.android.model.FilterResult;
|
import org.joinmastodon.android.model.FilterResult;
|
||||||
import org.joinmastodon.android.model.Notification;
|
|
||||||
import org.joinmastodon.android.model.Poll;
|
import org.joinmastodon.android.model.Poll;
|
||||||
import org.joinmastodon.android.model.ScheduledStatus;
|
import org.joinmastodon.android.model.ScheduledStatus;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
import org.joinmastodon.android.ui.PhotoLayoutHelper;
|
import org.joinmastodon.android.ui.PhotoLayoutHelper;
|
||||||
import org.joinmastodon.android.ui.text.HtmlParser;
|
import org.joinmastodon.android.ui.text.HtmlParser;
|
||||||
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
import org.joinmastodon.android.ui.viewholders.AccountViewHolder;
|
import org.joinmastodon.android.ui.viewholders.AccountViewHolder;
|
||||||
import org.joinmastodon.android.utils.StatusFilterPredicate;
|
import org.joinmastodon.android.utils.StatusFilterPredicate;
|
||||||
import org.parceler.Parcels;
|
import org.parceler.Parcels;
|
||||||
@@ -48,8 +53,8 @@ import me.grishka.appkit.views.UsableRecyclerView;
|
|||||||
|
|
||||||
public abstract class StatusDisplayItem{
|
public abstract class StatusDisplayItem{
|
||||||
public final String parentID;
|
public final String parentID;
|
||||||
public final BaseStatusListFragment parentFragment;
|
public final BaseStatusListFragment<?> parentFragment;
|
||||||
public boolean inset;
|
public boolean inset, insetPadding=true;
|
||||||
public int index;
|
public int index;
|
||||||
public boolean
|
public boolean
|
||||||
hasDescendantNeighbor = false,
|
hasDescendantNeighbor = false,
|
||||||
@@ -63,6 +68,7 @@ public abstract class StatusDisplayItem{
|
|||||||
public static final int FLAG_MEDIA_FORCE_HIDDEN=1 << 3;
|
public static final int FLAG_MEDIA_FORCE_HIDDEN=1 << 3;
|
||||||
public static final int FLAG_NO_HEADER=1 << 4;
|
public static final int FLAG_NO_HEADER=1 << 4;
|
||||||
public static final int FLAG_NO_TRANSLATE=1 << 5;
|
public static final int FLAG_NO_TRANSLATE=1 << 5;
|
||||||
|
public static final int FLAG_NO_EMOJI_REACTIONS=1 << 6;
|
||||||
|
|
||||||
public void setAncestryInfo(
|
public void setAncestryInfo(
|
||||||
boolean hasDescendantNeighbor,
|
boolean hasDescendantNeighbor,
|
||||||
@@ -76,7 +82,7 @@ public abstract class StatusDisplayItem{
|
|||||||
this.isDirectDescendant = isDirectDescendant;
|
this.isDirectDescendant = isDirectDescendant;
|
||||||
}
|
}
|
||||||
|
|
||||||
public StatusDisplayItem(String parentID, BaseStatusListFragment parentFragment){
|
public StatusDisplayItem(String parentID, BaseStatusListFragment<?> parentFragment){
|
||||||
this.parentID=parentID;
|
this.parentID=parentID;
|
||||||
this.parentFragment=parentFragment;
|
this.parentFragment=parentFragment;
|
||||||
}
|
}
|
||||||
@@ -101,6 +107,7 @@ public abstract class StatusDisplayItem{
|
|||||||
case POLL_OPTION -> new PollOptionStatusDisplayItem.Holder(activity, parent);
|
case POLL_OPTION -> new PollOptionStatusDisplayItem.Holder(activity, parent);
|
||||||
case POLL_FOOTER -> new PollFooterStatusDisplayItem.Holder(activity, parent);
|
case POLL_FOOTER -> new PollFooterStatusDisplayItem.Holder(activity, parent);
|
||||||
case CARD -> new LinkCardStatusDisplayItem.Holder(activity, parent);
|
case CARD -> new LinkCardStatusDisplayItem.Holder(activity, parent);
|
||||||
|
case EMOJI_REACTIONS -> new EmojiReactionsStatusDisplayItem.Holder(activity, parent);
|
||||||
case FOOTER -> new FooterStatusDisplayItem.Holder(activity, parent);
|
case FOOTER -> new FooterStatusDisplayItem.Holder(activity, parent);
|
||||||
case ACCOUNT_CARD -> new AccountCardStatusDisplayItem.Holder(activity, parent);
|
case ACCOUNT_CARD -> new AccountCardStatusDisplayItem.Holder(activity, parent);
|
||||||
case ACCOUNT -> new AccountStatusDisplayItem.Holder(new AccountViewHolder(parentFragment, parent, null));
|
case ACCOUNT -> new AccountStatusDisplayItem.Holder(new AccountViewHolder(parentFragment, parent, null));
|
||||||
@@ -113,21 +120,10 @@ public abstract class StatusDisplayItem{
|
|||||||
case SPOILER, FILTER_SPOILER -> new SpoilerStatusDisplayItem.Holder(activity, parent, type);
|
case SPOILER, FILTER_SPOILER -> new SpoilerStatusDisplayItem.Holder(activity, parent, type);
|
||||||
case SECTION_HEADER -> null; // new SectionHeaderStatusDisplayItem.Holder(activity, parent);
|
case SECTION_HEADER -> null; // new SectionHeaderStatusDisplayItem.Holder(activity, parent);
|
||||||
case NOTIFICATION_HEADER -> new NotificationHeaderStatusDisplayItem.Holder(activity, parent);
|
case NOTIFICATION_HEADER -> new NotificationHeaderStatusDisplayItem.Holder(activity, parent);
|
||||||
case DUMMY -> new InsetDummyStatusDisplayItem.Holder(activity);
|
case DUMMY -> new DummyStatusDisplayItem.Holder(activity);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ArrayList<StatusDisplayItem> buildItems(BaseStatusListFragment<?> fragment, Status status, String accountID, DisplayItemsParent parentObject, Map<String, Account> knownAccounts, boolean inset, boolean addFooter, boolean disableTranslate, FilterContext filterContext) {
|
|
||||||
int flags=0;
|
|
||||||
if(inset)
|
|
||||||
flags|=FLAG_INSET;
|
|
||||||
if(!addFooter)
|
|
||||||
flags|=FLAG_NO_FOOTER;
|
|
||||||
if (disableTranslate)
|
|
||||||
flags|=FLAG_NO_TRANSLATE;
|
|
||||||
return buildItems(fragment, status, accountID, parentObject, knownAccounts, filterContext, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ReblogOrReplyLineStatusDisplayItem buildReplyLine(BaseStatusListFragment<?> fragment, Status status, String accountID, DisplayItemsParent parent, Account account, boolean threadReply) {
|
public static ReblogOrReplyLineStatusDisplayItem buildReplyLine(BaseStatusListFragment<?> fragment, Status status, String accountID, DisplayItemsParent parent, Account account, boolean threadReply) {
|
||||||
String parentID = parent.getID();
|
String parentID = parent.getID();
|
||||||
String text = threadReply ? fragment.getString(R.string.sk_show_thread)
|
String text = threadReply ? fragment.getString(R.string.sk_show_thread)
|
||||||
@@ -203,7 +199,7 @@ public abstract class StatusDisplayItem{
|
|||||||
items.add(replyLine);
|
items.add(replyLine);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if((flags & FLAG_CHECKABLE)!=0)
|
if((flags & FLAG_CHECKABLE)!=0)
|
||||||
items.add(header=new CheckableHeaderStatusDisplayItem(parentID, statusForContent.account, statusForContent.createdAt, fragment, accountID, statusForContent, null));
|
items.add(header=new CheckableHeaderStatusDisplayItem(parentID, statusForContent.account, statusForContent.createdAt, fragment, accountID, statusForContent, null));
|
||||||
else
|
else
|
||||||
@@ -221,7 +217,7 @@ public abstract class StatusDisplayItem{
|
|||||||
}
|
}
|
||||||
|
|
||||||
ArrayList<StatusDisplayItem> contentItems;
|
ArrayList<StatusDisplayItem> contentItems;
|
||||||
if(!TextUtils.isEmpty(statusForContent.spoilerText)){
|
if(statusForContent.hasSpoiler()){
|
||||||
if (AccountSessionManager.get(accountID).getLocalPreferences().revealCWs) statusForContent.spoilerRevealed = true;
|
if (AccountSessionManager.get(accountID).getLocalPreferences().revealCWs) statusForContent.spoilerRevealed = true;
|
||||||
SpoilerStatusDisplayItem spoilerItem=new SpoilerStatusDisplayItem(parentID, fragment, null, statusForContent, Type.SPOILER);
|
SpoilerStatusDisplayItem spoilerItem=new SpoilerStatusDisplayItem(parentID, fragment, null, statusForContent, Type.SPOILER);
|
||||||
items.add(spoilerItem);
|
items.add(spoilerItem);
|
||||||
@@ -240,17 +236,26 @@ public abstract class StatusDisplayItem{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean hasSpoiler=!TextUtils.isEmpty(statusForContent.spoilerText);
|
||||||
if(!TextUtils.isEmpty(statusForContent.content)){
|
if(!TextUtils.isEmpty(statusForContent.content)){
|
||||||
SpannableStringBuilder parsedText=HtmlParser.parse(statusForContent.content, statusForContent.emojis, statusForContent.mentions, statusForContent.tags, accountID);
|
SpannableStringBuilder parsedText=HtmlParser.parse(statusForContent.content, statusForContent.emojis, statusForContent.mentions, statusForContent.tags, accountID);
|
||||||
HtmlParser.applyFilterHighlights(fragment.getActivity(), parsedText, status.filtered);
|
HtmlParser.applyFilterHighlights(fragment.getActivity(), parsedText, status.filtered);
|
||||||
TextStatusDisplayItem text=new TextStatusDisplayItem(parentID, HtmlParser.parse(statusForContent.content, statusForContent.emojis, statusForContent.mentions, statusForContent.tags, accountID), fragment, statusForContent, (flags & FLAG_NO_TRANSLATE) != 0);
|
TextStatusDisplayItem text=new TextStatusDisplayItem(parentID, HtmlParser.parse(statusForContent.content, statusForContent.emojis, statusForContent.mentions, statusForContent.tags, accountID), fragment, statusForContent, (flags & FLAG_NO_TRANSLATE) != 0);
|
||||||
contentItems.add(text);
|
contentItems.add(text);
|
||||||
} else if (header!=null){
|
}else if(!hasSpoiler && header!=null){
|
||||||
header.needBottomPadding=true;
|
header.needBottomPadding=true;
|
||||||
|
}else if(hasSpoiler){
|
||||||
|
contentItems.add(new DummyStatusDisplayItem(parentID, fragment));
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Attachment> imageAttachments=statusForContent.mediaAttachments.stream().filter(att->att.type.isImage()).collect(Collectors.toList());
|
List<Attachment> imageAttachments=statusForContent.mediaAttachments.stream().filter(att->att.type.isImage()).collect(Collectors.toList());
|
||||||
if(!imageAttachments.isEmpty()){
|
if(!imageAttachments.isEmpty()){
|
||||||
|
int color = UiUtils.getThemeColor(fragment.getContext(), R.attr.colorM3SurfaceVariant);
|
||||||
|
for (Attachment att : imageAttachments) {
|
||||||
|
if (att.blurhashPlaceholder == null) {
|
||||||
|
att.blurhashPlaceholder = new ColorDrawable(color);
|
||||||
|
}
|
||||||
|
}
|
||||||
PhotoLayoutHelper.TiledLayoutResult layout=PhotoLayoutHelper.processThumbs(imageAttachments);
|
PhotoLayoutHelper.TiledLayoutResult layout=PhotoLayoutHelper.processThumbs(imageAttachments);
|
||||||
MediaGridStatusDisplayItem mediaGrid=new MediaGridStatusDisplayItem(parentID, fragment, layout, imageAttachments, statusForContent);
|
MediaGridStatusDisplayItem mediaGrid=new MediaGridStatusDisplayItem(parentID, fragment, layout, imageAttachments, statusForContent);
|
||||||
if((flags & FLAG_MEDIA_FORCE_HIDDEN)!=0)
|
if((flags & FLAG_MEDIA_FORCE_HIDDEN)!=0)
|
||||||
@@ -276,8 +281,16 @@ public abstract class StatusDisplayItem{
|
|||||||
if(contentItems!=items && statusForContent.spoilerRevealed){
|
if(contentItems!=items && statusForContent.spoilerRevealed){
|
||||||
items.addAll(contentItems);
|
items.addAll(contentItems);
|
||||||
}
|
}
|
||||||
|
AccountLocalPreferences lp=fragment.getLocalPrefs();
|
||||||
|
if((flags & FLAG_NO_EMOJI_REACTIONS)==0 && lp.emojiReactionsEnabled &&
|
||||||
|
(lp.showEmojiReactions!=ONLY_OPENED || fragment instanceof ThreadFragment)){
|
||||||
|
boolean isMainStatus=fragment instanceof ThreadFragment t && t.getMainStatus().id.equals(statusForContent.id);
|
||||||
|
boolean showAddButton=lp.showEmojiReactions==ALWAYS || isMainStatus;
|
||||||
|
items.add(new EmojiReactionsStatusDisplayItem(parentID, fragment, statusForContent, accountID, !showAddButton, false));
|
||||||
|
}
|
||||||
|
FooterStatusDisplayItem footer=null;
|
||||||
if((flags & FLAG_NO_FOOTER)==0){
|
if((flags & FLAG_NO_FOOTER)==0){
|
||||||
FooterStatusDisplayItem footer=new FooterStatusDisplayItem(parentID, fragment, statusForContent, accountID);
|
footer=new FooterStatusDisplayItem(parentID, fragment, statusForContent, accountID);
|
||||||
footer.hideCounts=hideCounts;
|
footer.hideCounts=hideCounts;
|
||||||
items.add(footer);
|
items.add(footer);
|
||||||
if(status.hasGapAfter && !(fragment instanceof ThreadFragment))
|
if(status.hasGapAfter && !(fragment instanceof ThreadFragment))
|
||||||
@@ -286,10 +299,12 @@ public abstract class StatusDisplayItem{
|
|||||||
int i=1;
|
int i=1;
|
||||||
boolean inset=(flags & FLAG_INSET)!=0;
|
boolean inset=(flags & FLAG_INSET)!=0;
|
||||||
// add inset dummy so last content item doesn't clip out of inset bounds
|
// add inset dummy so last content item doesn't clip out of inset bounds
|
||||||
if (inset) {
|
if((inset || footer==null) && (flags & FLAG_CHECKABLE)==0){
|
||||||
items.add(new InsetDummyStatusDisplayItem(parentID, fragment,
|
items.add(new DummyStatusDisplayItem(parentID, fragment));
|
||||||
!contentItems.isEmpty() && contentItems
|
// in case we ever need the dummy to display a margin for the media grid again:
|
||||||
.get(contentItems.size() - 1) instanceof MediaGridStatusDisplayItem));
|
// (i forgot why we apparently don't need this anymore)
|
||||||
|
// !contentItems.isEmpty() && contentItems
|
||||||
|
// .get(contentItems.size() - 1) instanceof MediaGridStatusDisplayItem));
|
||||||
}
|
}
|
||||||
for(StatusDisplayItem item:items){
|
for(StatusDisplayItem item:items){
|
||||||
item.inset=inset;
|
item.inset=inset;
|
||||||
@@ -330,6 +345,7 @@ public abstract class StatusDisplayItem{
|
|||||||
POLL_OPTION,
|
POLL_OPTION,
|
||||||
POLL_FOOTER,
|
POLL_FOOTER,
|
||||||
CARD,
|
CARD,
|
||||||
|
EMOJI_REACTIONS,
|
||||||
FOOTER,
|
FOOTER,
|
||||||
ACCOUNT_CARD,
|
ACCOUNT_CARD,
|
||||||
ACCOUNT,
|
ACCOUNT,
|
||||||
@@ -365,6 +381,35 @@ public abstract class StatusDisplayItem{
|
|||||||
item.parentFragment.onItemClick(item.parentID);
|
item.parentFragment.onItemClick(item.parentID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Optional<StatusDisplayItem> getNextVisibleDisplayItem(){
|
||||||
|
Optional<StatusDisplayItem> next=getNextDisplayItem();
|
||||||
|
for(int offset=1; next.isPresent(); next=getDisplayItemOffset(++offset)){
|
||||||
|
if(!next.map(n->
|
||||||
|
(n instanceof EmojiReactionsStatusDisplayItem e && e.isHidden()) ||
|
||||||
|
(n instanceof DummyStatusDisplayItem)
|
||||||
|
).orElse(false)) return next;
|
||||||
|
}
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<StatusDisplayItem> getNextDisplayItem(){
|
||||||
|
return getDisplayItemOffset(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<StatusDisplayItem> getDisplayItemOffset(int offset){
|
||||||
|
int nextPos=getAbsoluteAdapterPosition() + offset;
|
||||||
|
List<StatusDisplayItem> displayItems=item.parentFragment.getDisplayItems();
|
||||||
|
return displayItems.size() > nextPos
|
||||||
|
? Optional.of(displayItems.get(nextPos))
|
||||||
|
: Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isLastDisplayItemForStatus(){
|
||||||
|
return getNextVisibleDisplayItem()
|
||||||
|
.map(n->!n.parentID.equals(item.parentID))
|
||||||
|
.orElse(true);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEnabled(){
|
public boolean isEnabled(){
|
||||||
return item.parentFragment.isItemEnabled(item.parentID);
|
return item.parentFragment.isItemEnabled(item.parentID);
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import org.joinmastodon.android.api.requests.statuses.TranslateStatus;
|
|||||||
import org.joinmastodon.android.api.session.AccountSession;
|
import org.joinmastodon.android.api.session.AccountSession;
|
||||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||||
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
||||||
|
import org.joinmastodon.android.fragments.ThreadFragment;
|
||||||
import org.joinmastodon.android.model.Instance;
|
import org.joinmastodon.android.model.Instance;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
import org.joinmastodon.android.model.StatusPrivacy;
|
import org.joinmastodon.android.model.StatusPrivacy;
|
||||||
@@ -193,12 +194,11 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
|
|||||||
|
|
||||||
readMore.setText(item.status.textExpanded ? R.string.sk_collapse : R.string.sk_expand);
|
readMore.setText(item.status.textExpanded ? R.string.sk_collapse : R.string.sk_expand);
|
||||||
|
|
||||||
// remove additional padding when (transparently padded) translate button is visible
|
StatusDisplayItem next=getNextVisibleDisplayItem().orElse(null);
|
||||||
int nextPos = getAbsoluteAdapterPosition() + 1;
|
if(next!=null && !next.parentID.equals(item.parentID)) next=null;
|
||||||
boolean nextIsFooter = item.parentFragment.getDisplayItems().size() > nextPos &&
|
int bottomPadding=next instanceof FooterStatusDisplayItem ? V.dp(6)
|
||||||
item.parentFragment.getDisplayItems().get(nextPos) instanceof FooterStatusDisplayItem;
|
: item.inset ? V.dp(12)
|
||||||
int bottomPadding = (translateVisible && nextIsFooter) ? 0
|
: (next instanceof EmojiReactionsStatusDisplayItem || next==null) ? 0
|
||||||
: nextIsFooter ? V.dp(6)
|
|
||||||
: V.dp(12);
|
: V.dp(12);
|
||||||
itemView.setPadding(itemView.getPaddingLeft(), itemView.getPaddingTop(), itemView.getPaddingRight(), bottomPadding);
|
itemView.setPadding(itemView.getPaddingLeft(), itemView.getPaddingTop(), itemView.getPaddingRight(), bottomPadding);
|
||||||
|
|
||||||
@@ -235,7 +235,7 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
|
|||||||
|
|
||||||
// compensate for spoiler's bottom margin
|
// compensate for spoiler's bottom margin
|
||||||
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) itemView.getLayoutParams();
|
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) itemView.getLayoutParams();
|
||||||
params.setMargins(params.leftMargin, (item.inset || GlobalUserPreferences.spectatorMode) && hasSpoiler ? V.dp(-16) : 0,
|
params.setMargins(params.leftMargin, item.inset && hasSpoiler ? V.dp(-16) : 0,
|
||||||
params.rightMargin, params.bottomMargin);
|
params.rightMargin, params.bottomMargin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -99,11 +99,15 @@ public class BlurhashCrossfadeDrawable extends Drawable{
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getIntrinsicWidth(){
|
public int getIntrinsicWidth(){
|
||||||
|
if(width==0)
|
||||||
|
return imageDrawable==null ? 1920 : imageDrawable.getIntrinsicWidth();
|
||||||
return width;
|
return width;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getIntrinsicHeight(){
|
public int getIntrinsicHeight(){
|
||||||
|
if(height==0)
|
||||||
|
return imageDrawable==null ? 1080 : imageDrawable.getIntrinsicHeight();
|
||||||
return height;
|
return height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ import android.widget.TextView;
|
|||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
import android.widget.Toolbar;
|
import android.widget.Toolbar;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.GlobalUserPreferences;
|
||||||
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.model.Attachment;
|
import org.joinmastodon.android.model.Attachment;
|
||||||
@@ -418,7 +419,8 @@ public class PhotoViewer implements ZoomPanView.Listener{
|
|||||||
WindowManager.LayoutParams wlp=(WindowManager.LayoutParams) windowView.getLayoutParams();
|
WindowManager.LayoutParams wlp=(WindowManager.LayoutParams) windowView.getLayoutParams();
|
||||||
wlp.flags|=WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
|
wlp.flags|=WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
|
||||||
wm.updateViewLayout(windowView, wlp);
|
wm.updateViewLayout(windowView, wlp);
|
||||||
activity.getSystemService(AudioManager.class).requestAudioFocus(audioFocusListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
|
int audiofocus = GlobalUserPreferences.overlayMedia ? AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK : AudioManager.AUDIOFOCUS_GAIN;
|
||||||
|
activity.getSystemService(AudioManager.class).requestAudioFocus(audioFocusListener, AudioManager.STREAM_MUSIC, audiofocus);
|
||||||
}
|
}
|
||||||
screenOnRefCount++;
|
screenOnRefCount++;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,8 +8,6 @@ import android.view.View;
|
|||||||
|
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
||||||
import org.joinmastodon.android.ui.displayitems.LinkCardStatusDisplayItem;
|
|
||||||
import org.joinmastodon.android.ui.displayitems.MediaGridStatusDisplayItem;
|
|
||||||
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -27,7 +25,7 @@ public class InsetStatusItemDecoration extends RecyclerView.ItemDecoration{
|
|||||||
|
|
||||||
public InsetStatusItemDecoration(BaseStatusListFragment<?> listFragment){
|
public InsetStatusItemDecoration(BaseStatusListFragment<?> listFragment){
|
||||||
this.listFragment=listFragment;
|
this.listFragment=listFragment;
|
||||||
bgColor=UiUtils.getThemeColor(listFragment.getActivity(), R.attr.colorM3SurfaceVariant);
|
bgColor=UiUtils.getThemeColor(listFragment.getActivity(), R.attr.colorM3Surface);
|
||||||
borderColor=UiUtils.getThemeColor(listFragment.getActivity(), R.attr.colorM3OutlineVariant);
|
borderColor=UiUtils.getThemeColor(listFragment.getActivity(), R.attr.colorM3OutlineVariant);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,13 +63,13 @@ public class InsetStatusItemDecoration extends RecyclerView.ItemDecoration{
|
|||||||
paint.setColor(bgColor);
|
paint.setColor(bgColor);
|
||||||
rect.left=V.dp(12);
|
rect.left=V.dp(12);
|
||||||
rect.right=list.getWidth()-V.dp(12);
|
rect.right=list.getWidth()-V.dp(12);
|
||||||
rect.inset(V.dp(4), V.dp(4));
|
rect.inset(V.dp(4), V.dp(0));
|
||||||
c.drawRoundRect(rect, V.dp(4), V.dp(4), paint);
|
c.drawRoundRect(rect, V.dp(12), V.dp(12), paint);
|
||||||
paint.setStyle(Paint.Style.STROKE);
|
paint.setStyle(Paint.Style.STROKE);
|
||||||
paint.setStrokeWidth(V.dp(1));
|
paint.setStrokeWidth(V.dp(1));
|
||||||
paint.setColor(borderColor);
|
paint.setColor(borderColor);
|
||||||
rect.inset(paint.getStrokeWidth()/2f, paint.getStrokeWidth()/2f);
|
rect.inset(paint.getStrokeWidth()/2f, paint.getStrokeWidth()/2f);
|
||||||
c.drawRoundRect(rect, V.dp(4), V.dp(4), paint);
|
c.drawRoundRect(rect, V.dp(12), V.dp(12), paint);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -85,19 +83,19 @@ public class InsetStatusItemDecoration extends RecyclerView.ItemDecoration{
|
|||||||
boolean topSiblingInset=pos>0 && displayItems.get(pos-1).inset;
|
boolean topSiblingInset=pos>0 && displayItems.get(pos-1).inset;
|
||||||
boolean bottomSiblingInset=pos<displayItems.size()-1 && displayItems.get(pos+1).inset;
|
boolean bottomSiblingInset=pos<displayItems.size()-1 && displayItems.get(pos+1).inset;
|
||||||
int pad;
|
int pad;
|
||||||
if(holder instanceof MediaGridStatusDisplayItem.Holder || holder instanceof LinkCardStatusDisplayItem.Holder)
|
// if(holder instanceof MediaGridStatusDisplayItem.Holder || holder instanceof LinkCardStatusDisplayItem.Holder)
|
||||||
pad=V.dp(16);
|
pad=V.dp(16);
|
||||||
else
|
// else
|
||||||
pad=V.dp(12);
|
// pad=V.dp(12);
|
||||||
boolean insetLeft=true, insetRight=true;
|
boolean insetPadding=((StatusDisplayItem.Holder<?>) holder).getItem().insetPadding;
|
||||||
if(insetLeft)
|
if(insetPadding)
|
||||||
outRect.left=pad;
|
outRect.left=pad;
|
||||||
if(insetRight)
|
if(insetPadding)
|
||||||
outRect.right=pad;
|
outRect.right=pad;
|
||||||
|
|
||||||
// had to comment this out because animations with offsets aren't handled properly.
|
// had to comment this out because animations with offsets aren't handled properly.
|
||||||
// can be worked around by manually applying top margins to items
|
// can be worked around by manually applying top margins to items
|
||||||
// see InsetDummyStatusDisplayItem#onBinds
|
// see InsetDummyStatusDisplayItem#onBind
|
||||||
// if(!topSiblingInset)
|
// if(!topSiblingInset)
|
||||||
// outRect.top=pad;
|
// outRect.top=pad;
|
||||||
// if(!bottomSiblingInset)
|
// if(!bottomSiblingInset)
|
||||||
|
|||||||
@@ -0,0 +1,242 @@
|
|||||||
|
package org.joinmastodon.android.ui.utils;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2016 Ali Muzaffar
|
||||||
|
* <p/>
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* <p/>
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* <p/>
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.ColorFilter;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.graphics.PixelFormat;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.text.Editable;
|
||||||
|
import android.text.TextWatcher;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
|
||||||
|
public class TextDrawable extends Drawable implements TextWatcher {
|
||||||
|
private WeakReference<TextView> ref;
|
||||||
|
private String mText;
|
||||||
|
private Paint mPaint;
|
||||||
|
private Rect mHeightBounds;
|
||||||
|
private boolean mBindToViewPaint = false;
|
||||||
|
private float mPrevTextSize = 0;
|
||||||
|
private boolean mInitFitText = false;
|
||||||
|
private boolean mFitTextEnabled = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a TextDrawable using the given paint object and string
|
||||||
|
*
|
||||||
|
* @param paint
|
||||||
|
* @param s
|
||||||
|
*/
|
||||||
|
public TextDrawable(Paint paint, String s) {
|
||||||
|
mText = s;
|
||||||
|
mPaint = new Paint(paint);
|
||||||
|
mHeightBounds = new Rect();
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a TextDrawable. This uses the given TextView to initialize paint and has initial text
|
||||||
|
* that will be drawn. Initial text can also be useful for reserving space that may otherwise
|
||||||
|
* not be available when setting compound drawables.
|
||||||
|
*
|
||||||
|
* @param tv The TextView / EditText using to initialize this drawable
|
||||||
|
* @param initialText Optional initial text to display
|
||||||
|
* @param bindToViewsText Should this drawable mirror the text in the TextView
|
||||||
|
* @param bindToViewsPaint Should this drawable mirror changes to Paint in the TextView, like textColor, typeface, alpha etc.
|
||||||
|
* Note, this will override any changes made using setColorFilter or setAlpha.
|
||||||
|
*/
|
||||||
|
public TextDrawable(TextView tv, String initialText, boolean bindToViewsText, boolean bindToViewsPaint) {
|
||||||
|
this(tv.getPaint(), initialText);
|
||||||
|
ref = new WeakReference<>(tv);
|
||||||
|
if (bindToViewsText || bindToViewsPaint) {
|
||||||
|
if (bindToViewsText) {
|
||||||
|
tv.addTextChangedListener(this);
|
||||||
|
}
|
||||||
|
mBindToViewPaint = bindToViewsPaint;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a TextDrawable. This uses the given TextView to initialize paint and the text that
|
||||||
|
* will be drawn.
|
||||||
|
*
|
||||||
|
* @param tv The TextView / EditText using to initialize this drawable
|
||||||
|
* @param bindToViewsText Should this drawable mirror the text in the TextView
|
||||||
|
* @param bindToViewsPaint Should this drawable mirror changes to Paint in the TextView, like textColor, typeface, alpha etc.
|
||||||
|
* Note, this will override any changes made using setColorFilter or setAlpha.
|
||||||
|
*/
|
||||||
|
public TextDrawable(TextView tv, boolean bindToViewsText, boolean bindToViewsPaint) {
|
||||||
|
this(tv, tv.getText().toString(), bindToViewsText, bindToViewsPaint);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use the provided TextView/EditText to initialize the drawable.
|
||||||
|
* The Drawable will copy the Text and the Paint properties, however it will from that
|
||||||
|
* point on be independant of the TextView.
|
||||||
|
*
|
||||||
|
* @param tv a TextView or EditText or any of their children.
|
||||||
|
*/
|
||||||
|
public TextDrawable(TextView tv) {
|
||||||
|
this(tv, false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use the provided TextView/EditText to initialize the drawable.
|
||||||
|
* The Drawable will copy the Paint properties, and use the provided text to initialise itself.
|
||||||
|
*
|
||||||
|
* @param tv a TextView or EditText or any of their children.
|
||||||
|
* @param s The String to draw
|
||||||
|
*/
|
||||||
|
public TextDrawable(TextView tv, String s) {
|
||||||
|
this(tv, s, false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void draw(Canvas canvas) {
|
||||||
|
if (mBindToViewPaint && ref.get() != null) {
|
||||||
|
Paint p = ref.get().getPaint();
|
||||||
|
canvas.drawText(mText, 0, getBounds().height(), p);
|
||||||
|
} else {
|
||||||
|
if (mInitFitText) {
|
||||||
|
fitTextAndInit();
|
||||||
|
}
|
||||||
|
canvas.drawText(mText, 0, getBounds().height(), mPaint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAlpha(int alpha) {
|
||||||
|
mPaint.setAlpha(alpha);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setColorFilter(ColorFilter colorFilter) {
|
||||||
|
mPaint.setColorFilter(colorFilter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getOpacity() {
|
||||||
|
int alpha = mPaint.getAlpha();
|
||||||
|
if (alpha == 0) {
|
||||||
|
return PixelFormat.TRANSPARENT;
|
||||||
|
} else if (alpha == 255) {
|
||||||
|
return PixelFormat.OPAQUE;
|
||||||
|
} else {
|
||||||
|
return PixelFormat.TRANSLUCENT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init() {
|
||||||
|
Rect bounds = getBounds();
|
||||||
|
//We want to use some character to determine the max height of the text.
|
||||||
|
//Otherwise if we draw something like "..." they will appear centered
|
||||||
|
//Here I'm just going to use the entire alphabet to determine max height.
|
||||||
|
mPaint.getTextBounds("1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 0, 1, mHeightBounds);
|
||||||
|
//This doesn't account for leading or training white spaces.
|
||||||
|
//mPaint.getTextBounds(mText, 0, mText.length(), bounds);
|
||||||
|
float width = mPaint.measureText(mText);
|
||||||
|
bounds.top = mHeightBounds.top;
|
||||||
|
bounds.bottom = mHeightBounds.bottom;
|
||||||
|
bounds.right = (int) width;
|
||||||
|
bounds.left = 0;
|
||||||
|
setBounds(bounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPaint(Paint paint) {
|
||||||
|
mPaint = new Paint(paint);
|
||||||
|
//Since this can change the font used, we need to recalculate bounds.
|
||||||
|
if (mFitTextEnabled && !mInitFitText) {
|
||||||
|
fitTextAndInit();
|
||||||
|
} else {
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
invalidateSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Paint getPaint() {
|
||||||
|
return mPaint;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setText(String text) {
|
||||||
|
mText = text;
|
||||||
|
//Since this can change the bounds of the text, we need to recalculate.
|
||||||
|
if (mFitTextEnabled && !mInitFitText) {
|
||||||
|
fitTextAndInit();
|
||||||
|
} else {
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
invalidateSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getText() {
|
||||||
|
return mText;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterTextChanged(Editable s) {
|
||||||
|
setText(s.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make the TextDrawable match the width of the View it's associated with.
|
||||||
|
* <p/>
|
||||||
|
* Note: While this option will not work if bindToViewPaint is true.
|
||||||
|
*
|
||||||
|
* @param fitText
|
||||||
|
*/
|
||||||
|
public void setFillText(boolean fitText) {
|
||||||
|
mFitTextEnabled = fitText;
|
||||||
|
if (fitText) {
|
||||||
|
mPrevTextSize = mPaint.getTextSize();
|
||||||
|
if (ref.get() != null) {
|
||||||
|
if (ref.get().getWidth() > 0) {
|
||||||
|
fitTextAndInit();
|
||||||
|
} else {
|
||||||
|
mInitFitText = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (mPrevTextSize > 0) {
|
||||||
|
mPaint.setTextSize(mPrevTextSize);
|
||||||
|
}
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fitTextAndInit() {
|
||||||
|
float fitWidth = ref.get().getWidth();
|
||||||
|
float textWidth = mPaint.measureText(mText);
|
||||||
|
float multi = fitWidth / textWidth;
|
||||||
|
mPaint.setTextSize(mPaint.getTextSize() * multi);
|
||||||
|
mInitFitText = false;
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -45,7 +45,9 @@ import android.transition.TransitionManager;
|
|||||||
import android.transition.TransitionSet;
|
import android.transition.TransitionSet;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
|
import android.view.Gravity;
|
||||||
import android.view.HapticFeedbackConstants;
|
import android.view.HapticFeedbackConstants;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.SubMenu;
|
import android.view.SubMenu;
|
||||||
@@ -73,6 +75,7 @@ import org.joinmastodon.android.api.requests.accounts.SetAccountMuted;
|
|||||||
import org.joinmastodon.android.api.requests.accounts.SetDomainBlocked;
|
import org.joinmastodon.android.api.requests.accounts.SetDomainBlocked;
|
||||||
import org.joinmastodon.android.api.requests.accounts.AuthorizeFollowRequest;
|
import org.joinmastodon.android.api.requests.accounts.AuthorizeFollowRequest;
|
||||||
import org.joinmastodon.android.api.requests.accounts.RejectFollowRequest;
|
import org.joinmastodon.android.api.requests.accounts.RejectFollowRequest;
|
||||||
|
import org.joinmastodon.android.api.requests.instance.GetInstance;
|
||||||
import org.joinmastodon.android.api.requests.lists.DeleteList;
|
import org.joinmastodon.android.api.requests.lists.DeleteList;
|
||||||
import org.joinmastodon.android.api.requests.notifications.DismissNotification;
|
import org.joinmastodon.android.api.requests.notifications.DismissNotification;
|
||||||
import org.joinmastodon.android.api.requests.search.GetSearchResults;
|
import org.joinmastodon.android.api.requests.search.GetSearchResults;
|
||||||
@@ -93,6 +96,8 @@ import org.joinmastodon.android.fragments.ComposeFragment;
|
|||||||
import org.joinmastodon.android.fragments.HashtagTimelineFragment;
|
import org.joinmastodon.android.fragments.HashtagTimelineFragment;
|
||||||
import org.joinmastodon.android.fragments.ProfileFragment;
|
import org.joinmastodon.android.fragments.ProfileFragment;
|
||||||
import org.joinmastodon.android.fragments.ThreadFragment;
|
import org.joinmastodon.android.fragments.ThreadFragment;
|
||||||
|
import org.joinmastodon.android.fragments.settings.SettingsServerAboutFragment;
|
||||||
|
import org.joinmastodon.android.fragments.settings.SettingsServerFragment;
|
||||||
import org.joinmastodon.android.model.Account;
|
import org.joinmastodon.android.model.Account;
|
||||||
import org.joinmastodon.android.model.AccountField;
|
import org.joinmastodon.android.model.AccountField;
|
||||||
import org.joinmastodon.android.model.Emoji;
|
import org.joinmastodon.android.model.Emoji;
|
||||||
@@ -103,7 +108,6 @@ import org.joinmastodon.android.model.ScheduledStatus;
|
|||||||
import org.joinmastodon.android.model.SearchResults;
|
import org.joinmastodon.android.model.SearchResults;
|
||||||
import org.joinmastodon.android.model.Searchable;
|
import org.joinmastodon.android.model.Searchable;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
import org.joinmastodon.android.model.StatusPrivacy;
|
|
||||||
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
|
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
|
||||||
import org.joinmastodon.android.ui.text.CustomEmojiSpan;
|
import org.joinmastodon.android.ui.text.CustomEmojiSpan;
|
||||||
import org.joinmastodon.android.ui.text.HtmlParser;
|
import org.joinmastodon.android.ui.text.HtmlParser;
|
||||||
@@ -115,6 +119,7 @@ import java.lang.reflect.Method;
|
|||||||
import java.net.IDN;
|
import java.net.IDN;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
|
import java.time.Duration;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.ZoneId;
|
import java.time.ZoneId;
|
||||||
@@ -132,6 +137,7 @@ import java.util.Map;
|
|||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
import java.util.function.BiPredicate;
|
import java.util.function.BiPredicate;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
@@ -528,31 +534,70 @@ public class UiUtils {
|
|||||||
.exec(accountID);
|
.exec(accountID);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
public static void confirmToggleMuteUser(Context context, String accountID, Account account, boolean currentlyMuted, Consumer<Relationship> resultCallback){
|
||||||
|
View durationView=LayoutInflater.from(context).inflate(R.layout.mute_user_dialog, null);
|
||||||
|
LinearLayout.LayoutParams params=new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
|
params.setMargins(0, V.dp(-12), 0, 0);
|
||||||
|
durationView.setLayoutParams(params);
|
||||||
|
Button button=durationView.findViewById(R.id.button);
|
||||||
|
((TextView) durationView.findViewById(R.id.message)).setText(context.getString(R.string.confirm_mute, account.displayName));
|
||||||
|
|
||||||
public static void confirmToggleMuteUser(Activity activity, String accountID, Account account, boolean currentlyMuted, Consumer<Relationship> resultCallback) {
|
AtomicReference<Duration> muteDuration=new AtomicReference<>(Duration.ZERO);
|
||||||
showConfirmationAlert(activity, activity.getString(currentlyMuted ? R.string.confirm_unmute_title : R.string.confirm_mute_title),
|
|
||||||
activity.getString(currentlyMuted ? R.string.confirm_unmute : R.string.confirm_mute, account.displayName),
|
PopupMenu popupMenu=new PopupMenu(context, button, Gravity.CENTER_HORIZONTAL);
|
||||||
activity.getString(currentlyMuted ? R.string.do_unmute : R.string.do_mute),
|
popupMenu.inflate(R.menu.mute_duration);
|
||||||
currentlyMuted ? R.drawable.ic_fluent_speaker_0_28_regular : R.drawable.ic_fluent_speaker_off_28_regular,
|
popupMenu.setOnMenuItemClickListener(item->{
|
||||||
() -> {
|
int id=item.getItemId();
|
||||||
new SetAccountMuted(account.id, !currentlyMuted)
|
if(id==R.id.duration_indefinite)
|
||||||
.setCallback(new Callback<>() {
|
muteDuration.set(Duration.ZERO);
|
||||||
|
else if(id==R.id.duration_minutes_5){
|
||||||
|
muteDuration.set(Duration.ofMinutes(5));
|
||||||
|
}else if(id==R.id.duration_minutes_30){
|
||||||
|
muteDuration.set(Duration.ofMinutes(30));
|
||||||
|
}else if(id==R.id.duration_hours_1){
|
||||||
|
muteDuration.set(Duration.ofHours(1));
|
||||||
|
}else if(id==R.id.duration_hours_6){
|
||||||
|
muteDuration.set(Duration.ofHours(6));
|
||||||
|
}else if(id==R.id.duration_days_1){
|
||||||
|
muteDuration.set(Duration.ofDays(1));
|
||||||
|
}else if(id==R.id.duration_days_3){
|
||||||
|
muteDuration.set(Duration.ofDays(3));
|
||||||
|
}else if(id==R.id.duration_days_7){
|
||||||
|
muteDuration.set(Duration.ofDays(7));
|
||||||
|
}
|
||||||
|
button.setText(item.getTitle());
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
button.setOnTouchListener(popupMenu.getDragToOpenListener());
|
||||||
|
button.setOnClickListener(v->popupMenu.show());
|
||||||
|
button.setText(popupMenu.getMenu().getItem(0).getTitle());
|
||||||
|
|
||||||
|
new M3AlertDialogBuilder(context)
|
||||||
|
.setTitle(context.getString(currentlyMuted ? R.string.confirm_unmute_title : R.string.confirm_mute_title))
|
||||||
|
.setMessage(currentlyMuted ? context.getString(R.string.confirm_unmute, account.displayName) : null)
|
||||||
|
.setView(currentlyMuted ? null : durationView)
|
||||||
|
.setPositiveButton(context.getString(currentlyMuted ? R.string.do_unmute : R.string.do_mute), (dlg, i)->{
|
||||||
|
new SetAccountMuted(account.id, !currentlyMuted, muteDuration.get().getSeconds())
|
||||||
|
.setCallback(new Callback<>(){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Relationship result) {
|
public void onSuccess(Relationship result){
|
||||||
resultCallback.accept(result);
|
resultCallback.accept(result);
|
||||||
if (!currentlyMuted) {
|
if(!currentlyMuted){
|
||||||
E.post(new RemoveAccountPostsEvent(accountID, account.id, false));
|
E.post(new RemoveAccountPostsEvent(accountID, account.id, false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onError(ErrorResponse error) {
|
public void onError(ErrorResponse error){
|
||||||
error.showToast(activity);
|
error.showToast(context);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.wrapProgress(activity, R.string.loading, false)
|
.wrapProgress(context, R.string.loading, false)
|
||||||
.exec(accountID);
|
.exec(accountID);
|
||||||
});
|
})
|
||||||
|
.setNegativeButton(R.string.cancel, null)
|
||||||
|
.setIcon(currentlyMuted ? R.drawable.ic_fluent_speaker_0_28_regular : R.drawable.ic_fluent_speaker_off_28_regular)
|
||||||
|
.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void confirmDeletePost(Activity activity, String accountID, Status status, Consumer<Status> resultCallback) {
|
public static void confirmDeletePost(Activity activity, String accountID, Status status, Consumer<Status> resultCallback) {
|
||||||
@@ -702,9 +747,6 @@ public class UiUtils {
|
|||||||
if(relationship.blocking){
|
if(relationship.blocking){
|
||||||
button.setText(R.string.button_blocked);
|
button.setText(R.string.button_blocked);
|
||||||
styleRes=R.style.Widget_Mastodon_M3_Button_Tonal_Error;
|
styleRes=R.style.Widget_Mastodon_M3_Button_Tonal_Error;
|
||||||
}else if(relationship.blockedBy){
|
|
||||||
button.setText(R.string.button_follow);
|
|
||||||
styleRes=R.style.Widget_Mastodon_M3_Button_Filled;
|
|
||||||
}else if(relationship.requested){
|
}else if(relationship.requested){
|
||||||
button.setText(R.string.button_follow_pending);
|
button.setText(R.string.button_follow_pending);
|
||||||
styleRes=R.style.Widget_Mastodon_M3_Button_Tonal;
|
styleRes=R.style.Widget_Mastodon_M3_Button_Tonal;
|
||||||
@@ -716,7 +758,6 @@ public class UiUtils {
|
|||||||
styleRes=R.style.Widget_Mastodon_M3_Button_Tonal;
|
styleRes=R.style.Widget_Mastodon_M3_Button_Tonal;
|
||||||
}
|
}
|
||||||
|
|
||||||
button.setEnabled(!relationship.blockedBy);
|
|
||||||
TypedArray ta=button.getContext().obtainStyledAttributes(styleRes, new int[]{android.R.attr.background});
|
TypedArray ta=button.getContext().obtainStyledAttributes(styleRes, new int[]{android.R.attr.background});
|
||||||
button.setBackground(ta.getDrawable(0));
|
button.setBackground(ta.getDrawable(0));
|
||||||
ta.recycle();
|
ta.recycle();
|
||||||
@@ -1028,13 +1069,11 @@ public class UiUtils {
|
|||||||
return back;
|
return back;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean setExtraTextInfo(Context ctx, TextView extraText, TextView pronouns, boolean mentionedOnly, boolean localOnly, @Nullable Account account) {
|
public static boolean setExtraTextInfo(Context ctx, @Nullable TextView extraText, @Nullable TextView pronouns, boolean displayPronouns, boolean mentionedOnly, boolean localOnly, @Nullable Account account) {
|
||||||
List<String> extraParts = new ArrayList<>();
|
List<String> extraParts = extraText!=null && (localOnly || mentionedOnly) ? new ArrayList<>() : null;
|
||||||
Optional<String> p=pronouns==null ? Optional.empty() : extractPronouns(ctx, account);
|
Optional<String> p=pronouns==null || !displayPronouns ? Optional.empty() : extractPronouns(ctx, account);
|
||||||
boolean setPronouns=false;
|
|
||||||
if(p.isPresent()) {
|
if(p.isPresent()) {
|
||||||
HtmlParser.setTextWithCustomEmoji(pronouns, p.get(), account.emojis);
|
HtmlParser.setTextWithCustomEmoji(pronouns, p.get(), account.emojis);
|
||||||
setPronouns=true;
|
|
||||||
pronouns.setVisibility(View.VISIBLE);
|
pronouns.setVisibility(View.VISIBLE);
|
||||||
}else if(pronouns!=null){
|
}else if(pronouns!=null){
|
||||||
pronouns.setVisibility(View.GONE);
|
pronouns.setVisibility(View.GONE);
|
||||||
@@ -1043,7 +1082,7 @@ public class UiUtils {
|
|||||||
extraParts.add(ctx.getString(R.string.sk_inline_local_only));
|
extraParts.add(ctx.getString(R.string.sk_inline_local_only));
|
||||||
if(mentionedOnly)
|
if(mentionedOnly)
|
||||||
extraParts.add(ctx.getString(R.string.sk_inline_direct));
|
extraParts.add(ctx.getString(R.string.sk_inline_direct));
|
||||||
if(!extraParts.isEmpty()) {
|
if(extraText!=null && extraParts!=null && !extraParts.isEmpty()) {
|
||||||
String sepp = ctx.getString(R.string.sk_separator);
|
String sepp = ctx.getString(R.string.sk_separator);
|
||||||
String text = String.join(" " + sepp + " ", extraParts);
|
String text = String.join(" " + sepp + " ", extraParts);
|
||||||
if(account == null) extraText.setText(text);
|
if(account == null) extraText.setText(text);
|
||||||
@@ -1051,7 +1090,7 @@ public class UiUtils {
|
|||||||
extraText.setVisibility(View.VISIBLE);
|
extraText.setVisibility(View.VISIBLE);
|
||||||
return true;
|
return true;
|
||||||
}else{
|
}else{
|
||||||
extraText.setVisibility(View.GONE);
|
if(extraText!=null) extraText.setVisibility(View.GONE);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1241,6 +1280,23 @@ public class UiUtils {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.exec(accountID));
|
.exec(accountID));
|
||||||
|
} else if (uri.getPath() != null && uri.getPath().matches("^/about$")) {
|
||||||
|
return Optional.of(new GetInstance()
|
||||||
|
.setCallback(new Callback<>(){
|
||||||
|
@Override
|
||||||
|
public void onSuccess(Instance result){
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
args.putParcelable("instance", Parcels.wrap(result));
|
||||||
|
args.putString("account", accountID);
|
||||||
|
go.accept(SettingsServerFragment.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(ErrorResponse error){
|
||||||
|
go.accept(null, bundleError(error));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.execNoAuth(uri.getHost()));
|
||||||
} else if (looksLikeFediverseUrl(url)) {
|
} else if (looksLikeFediverseUrl(url)) {
|
||||||
return Optional.of(new GetSearchResults(url, null, true)
|
return Optional.of(new GetSearchResults(url, null, true)
|
||||||
.setCallback(new Callback<>() {
|
.setCallback(new Callback<>() {
|
||||||
@@ -1586,7 +1642,7 @@ public class UiUtils {
|
|||||||
private static String extractPronounsFromField(String localizedPronouns, AccountField field) {
|
private static String extractPronounsFromField(String localizedPronouns, AccountField field) {
|
||||||
if(!field.name.toLowerCase().contains(localizedPronouns) &&
|
if(!field.name.toLowerCase().contains(localizedPronouns) &&
|
||||||
!field.name.toLowerCase().contains("pronouns")) return null;
|
!field.name.toLowerCase().contains("pronouns")) return null;
|
||||||
String text=HtmlParser.strip(field.value);
|
String text=HtmlParser.text(field.value);
|
||||||
if(field.value.toLowerCase().contains("https://")){
|
if(field.value.toLowerCase().contains("https://")){
|
||||||
for(String pronounUrl : pronounsUrls){
|
for(String pronounUrl : pronounsUrls){
|
||||||
int index=text.indexOf(pronounUrl);
|
int index=text.indexOf(pronounUrl);
|
||||||
|
|||||||
@@ -27,10 +27,14 @@ import android.widget.ProgressBar;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
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.MastodonAPIController;
|
import org.joinmastodon.android.api.MastodonAPIController;
|
||||||
import org.joinmastodon.android.api.ProgressListener;
|
import org.joinmastodon.android.api.ProgressListener;
|
||||||
|
import org.joinmastodon.android.api.requests.statuses.CreateStatus;
|
||||||
import org.joinmastodon.android.api.requests.statuses.GetAttachmentByID;
|
import org.joinmastodon.android.api.requests.statuses.GetAttachmentByID;
|
||||||
import org.joinmastodon.android.api.requests.statuses.UpdateAttachment;
|
import org.joinmastodon.android.api.requests.statuses.UpdateAttachment;
|
||||||
import org.joinmastodon.android.api.requests.statuses.UploadAttachment;
|
import org.joinmastodon.android.api.requests.statuses.UploadAttachment;
|
||||||
@@ -47,8 +51,11 @@ import org.parceler.Parcel;
|
|||||||
import org.parceler.Parcels;
|
import org.parceler.Parcels;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.ListIterator;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
@@ -550,6 +557,14 @@ public class ComposeMediaViewController{
|
|||||||
public List<String> getAttachmentIDs(){
|
public List<String> getAttachmentIDs(){
|
||||||
return attachments.stream().map(a->a.serverAttachment.id).collect(Collectors.toList());
|
return attachments.stream().map(a->a.serverAttachment.id).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<CreateStatus.Request.MediaAttribute> getAttachmentAttributes(){
|
||||||
|
List<CreateStatus.Request.MediaAttribute> mediaAttributes = new ArrayList<>();
|
||||||
|
for (DraftMediaAttachment att:attachments){
|
||||||
|
mediaAttributes.add(new CreateStatus.Request.MediaAttribute(att.serverAttachment.id, att.description, null));
|
||||||
|
}
|
||||||
|
return mediaAttributes;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isEmpty(){
|
public boolean isEmpty(){
|
||||||
return attachments.isEmpty();
|
return attachments.isEmpty();
|
||||||
@@ -592,7 +607,7 @@ public class ComposeMediaViewController{
|
|||||||
public void saveAltTextsBeforePublishing(Runnable onSuccess, Consumer<ErrorResponse> onError){
|
public void saveAltTextsBeforePublishing(Runnable onSuccess, Consumer<ErrorResponse> onError){
|
||||||
ArrayList<UpdateAttachment> updateAltTextRequests=new ArrayList<>();
|
ArrayList<UpdateAttachment> updateAltTextRequests=new ArrayList<>();
|
||||||
for(DraftMediaAttachment att:attachments){
|
for(DraftMediaAttachment att:attachments){
|
||||||
if(!att.descriptionSaved){
|
if(!att.descriptionSaved && att.serverAttachment.description == null){
|
||||||
UpdateAttachment req=new UpdateAttachment(att.serverAttachment.id, att.description);
|
UpdateAttachment req=new UpdateAttachment(att.serverAttachment.id, att.description);
|
||||||
req.setCallback(new Callback<>(){
|
req.setCallback(new Callback<>(){
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -74,10 +74,17 @@ public class ComposePollViewController{
|
|||||||
pollWrap=view.findViewById(R.id.poll_wrap);
|
pollWrap=view.findViewById(R.id.poll_wrap);
|
||||||
|
|
||||||
Instance instance=fragment.instance;
|
Instance instance=fragment.instance;
|
||||||
if(instance.configuration!=null && instance.configuration.polls!=null && instance.configuration.polls.maxOptions>0)
|
if (!instance.isAkkoma()) {
|
||||||
maxPollOptions=instance.configuration.polls.maxOptions;
|
if(instance.configuration!=null && instance.configuration.polls!=null && instance.configuration.polls.maxOptions>0)
|
||||||
if(instance.configuration!=null && instance.configuration.polls!=null && instance.configuration.polls.maxCharactersPerOption>0)
|
maxPollOptions=instance.configuration.polls.maxOptions;
|
||||||
maxPollOptionLength=instance.configuration.polls.maxCharactersPerOption;
|
if(instance.configuration!=null && instance.configuration.polls!=null && instance.configuration.polls.maxCharactersPerOption>0)
|
||||||
|
maxPollOptionLength=instance.configuration.polls.maxCharactersPerOption;
|
||||||
|
} else {
|
||||||
|
if (instance.pollLimits!=null && instance.pollLimits.maxOptions>0)
|
||||||
|
maxPollOptions=instance.pollLimits.maxOptions;
|
||||||
|
if(instance.pollLimits!=null && instance.pollLimits.maxOptionChars>0)
|
||||||
|
maxPollOptionLength=instance.pollLimits.maxOptionChars;
|
||||||
|
}
|
||||||
|
|
||||||
pollOptionsView=pollWrap.findViewById(R.id.poll_options);
|
pollOptionsView=pollWrap.findViewById(R.id.poll_options);
|
||||||
addPollOptionBtn=pollWrap.findViewById(R.id.add_poll_option);
|
addPollOptionBtn=pollWrap.findViewById(R.id.add_poll_option);
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import android.widget.ProgressBar;
|
|||||||
import android.widget.RadioButton;
|
import android.widget.RadioButton;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.GlobalUserPreferences;
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.requests.accounts.SetAccountFollowed;
|
import org.joinmastodon.android.api.requests.accounts.SetAccountFollowed;
|
||||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||||
@@ -124,7 +125,8 @@ public class AccountViewHolder extends BindableViewHolder<AccountViewModel> impl
|
|||||||
}
|
}
|
||||||
|
|
||||||
// you know what's cooler than followers or verified links? yep. pronouns
|
// you know what's cooler than followers or verified links? yep. pronouns
|
||||||
Optional<String> pronounsString = UiUtils.extractPronouns(itemView.getContext(), item.account);
|
Optional<String> pronounsString=GlobalUserPreferences.displayPronounsInUserListings
|
||||||
|
? UiUtils.extractPronouns(itemView.getContext(), item.account) : Optional.empty();
|
||||||
pronouns.setVisibility(pronounsString.isPresent() ? View.VISIBLE : View.GONE);
|
pronouns.setVisibility(pronounsString.isPresent() ? View.VISIBLE : View.GONE);
|
||||||
pronounsString.ifPresent(p -> HtmlParser.setTextWithCustomEmoji(pronouns, p, item.account.emojis));
|
pronounsString.ifPresent(p -> HtmlParser.setTextWithCustomEmoji(pronouns, p, item.account.emojis));
|
||||||
|
|
||||||
@@ -188,7 +190,10 @@ public class AccountViewHolder extends BindableViewHolder<AccountViewModel> impl
|
|||||||
}
|
}
|
||||||
Bundle args=new Bundle();
|
Bundle args=new Bundle();
|
||||||
args.putString("account", accountID);
|
args.putString("account", accountID);
|
||||||
args.putParcelable("profileAccount", Parcels.wrap(item.account));
|
if (item.account.isRemote)
|
||||||
|
args.putParcelable("remoteAccount", Parcels.wrap(item.account));
|
||||||
|
else
|
||||||
|
args.putParcelable("profileAccount", Parcels.wrap(item.account));
|
||||||
Nav.go(fragment.getActivity(), ProfileFragment.class, args);
|
Nav.go(fragment.getActivity(), ProfileFragment.class, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -208,6 +213,7 @@ public class AccountViewHolder extends BindableViewHolder<AccountViewModel> impl
|
|||||||
Account account=item.account;
|
Account account=item.account;
|
||||||
|
|
||||||
menu.findItem(R.id.share).setTitle(fragment.getString(R.string.share_user, account.getDisplayUsername()));
|
menu.findItem(R.id.share).setTitle(fragment.getString(R.string.share_user, account.getDisplayUsername()));
|
||||||
|
menu.findItem(R.id.manage_user_lists).setTitle(fragment.getString(R.string.sk_lists_with_user, account.getShortUsername()));
|
||||||
menu.findItem(R.id.mute).setTitle(fragment.getString(relationship.muting ? R.string.unmute_user : R.string.mute_user, account.getDisplayUsername()));
|
menu.findItem(R.id.mute).setTitle(fragment.getString(relationship.muting ? R.string.unmute_user : R.string.mute_user, account.getDisplayUsername()));
|
||||||
menu.findItem(R.id.block).setTitle(fragment.getString(relationship.blocking ? R.string.unblock_user : R.string.block_user, account.getDisplayUsername()));
|
menu.findItem(R.id.block).setTitle(fragment.getString(relationship.blocking ? R.string.unblock_user : R.string.block_user, account.getDisplayUsername()));
|
||||||
menu.findItem(R.id.report).setTitle(fragment.getString(R.string.report_user, account.getDisplayUsername()));
|
menu.findItem(R.id.report).setTitle(fragment.getString(R.string.report_user, account.getDisplayUsername()));
|
||||||
|
|||||||
@@ -40,11 +40,9 @@ public abstract class ListItemViewHolder<T extends ListItem<?>> extends Bindable
|
|||||||
|
|
||||||
if(TextUtils.isEmpty(item.subtitle) && item.subtitleRes==0){
|
if(TextUtils.isEmpty(item.subtitle) && item.subtitleRes==0){
|
||||||
subtitle.setVisibility(View.GONE);
|
subtitle.setVisibility(View.GONE);
|
||||||
title.setMaxLines(2);
|
|
||||||
view.setMinimumHeight(V.dp(56));
|
view.setMinimumHeight(V.dp(56));
|
||||||
}else{
|
}else{
|
||||||
subtitle.setVisibility(View.VISIBLE);
|
subtitle.setVisibility(View.VISIBLE);
|
||||||
title.setMaxLines(1);
|
|
||||||
view.setMinimumHeight(V.dp(72));
|
view.setMinimumHeight(V.dp(72));
|
||||||
if(TextUtils.isEmpty(item.subtitle))
|
if(TextUtils.isEmpty(item.subtitle))
|
||||||
subtitle.setText(item.subtitleRes);
|
subtitle.setText(item.subtitleRes);
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package org.joinmastodon.android.ui.views;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
|
||||||
|
import me.grishka.appkit.views.UsableRecyclerView;
|
||||||
|
|
||||||
|
public class EmojiReactionsRecyclerView extends UsableRecyclerView{
|
||||||
|
public EmojiReactionsRecyclerView(Context context){
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public EmojiReactionsRecyclerView(Context context, AttributeSet attrs){
|
||||||
|
super(context, attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public EmojiReactionsRecyclerView(Context context, AttributeSet attrs, int defStyle){
|
||||||
|
super(context, attrs, defStyle);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onTouchEvent(MotionEvent e){
|
||||||
|
super.onTouchEvent(e);
|
||||||
|
// to pass through touch events (i.e. clicking the status) to the parent view
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://stackoverflow.com/questions/55372837/is-there-a-way-to-make-recyclerview-requiresfadingedge-unaffected-by-paddingtop
|
||||||
|
@Override
|
||||||
|
protected boolean isPaddingOffsetRequired() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int getLeftPaddingOffset(){
|
||||||
|
return -getPaddingLeft();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int getRightPaddingOffset() {
|
||||||
|
return getPaddingRight();
|
||||||
|
}
|
||||||
|
}
|
||||||
6
mastodon/src/main/res/color/m3_on_surface_selector.xml
Normal file
6
mastodon/src/main/res/color/m3_on_surface_selector.xml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:color="?colorM3OnSurfaceVariant" android:state_enabled="false" />
|
||||||
|
<item android:color="?colorM3OnSurfaceVariant" android:state_selected="false" />
|
||||||
|
<item android:color="?colorM3OnSurface" />
|
||||||
|
</selector>
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="108dp"
|
|
||||||
android:height="108dp"
|
|
||||||
android:viewportWidth="108"
|
|
||||||
android:viewportHeight="108">
|
|
||||||
<path
|
|
||||||
android:pathData="M63.26,36.4C67.49,37.02 71.06,40.24 71.61,44.32C71.85,46.75 71.73,50.4 71.67,52.17C71.65,52.61 71.64,52.94 71.64,53.1C71.64,53.34 71.61,55.51 71.6,55.74C71.22,61.58 67.58,63.88 63.75,64.62C63.7,64.63 63.66,64.64 63.61,64.65C63.6,64.65 63.58,64.65 63.57,64.65C61.14,65.13 58.54,65.25 56.07,65.32C55.48,65.34 54.89,65.34 54.3,65.34C51.85,65.34 49.4,65.05 47.01,64.47C47,64.47 46.99,64.47 46.97,64.47C46.96,64.48 46.95,64.48 46.94,64.49C46.93,64.5 46.92,64.51 46.92,64.52C46.91,64.53 46.91,64.55 46.91,64.56C46.98,65.33 47.14,66.1 47.41,66.83C47.74,67.68 48.9,69.71 53.19,69.71C55.69,69.71 58.17,69.42 60.6,68.84C60.61,68.84 60.63,68.84 60.64,68.84C60.65,68.85 60.66,68.85 60.67,68.86C60.68,68.87 60.69,68.88 60.7,68.89C60.7,68.9 60.7,68.91 60.7,68.93V71.79C60.7,71.8 60.7,71.82 60.69,71.83C60.69,71.84 60.68,71.85 60.67,71.86C59.91,72.41 58.89,72.73 58,73.01C57.96,73.02 57.91,73.04 57.88,73.05C57.47,73.18 57.06,73.29 56.64,73.39C52.85,74.25 48.9,74.04 45.22,72.79C41.79,71.58 38.28,68.64 37.42,65.1C36.95,63.18 36.63,61.23 36.44,59.27C36.25,57.12 36.18,54.97 36.11,52.82C36.09,52.01 36.06,51.2 36.03,50.38C35.95,48.32 36,46.06 36.44,44.03C37.35,39.89 41.1,37 45.21,36.4C45.3,36.38 45.39,36.37 45.5,36.35C46.31,36.21 48.01,35.91 53.53,35.91H53.58C59.84,35.91 62.55,36.29 63.26,36.4ZM65.39,58.94V48.84C65.39,46.78 64.86,45.14 63.81,43.93C62.71,42.72 61.29,42.09 59.51,42.09C57.47,42.09 55.92,42.88 54.88,44.45L53.88,46.12L52.88,44.45C51.85,42.88 50.3,42.09 48.25,42.09C46.47,42.09 45.05,42.72 43.96,43.93C42.9,45.14 42.37,46.78 42.37,48.84V58.94H46.38V49.14C46.38,47.08 47.25,46.03 48.99,46.03C50.92,46.03 51.89,47.27 51.89,49.73V55.09H55.87V49.73C55.87,47.27 56.84,46.03 58.76,46.03C60.52,46.03 61.38,47.08 61.38,49.14V58.94H65.39Z"
|
|
||||||
android:fillColor="#4A454E"
|
|
||||||
android:fillType="evenOdd"/>
|
|
||||||
</vector>
|
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<item android:state_enabled="true">
|
<item android:state_enabled="true">
|
||||||
<ripple android:color="@color/m3_on_secondary_container_overlay">
|
<ripple android:color="@color/m3_on_secondary_container_overlay">
|
||||||
<item>
|
<item android:gravity="center_vertical" android:height="40dp">
|
||||||
<shape>
|
<shape>
|
||||||
<solid android:color="?colorM3ErrorContainer"/>
|
<solid android:color="?colorM3ErrorContainer"/>
|
||||||
<corners android:radius="20dp"/>
|
<corners android:radius="20dp"/>
|
||||||
@@ -11,9 +11,13 @@
|
|||||||
</ripple>
|
</ripple>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<shape>
|
<layer-list>
|
||||||
<solid android:color="?colorM3DisabledBackground"/>
|
<item android:gravity="center_vertical" android:height="40dp">
|
||||||
<corners android:radius="20dp"/>
|
<shape>
|
||||||
</shape>
|
<solid android:color="?colorM3DisabledBackground"/>
|
||||||
|
<corners android:radius="20dp"/>
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
</layer-list>
|
||||||
</item>
|
</item>
|
||||||
</selector>
|
</selector>
|
||||||
20
mastodon/src/main/res/drawable/bg_filled_card.xml
Normal file
20
mastodon/src/main/res/drawable/bg_filled_card.xml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item>
|
||||||
|
<color android:color="?colorM3Surface"/>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<shape android:tint="?colorM3Primary">
|
||||||
|
<solid android:color="?colorFilledCardAlpha"/>
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<ripple android:color="@color/m3_on_surface_variant_overlay">
|
||||||
|
<item android:id="@android:id/mask">
|
||||||
|
<shape>
|
||||||
|
<solid android:color="#000"/>
|
||||||
|
</shape>
|
||||||
|
</item>
|
||||||
|
</ripple>
|
||||||
|
</item>
|
||||||
|
</layer-list>
|
||||||
@@ -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="#B2000000"/>
|
<solid android:color="#69000000"/>
|
||||||
<corners android:radius="4dp"/>
|
<corners android:radius="4dp"/>
|
||||||
</shape>
|
</shape>
|
||||||
@@ -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">
|
||||||
<corners android:radius="26dp"/>
|
<solid android:color="#D0000000"/>
|
||||||
<stroke android:width="4dp" android:color="?colorM3Surface"/>
|
<corners android:radius="4dp"/>
|
||||||
</shape>
|
</shape>
|
||||||
@@ -1,21 +1,12 @@
|
|||||||
<?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">
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<item>
|
<item android:state_selected="true">
|
||||||
<ripple
|
<layer-list>
|
||||||
android:color="@color/m3_on_surface_variant_overlay"
|
<item android:gravity="center" android:width="28dp" android:height="28dp">
|
||||||
android:radius="42dp" />
|
<shape android:shape="oval">
|
||||||
</item>
|
<stroke android:color="?colorM3OnSecondaryContainer" android:width="2dp"/>
|
||||||
<item>
|
</shape>
|
||||||
<selector>
|
|
||||||
<item android:state_selected="true">
|
|
||||||
<layer-list>
|
|
||||||
<item android:gravity="center" android:width="28dp" android:height="28dp">
|
|
||||||
<shape android:shape="oval">
|
|
||||||
<stroke android:color="?colorM3OnSecondaryContainer" android:width="2dp"/>
|
|
||||||
</shape>
|
|
||||||
</item>
|
|
||||||
</layer-list>
|
|
||||||
</item>
|
</item>
|
||||||
</selector>
|
</layer-list>
|
||||||
</item>
|
</item>
|
||||||
</layer-list>
|
</selector>
|
||||||
|
|||||||
@@ -2,4 +2,4 @@
|
|||||||
<ripple
|
<ripple
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:color="@color/m3_on_surface_variant_overlay"
|
android:color="@color/m3_on_surface_variant_overlay"
|
||||||
android:radius="42dp" />
|
android:radius="56dp" />
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="24dp"
|
|
||||||
android:height="24dp"
|
|
||||||
android:viewportWidth="24"
|
|
||||||
android:viewportHeight="24">
|
|
||||||
<path
|
|
||||||
android:fillColor="@android:color/white"
|
|
||||||
android:pathData="M4,15V13H20V15ZM4,11V9H20V11Z"/>
|
|
||||||
</vector>
|
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:pathData="M12,1.996C16.05,1.996 19.357,5.191 19.496,9.245L19.5,9.496V13.593L20.88,16.749C20.949,16.907 20.985,17.077 20.985,17.25C20.985,17.94 20.425,18.5 19.735,18.5L15,18.501C15,20.158 13.657,21.501 12,21.501C10.402,21.501 9.096,20.252 9.005,18.678L9,18.499L4.275,18.5C4.104,18.5 3.934,18.465 3.777,18.396C3.144,18.121 2.853,17.385 3.128,16.752L4.5,13.594V9.496C4.501,5.341 7.852,1.996 12,1.996ZM13.5,18.499L10.5,18.501C10.5,19.33 11.172,20.001 12,20.001C12.78,20.001 13.421,19.406 13.493,18.646L13.5,18.499ZM12,3.496C8.68,3.496 6.001,6.17 6,9.496V13.906L4.656,17H19.353L18,13.907L18,9.509L17.997,9.284C17.885,6.05 15.242,3.496 12,3.496Z"
|
||||||
|
android:fillColor="@color/fluent_default_icon_tint"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M8.083,9.969A0.508,0.508 0,0 0,8.807 10.682L11.494,7.955L11.494,14.897a0.508,0.508 0,1 0,1.016 0L12.509,7.958L15.193,10.682A0.508,0.508 45,0 0,15.917 9.969L12.452,6.453a0.635,0.635 0,0 0,-0.904 0z"
|
||||||
|
android:fillColor="@color/fluent_default_icon_tint"/>
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="20dp" android:height="20dp" android:viewportWidth="20" android:viewportHeight="20">
|
||||||
|
<path android:pathData="M14.69 11.503c1 0 1.81 0.81 1.81 1.81v0.689h-0.005c-0.034 0.78-0.248 1.757-1.123 2.555C14.416 17.43 12.765 18 10 18c-2.766 0-4.416-0.57-5.372-1.443-0.875-0.798-1.089-1.776-1.123-2.555H3.5v-0.69c0-0.999 0.81-1.809 1.81-1.809h9.38zM6.5 3C5.672 3 5 3.672 5 4.5v4C5 9.328 5.672 10 6.5 10h7c0.828 0 1.5-0.672 1.5-1.5v-4C15 3.672 14.328 3 13.5 3h-3V2.5C10.5 2.191 10.276 2 10 2S9.5 2.23 9.5 2.5V3h-3zM7 6.5c0-0.552 0.448-1 1-1s1 0.448 1 1-0.448 1-1 1-1-0.448-1-1zm4 0c0-0.552 0.448-1 1-1s1 0.448 1 1-0.448 1-1 1-1-0.448-1-1z" android:fillColor="@color/fluent_default_icon_tint"/>
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="20dp" android:height="20dp" android:viewportWidth="20" android:viewportHeight="20">
|
||||||
|
<path android:pathData="M8.46 1.897l0.99 0.39c0.353 0.138 0.746 0.138 1.099 0l0.99-0.39c1.21-0.477 2.582 0.091 3.102 1.285l0.424 0.975c0.151 0.348 0.429 0.626 0.777 0.777l0.975 0.424c1.194 0.52 1.762 1.891 1.285 3.103l-0.39 0.99c-0.139 0.352-0.139 0.745 0 1.098l0.39 0.99c0.477 1.21-0.091 2.582-1.285 3.102l-0.975 0.424c-0.348 0.151-0.626 0.429-0.777 0.777l-0.424 0.975c-0.52 1.194-1.891 1.762-3.103 1.285l-0.99-0.39c-0.352-0.139-0.745-0.139-1.098 0l-0.99 0.39c-1.21 0.477-2.582-0.091-3.102-1.285l-0.424-0.975c-0.151-0.348-0.429-0.626-0.777-0.777l-0.975-0.424c-1.194-0.52-1.762-1.891-1.285-3.103l0.39-0.99c0.138-0.352 0.138-0.745 0-1.098l-0.39-0.99C1.42 7.25 1.988 5.878 3.182 5.358l0.975-0.424c0.348-0.151 0.626-0.429 0.777-0.777l0.424-0.975C5.878 1.988 7.25 1.42 8.461 1.897zm3.445 0.93l-0.99 0.39c-0.588 0.232-1.243 0.232-1.831 0l-0.99-0.39C7.384 2.549 6.58 2.881 6.275 3.582L5.851 4.556C5.599 5.136 5.136 5.6 4.556 5.851L3.581 6.275c-0.7 0.305-1.033 1.109-0.753 1.82l0.389 0.989c0.232 0.588 0.232 1.243 0 1.831l-0.39 0.99c-0.279 0.71 0.054 1.514 0.754 1.819l0.975 0.424c0.58 0.252 1.043 0.715 1.295 1.295l0.424 0.975c0.305 0.7 1.109 1.033 1.82 0.753l0.989-0.39c0.588-0.23 1.243-0.23 1.831 0l0.99 0.39c0.71 0.28 1.514-0.053 1.819-0.753l0.424-0.975c0.252-0.58 0.715-1.043 1.295-1.295l0.975-0.424c0.7-0.305 1.033-1.11 0.753-1.82l-0.39-0.989c-0.23-0.588-0.23-1.243 0-1.831l0.39-0.99c0.28-0.71-0.053-1.514-0.753-1.819l-0.975-0.424c-0.58-0.252-1.043-0.715-1.295-1.295l-0.424-0.975c-0.305-0.7-1.11-1.033-1.82-0.753zm-2.927 8.944l3.648-4.104c0.183-0.206 0.5-0.225 0.706-0.041 0.183 0.163 0.218 0.43 0.095 0.633l-0.054 0.073-4 4.5c-0.17 0.19-0.451 0.22-0.655 0.081l-0.072-0.06-2-2c-0.195-0.195-0.195-0.512 0-0.707 0.173-0.174 0.443-0.193 0.638-0.058l0.069 0.058 1.625 1.625 3.648-4.104-3.648 4.104z" android:fillColor="@color/fluent_default_icon_tint"/>
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
||||||
|
<path android:pathData="M12 2c-0.667 0-1.32 0.065-1.95 0.19-0.407 0.08-0.671 0.475-0.591 0.882 0.08 0.406 0.475 0.67 0.881 0.59C10.877 3.556 11.431 3.5 12 3.5s1.123 0.056 1.66 0.162c0.406 0.08 0.8-0.184 0.881-0.59 0.08-0.407-0.184-0.801-0.59-0.882C13.319 2.065 12.667 2 12 2zM7.278 4.931c0.344-0.23 0.436-0.696 0.206-1.04-0.23-0.344-0.697-0.437-1.04-0.206-1.09 0.73-2.03 1.668-2.76 2.758-0.23 0.345-0.137 0.81 0.207 1.04 0.344 0.231 0.81 0.139 1.04-0.205 0.621-0.927 1.42-1.726 2.347-2.347zm10.279-1.246c-0.345-0.23-0.81-0.138-1.04 0.206-0.231 0.344-0.139 0.81 0.205 1.04 0.927 0.621 1.726 1.42 2.347 2.347 0.23 0.344 0.696 0.436 1.04 0.206 0.344-0.23 0.437-0.697 0.206-1.04-0.73-1.09-1.668-2.03-2.758-2.76zm4.253 6.364c-0.08-0.406-0.475-0.67-0.882-0.59-0.406 0.08-0.67 0.475-0.59 0.881 0.106 0.537 0.162 1.091 0.162 1.66s-0.056 1.123-0.162 1.66c-0.08 0.406 0.184 0.8 0.59 0.881 0.407 0.08 0.801-0.184 0.882-0.59C21.935 13.319 22 12.667 22 12s-0.065-1.32-0.19-1.95zM3.662 10.34c0.08-0.406-0.184-0.8-0.59-0.881-0.407-0.08-0.801 0.184-0.882 0.59C2.065 10.681 2 11.333 2 12s0.065 1.32 0.19 1.95c0.08 0.407 0.475 0.671 0.882 0.591 0.406-0.08 0.67-0.475 0.59-0.881C3.556 13.123 3.5 12.569 3.5 12s0.056-1.123 0.162-1.66zm1.27 6.382C4.7 16.378 4.234 16.286 3.89 16.516s-0.437 0.697-0.206 1.04c0.73 1.09 1.668 2.03 2.758 2.76 0.345 0.23 0.81 0.137 1.04-0.207 0.231-0.344 0.139-0.81-0.205-1.04-0.927-0.621-1.726-1.42-2.347-2.347zm15.383 0.835c0.23-0.345 0.138-0.81-0.206-1.04-0.344-0.231-0.81-0.139-1.04 0.205-0.621 0.927-1.42 1.726-2.347 2.347-0.344 0.23-0.436 0.696-0.206 1.04 0.23 0.344 0.697 0.437 1.04 0.206 1.09-0.73 2.03-1.668 2.76-2.758zm-9.975 2.781c-0.406-0.08-0.8 0.184-0.881 0.59-0.08 0.407 0.184 0.801 0.59 0.882C10.681 21.935 11.333 22 12 22s1.32-0.065 1.95-0.19c0.407-0.08 0.671-0.475 0.591-0.882-0.08-0.406-0.475-0.67-0.881-0.59C13.123 20.444 12.569 20.5 12 20.5s-1.123-0.056-1.66-0.162zM9 9.248c0-0.952 1.023-1.554 1.856-1.093l5.757 3.186C16.852 11.473 17 11.724 17 11.997c0 0.272-0.148 0.523-0.387 0.655l-5.757 3.187C10.023 16.299 9 15.697 9 14.745V9.247z" android:fillColor="@color/fluent_default_icon_tint"/>
|
||||||
|
</vector>
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="16dp" android:height="16dp" android:viewportWidth="16" android:viewportHeight="16">
|
|
||||||
<group android:translateX="-2" android:translateY="-1">
|
|
||||||
<path android:pathData="M10 2c1.657 0 3 1.343 3 3v1h1c1.105 0 2 0.895 2 2v7c0 1.105-0.895 2-2 2H6c-1.105 0-2-0.895-2-2V8c0-1.105 0.895-2 2-2h1V5c0-1.657 1.343-3 3-3zm0 8.5c-0.552 0-1 0.448-1 1s0.448 1 1 1 1-0.448 1-1-0.448-1-1-1zM10 4C9.448 4 9 4.448 9 5v1h2V5c0-0.552-0.448-1-1-1z" android:fillColor="@color/fluent_default_icon_tint"/>
|
|
||||||
</group>
|
|
||||||
</vector>
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:width="20dp"
|
|
||||||
android:height="20dp"
|
|
||||||
android:viewportWidth="20"
|
|
||||||
android:viewportHeight="20">
|
|
||||||
<path
|
|
||||||
android:fillColor="@android:color/white"
|
|
||||||
android:pathData="M5.5,19Q4.875,19 4.438,18.562Q4,18.125 4,17.5V9.5Q4,8.875 4.438,8.438Q4.875,8 5.5,8H6V6Q6,4.333 7.167,3.167Q8.333,2 10,2Q11.667,2 12.833,3.167Q14,4.333 14,6V8H14.5Q15.125,8 15.562,8.438Q16,8.875 16,9.5V17.5Q16,18.125 15.562,18.562Q15.125,19 14.5,19ZM7.5,8H12.5V6Q12.5,4.958 11.771,4.229Q11.042,3.5 10,3.5Q8.958,3.5 8.229,4.229Q7.5,4.958 7.5,6ZM10,15Q10.625,15 11.062,14.562Q11.5,14.125 11.5,13.5Q11.5,12.875 11.062,12.438Q10.625,12 10,12Q9.375,12 8.938,12.438Q8.5,12.875 8.5,13.5Q8.5,14.125 8.938,14.562Q9.375,15 10,15Z"/>
|
|
||||||
</vector>
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user