Compare commits
1 Commits
v2.1.6+for
...
revert-804
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f6ea0404ef |
@@ -15,8 +15,8 @@ android {
|
|||||||
applicationId "org.joinmastodon.android.sk"
|
applicationId "org.joinmastodon.android.sk"
|
||||||
minSdk 23
|
minSdk 23
|
||||||
targetSdk 33
|
targetSdk 33
|
||||||
versionCode 103
|
versionCode 98
|
||||||
versionName "2.1.6+fork.103"
|
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']
|
||||||
}
|
}
|
||||||
@@ -70,7 +70,7 @@ dependencies {
|
|||||||
implementation 'me.grishka.litex:viewpager:1.0.0'
|
implementation 'me.grishka.litex:viewpager:1.0.0'
|
||||||
implementation 'me.grishka.litex:viewpager2:1.0.0'
|
implementation 'me.grishka.litex:viewpager2:1.0.0'
|
||||||
implementation 'me.grishka.litex:palette:1.0.0'
|
implementation 'me.grishka.litex:palette:1.0.0'
|
||||||
implementation 'me.grishka.appkit:appkit:1.2.14'
|
implementation 'me.grishka.appkit:appkit:1.2.9'
|
||||||
implementation 'com.google.code.gson:gson:2.9.0'
|
implementation 'com.google.code.gson:gson:2.9.0'
|
||||||
implementation 'org.jsoup:jsoup:1.14.3'
|
implementation 'org.jsoup:jsoup:1.14.3'
|
||||||
implementation 'com.squareup:otto:1.3.8'
|
implementation 'com.squareup:otto:1.3.8'
|
||||||
|
|||||||
@@ -1,43 +1,56 @@
|
|||||||
13bells.com
|
13bells.com
|
||||||
1611.social
|
1611.social
|
||||||
4aem.com
|
4aem.com
|
||||||
5dollah.click
|
|
||||||
adachi.party
|
adachi.party
|
||||||
adtension.com
|
anime.website
|
||||||
annihilation.social
|
annihilation.social
|
||||||
anon-kenkai.com
|
anon-kenkai.com
|
||||||
asbestos.cafe
|
asbestos.cafe
|
||||||
bae.st
|
bae.st
|
||||||
|
bajax.us
|
||||||
banepo.st
|
banepo.st
|
||||||
|
baraag.net
|
||||||
bassam.social
|
bassam.social
|
||||||
battlepenguin.video
|
|
||||||
beefyboys.win
|
beefyboys.win
|
||||||
|
beepboop.ga
|
||||||
|
berserker.town
|
||||||
|
bikeshed.party
|
||||||
|
boks.moe
|
||||||
boymoder.biz
|
boymoder.biz
|
||||||
brainsoap.net
|
brainsoap.net
|
||||||
breastmilk.club
|
breastmilk.club
|
||||||
brighteon.social
|
brighteon.social
|
||||||
cachapa.xyz
|
bungle.online
|
||||||
canary.fedinuke.example.com
|
|
||||||
catgirl.life
|
|
||||||
cawfee.club
|
cawfee.club
|
||||||
childlove.space
|
|
||||||
clew.lol
|
clew.lol
|
||||||
clubcyberia.co
|
clubcyberia.co
|
||||||
|
collapsitarian.io
|
||||||
|
comfyboy.club
|
||||||
contrapointsfan.club
|
contrapointsfan.club
|
||||||
crucible.world
|
|
||||||
cum.camp
|
cum.camp
|
||||||
cum.salon
|
cum.salon
|
||||||
|
darknight-coffee.org
|
||||||
decayable.ink
|
decayable.ink
|
||||||
dembased.xyz
|
dembased.xyz
|
||||||
|
desupost.soy
|
||||||
detroitriotcity.com
|
detroitriotcity.com
|
||||||
djsumdog.com
|
eatthebugs.social
|
||||||
eientei.org
|
eientei.org
|
||||||
|
elementality.org
|
||||||
eveningzoo.club
|
eveningzoo.club
|
||||||
|
firedragonstudios.com
|
||||||
|
firefaithfellowship.com
|
||||||
fluf.club
|
fluf.club
|
||||||
|
foxfam.club
|
||||||
freak.university
|
freak.university
|
||||||
freeatlantis.com
|
freeatlantis.com
|
||||||
|
freedomstrike.org
|
||||||
|
freesoftwareextremist.com
|
||||||
|
freespeech.group
|
||||||
freespeechextremist.com
|
freespeechextremist.com
|
||||||
|
freetalklive.com
|
||||||
froth.zone
|
froth.zone
|
||||||
|
fulltermprivacy.com
|
||||||
gameliberty.club
|
gameliberty.club
|
||||||
gearlandia.haus
|
gearlandia.haus
|
||||||
genderheretics.xyz
|
genderheretics.xyz
|
||||||
@@ -46,34 +59,42 @@ gleasonator.com
|
|||||||
glee.li
|
glee.li
|
||||||
glindr.org
|
glindr.org
|
||||||
goyim.app
|
goyim.app
|
||||||
h5q.net
|
goyslop.cafe
|
||||||
haeder.net
|
haeder.net
|
||||||
handholding.io
|
handholding.io
|
||||||
hitchhiker.social
|
hitchhiker.social
|
||||||
|
hunk.city
|
||||||
iddqd.social
|
iddqd.social
|
||||||
|
intkos.link
|
||||||
|
justicewarrior.social
|
||||||
|
kawa-kun.com
|
||||||
kitsunemimi.club
|
kitsunemimi.club
|
||||||
kiwifarms.cc
|
kiwifarms.cc
|
||||||
|
kompost.cz
|
||||||
kurosawa.moe
|
kurosawa.moe
|
||||||
kyaruc.moe
|
|
||||||
leafposter.club
|
leafposter.club
|
||||||
|
leftychan.net
|
||||||
lewdieheaven.com
|
lewdieheaven.com
|
||||||
liberdon.com
|
liberdon.com
|
||||||
ligma.pro
|
ligma.pro
|
||||||
lolicon.rocks
|
lolicon.rocks
|
||||||
lolison.network
|
|
||||||
lolison.top
|
lolison.top
|
||||||
lovingexpressions.net
|
lovingexpressions.net
|
||||||
|
mahodou.moe
|
||||||
makemysarcophagus.com
|
makemysarcophagus.com
|
||||||
|
maladaptive.art
|
||||||
marsey.moe
|
marsey.moe
|
||||||
|
masochi.st
|
||||||
mastinator.com
|
mastinator.com
|
||||||
merovingian.club
|
merovingian.club
|
||||||
midwaytrades.com
|
midwaytrades.com
|
||||||
mirr0r.city
|
mirr0r.city
|
||||||
morale.ch
|
moa.st
|
||||||
mouse.services
|
mouse.services
|
||||||
mugicha.club
|
mugicha.club
|
||||||
narrativerry.xyz
|
narrativerry.xyz
|
||||||
natehiggers.online
|
natehiggers.online
|
||||||
|
neckbeard.xyz
|
||||||
needs.vodka
|
needs.vodka
|
||||||
neenster.org
|
neenster.org
|
||||||
nicecrew.digital
|
nicecrew.digital
|
||||||
@@ -82,18 +103,18 @@ noagendasocial.com
|
|||||||
noagendasocial.nl
|
noagendasocial.nl
|
||||||
noagendatube.com
|
noagendatube.com
|
||||||
nobodyhasthe.biz
|
nobodyhasthe.biz
|
||||||
norwoodzero.net
|
nukem.biz
|
||||||
nyanide.com
|
obo.sh
|
||||||
onionfarms.org
|
onionfarms.org
|
||||||
pawlicker.com
|
pawlicker.com
|
||||||
pawoo.net
|
pawoo.net
|
||||||
pedo.school
|
pedo.school
|
||||||
peervideo.club
|
|
||||||
piazza.today
|
piazza.today
|
||||||
pibvt.net
|
pibvt.net
|
||||||
pieville.net
|
pieville.net
|
||||||
pisskey.io
|
pisskey.io
|
||||||
plagu.ee
|
plagu.ee
|
||||||
|
pmth.us
|
||||||
poa.st
|
poa.st
|
||||||
poast.org
|
poast.org
|
||||||
poast.tv
|
poast.tv
|
||||||
@@ -102,18 +123,17 @@ prospeech.space
|
|||||||
quodverum.com
|
quodverum.com
|
||||||
r18.social
|
r18.social
|
||||||
rakket.app
|
rakket.app
|
||||||
rapemeat.express
|
|
||||||
rapemeat.solutions
|
rapemeat.solutions
|
||||||
rayci.st
|
rdrama.cc
|
||||||
rebelbase.site
|
rebelbase.site
|
||||||
|
retardedniggers.forsale
|
||||||
|
rojogato.com
|
||||||
ryona.agency
|
ryona.agency
|
||||||
sad.cab
|
|
||||||
schwartzwelt.xyz
|
schwartzwelt.xyz
|
||||||
seal.cafe
|
seal.cafe
|
||||||
shaw.app
|
|
||||||
shigusegubu.club
|
shigusegubu.club
|
||||||
shitpost.cloud
|
shitpost.cloud
|
||||||
shortstacksran.ch
|
shota.house
|
||||||
silliness.observer
|
silliness.observer
|
||||||
skinheads.eu
|
skinheads.eu
|
||||||
skinheads.io
|
skinheads.io
|
||||||
@@ -128,20 +148,23 @@ sneed.social
|
|||||||
sonichu.com
|
sonichu.com
|
||||||
spinster.xyz
|
spinster.xyz
|
||||||
springbo.cc
|
springbo.cc
|
||||||
|
starnix.network
|
||||||
strelizia.net
|
strelizia.net
|
||||||
|
syspxl.xyz
|
||||||
tastingtraffic.net
|
tastingtraffic.net
|
||||||
teci.world
|
teci.world
|
||||||
theapex.social
|
theapex.social
|
||||||
thechimp.zone
|
|
||||||
thenobody.club
|
|
||||||
thepostearthdestination.com
|
thepostearthdestination.com
|
||||||
tkammer.de
|
tkammer.de
|
||||||
trumpislovetrumpis.life
|
trumpislovetrumpis.life
|
||||||
truthsocial.co.in
|
truthsocial.co.in
|
||||||
usualsuspects.lol
|
urchan.org
|
||||||
varishangout.net
|
varishangout.net
|
||||||
vtuberfan.social
|
whinge.house
|
||||||
|
whinge.town
|
||||||
|
wideboys.org
|
||||||
wolfgirl.bar
|
wolfgirl.bar
|
||||||
xn--p1abe3d.xn--80asehdb
|
xn--p1abe3d.xn--80asehdb
|
||||||
yggdrasil.social
|
yggdrasil.social
|
||||||
youjo.love
|
youjo.love
|
||||||
|
zztails.gay
|
||||||
|
|||||||
@@ -281,7 +281,7 @@ public class AudioPlayerService extends Service{
|
|||||||
|
|
||||||
if(playerReady){
|
if(playerReady){
|
||||||
boolean isPlaying=player.isPlaying();
|
boolean isPlaying=player.isPlaying();
|
||||||
bldr.addAction(new Notification.Action.Builder(Icon.createWithResource(this, isPlaying ? R.drawable.ic_fluent_pause_24_filled : R.drawable.ic_fluent_play_24_filled),
|
bldr.addAction(new Notification.Action.Builder(Icon.createWithResource(this, isPlaying ? R.drawable.ic_pause_24 : R.drawable.ic_play_24),
|
||||||
getString(isPlaying ? R.string.pause : R.string.play),
|
getString(isPlaying ? R.string.pause : R.string.play),
|
||||||
PendingIntent.getBroadcast(this, 2, new Intent(ACTION_PLAY_PAUSE), PendingIntent.FLAG_IMMUTABLE))
|
PendingIntent.getBroadcast(this, 2, new Intent(ACTION_PLAY_PAUSE), PendingIntent.FLAG_IMMUTABLE))
|
||||||
.build());
|
.build());
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ public class ExternalShareActivity extends FragmentStackActivity{
|
|||||||
UiUtils.setUserPreferredTheme(this);
|
UiUtils.setUserPreferredTheme(this);
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
if(savedInstanceState==null){
|
if(savedInstanceState==null){
|
||||||
|
|
||||||
Optional<String> text = Optional.ofNullable(getIntent().getStringExtra(Intent.EXTRA_TEXT));
|
Optional<String> text = Optional.ofNullable(getIntent().getStringExtra(Intent.EXTRA_TEXT));
|
||||||
Optional<Pair<String, Optional<String>>> fediHandle = text.flatMap(UiUtils::parseFediverseHandle);
|
Optional<Pair<String, Optional<String>>> fediHandle = text.flatMap(UiUtils::parseFediverseHandle);
|
||||||
boolean isFediUrl = text.map(UiUtils::looksLikeFediverseUrl).orElse(false);
|
boolean isFediUrl = text.map(UiUtils::looksLikeFediverseUrl).orElse(false);
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
package org.joinmastodon.android;
|
package org.joinmastodon.android;
|
||||||
|
|
||||||
import static org.joinmastodon.android.api.MastodonAPIController.gson;
|
import static org.joinmastodon.android.api.MastodonAPIController.gson;
|
||||||
import static org.joinmastodon.android.api.session.AccountLocalPreferences.ColorPreference.MATERIAL3;
|
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.annotation.StringRes;
|
||||||
|
|
||||||
import com.google.gson.JsonSyntaxException;
|
import com.google.gson.JsonSyntaxException;
|
||||||
import com.google.gson.reflect.TypeToken;
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
|
||||||
import org.joinmastodon.android.api.session.AccountLocalPreferences;
|
import org.joinmastodon.android.api.session.AccountLocalPreferences;
|
||||||
import org.joinmastodon.android.api.session.AccountLocalPreferences.ColorPreference;
|
|
||||||
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.model.ContentType;
|
import org.joinmastodon.android.model.ContentType;
|
||||||
@@ -55,14 +55,11 @@ public class GlobalUserPreferences{
|
|||||||
public static boolean allowRemoteLoading;
|
public static boolean allowRemoteLoading;
|
||||||
public static boolean forwardReportDefault;
|
public static boolean forwardReportDefault;
|
||||||
public static AutoRevealMode autoRevealEqualSpoilers;
|
public static AutoRevealMode autoRevealEqualSpoilers;
|
||||||
|
public static ColorPreference color;
|
||||||
public static boolean disableM3PillActiveIndicator;
|
public static boolean disableM3PillActiveIndicator;
|
||||||
public static boolean showNavigationLabels;
|
public static boolean showNavigationLabels;
|
||||||
public static boolean displayPronounsInTimelines, displayPronounsInThreads, displayPronounsInUserListings;
|
public static boolean displayPronounsInTimelines, displayPronounsInThreads, displayPronounsInUserListings;
|
||||||
public static boolean overlayMedia;
|
public static boolean overlayMedia;
|
||||||
public static boolean showSuicideHelp;
|
|
||||||
public static boolean underlinedLinks;
|
|
||||||
public static ColorPreference color;
|
|
||||||
public static boolean likeIcon;
|
|
||||||
|
|
||||||
private static SharedPreferences getPrefs(){
|
private static SharedPreferences getPrefs(){
|
||||||
return MastodonApp.context.getSharedPreferences("global", Context.MODE_PRIVATE);
|
return MastodonApp.context.getSharedPreferences("global", Context.MODE_PRIVATE);
|
||||||
@@ -117,15 +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", false);
|
showNavigationLabels=prefs.getBoolean("showNavigationLabels", true);
|
||||||
displayPronounsInTimelines=prefs.getBoolean("displayPronounsInTimelines", true);
|
displayPronounsInTimelines=prefs.getBoolean("displayPronounsInTimelines", true);
|
||||||
displayPronounsInThreads=prefs.getBoolean("displayPronounsInThreads", true);
|
displayPronounsInThreads=prefs.getBoolean("displayPronounsInThreads", true);
|
||||||
displayPronounsInUserListings=prefs.getBoolean("displayPronounsInUserListings", true);
|
displayPronounsInUserListings=prefs.getBoolean("displayPronounsInUserListings", true);
|
||||||
overlayMedia=prefs.getBoolean("overlayMedia", false);
|
overlayMedia=prefs.getBoolean("overlayMedia", false);
|
||||||
showSuicideHelp=prefs.getBoolean("showSuicideHelp", true);
|
|
||||||
underlinedLinks=prefs.getBoolean("underlinedLinks", true);
|
|
||||||
color=ColorPreference.valueOf(prefs.getString("color", MATERIAL3.name()));
|
|
||||||
likeIcon=prefs.getBoolean("likeIcon", false);
|
|
||||||
|
|
||||||
if (prefs.contains("prefixRepliesWithRe")) {
|
if (prefs.contains("prefixRepliesWithRe")) {
|
||||||
prefixReplies = prefs.getBoolean("prefixRepliesWithRe", false)
|
prefixReplies = prefs.getBoolean("prefixRepliesWithRe", false)
|
||||||
@@ -136,11 +129,14 @@ public class GlobalUserPreferences{
|
|||||||
.apply();
|
.apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
int migrationLevel=prefs.getInt("migrationLevel", BuildConfig.VERSION_CODE);
|
try {
|
||||||
if(migrationLevel < 61)
|
color=ColorPreference.valueOf(prefs.getString("color", ColorPreference.PINK.name()));
|
||||||
migrateToUpstreamVersion61();
|
} catch (IllegalArgumentException|ClassCastException ignored) {
|
||||||
if(migrationLevel < BuildConfig.VERSION_CODE)
|
// invalid color name or color was previously saved as integer
|
||||||
prefs.edit().putInt("migrationLevel", BuildConfig.VERSION_CODE).apply();
|
color=ColorPreference.PINK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(prefs.getInt("migrationLevel", 0) < 61) migrateToUpstreamVersion61();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void save(){
|
public static void save(){
|
||||||
@@ -171,6 +167,7 @@ public class GlobalUserPreferences{
|
|||||||
.putBoolean("spectatorMode", spectatorMode)
|
.putBoolean("spectatorMode", spectatorMode)
|
||||||
.putBoolean("autoHideFab", autoHideFab)
|
.putBoolean("autoHideFab", autoHideFab)
|
||||||
.putBoolean("compactReblogReplyLine", compactReblogReplyLine)
|
.putBoolean("compactReblogReplyLine", compactReblogReplyLine)
|
||||||
|
.putString("color", color.name())
|
||||||
.putBoolean("allowRemoteLoading", allowRemoteLoading)
|
.putBoolean("allowRemoteLoading", allowRemoteLoading)
|
||||||
.putString("autoRevealEqualSpoilers", autoRevealEqualSpoilers.name())
|
.putString("autoRevealEqualSpoilers", autoRevealEqualSpoilers.name())
|
||||||
.putBoolean("forwardReportDefault", forwardReportDefault)
|
.putBoolean("forwardReportDefault", forwardReportDefault)
|
||||||
@@ -180,34 +177,9 @@ public class GlobalUserPreferences{
|
|||||||
.putBoolean("displayPronounsInThreads", displayPronounsInThreads)
|
.putBoolean("displayPronounsInThreads", displayPronounsInThreads)
|
||||||
.putBoolean("displayPronounsInUserListings", displayPronounsInUserListings)
|
.putBoolean("displayPronounsInUserListings", displayPronounsInUserListings)
|
||||||
.putBoolean("overlayMedia", overlayMedia)
|
.putBoolean("overlayMedia", overlayMedia)
|
||||||
.putBoolean("showSuicideHelp", showSuicideHelp)
|
|
||||||
.putBoolean("underlinedLinks", underlinedLinks)
|
|
||||||
.putString("color", color.name())
|
|
||||||
.putBoolean("likeIcon", likeIcon)
|
|
||||||
.apply();
|
.apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum ThemePreference{
|
|
||||||
AUTO,
|
|
||||||
LIGHT,
|
|
||||||
DARK
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum AutoRevealMode {
|
|
||||||
NEVER,
|
|
||||||
THREADS,
|
|
||||||
DISCUSSIONS
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum PrefixRepliesMode {
|
|
||||||
NEVER,
|
|
||||||
ALWAYS,
|
|
||||||
TO_OTHERS
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//region preferences migrations
|
|
||||||
|
|
||||||
private static void migrateToUpstreamVersion61(){
|
private static void migrateToUpstreamVersion61(){
|
||||||
Log.d(TAG, "Migrating preferences to upstream version 61!!");
|
Log.d(TAG, "Migrating preferences to upstream version 61!!");
|
||||||
|
|
||||||
@@ -254,8 +226,49 @@ public class GlobalUserPreferences{
|
|||||||
|
|
||||||
localPrefs.save();
|
localPrefs.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prefs.edit().putInt("migrationLevel", 61).apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
//endregion
|
public enum ColorPreference{
|
||||||
|
MATERIAL3,
|
||||||
|
PINK,
|
||||||
|
PURPLE,
|
||||||
|
GREEN,
|
||||||
|
BLUE,
|
||||||
|
BROWN,
|
||||||
|
RED,
|
||||||
|
YELLOW;
|
||||||
|
|
||||||
|
public @StringRes int getName() {
|
||||||
|
return switch(this){
|
||||||
|
case MATERIAL3 -> R.string.sk_color_palette_material3;
|
||||||
|
case PINK -> R.string.sk_color_palette_pink;
|
||||||
|
case PURPLE -> R.string.sk_color_palette_purple;
|
||||||
|
case GREEN -> R.string.sk_color_palette_green;
|
||||||
|
case BLUE -> R.string.sk_color_palette_blue;
|
||||||
|
case BROWN -> R.string.sk_color_palette_brown;
|
||||||
|
case RED -> R.string.sk_color_palette_red;
|
||||||
|
case YELLOW -> R.string.sk_color_palette_yellow;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ThemePreference{
|
||||||
|
AUTO,
|
||||||
|
LIGHT,
|
||||||
|
DARK
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum AutoRevealMode {
|
||||||
|
NEVER,
|
||||||
|
THREADS,
|
||||||
|
DISCUSSIONS
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum PrefixRepliesMode {
|
||||||
|
NEVER,
|
||||||
|
ALWAYS,
|
||||||
|
TO_OTHERS
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,12 +39,54 @@ import me.grishka.appkit.api.ErrorResponse;
|
|||||||
public class MainActivity extends FragmentStackActivity implements ProvidesAssistContent {
|
public class MainActivity extends FragmentStackActivity implements ProvidesAssistContent {
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(@Nullable Bundle savedInstanceState){
|
protected void onCreate(@Nullable Bundle savedInstanceState){
|
||||||
AccountSession session=getCurrentSession();
|
UiUtils.setUserPreferredTheme(this);
|
||||||
UiUtils.setUserPreferredTheme(this, session);
|
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
if(savedInstanceState==null){
|
if(savedInstanceState==null){
|
||||||
restartHomeFragment();
|
if(AccountSessionManager.getInstance().getLoggedInAccounts().isEmpty()){
|
||||||
|
showFragmentClearingBackStack(new CustomWelcomeFragment());
|
||||||
|
}else{
|
||||||
|
AccountSession session;
|
||||||
|
Bundle args=new Bundle();
|
||||||
|
Intent intent=getIntent();
|
||||||
|
if(intent.hasExtra("fromExternalShare")) {
|
||||||
|
AccountSessionManager.getInstance()
|
||||||
|
.setLastActiveAccountID(intent.getStringExtra("account"));
|
||||||
|
AccountSessionManager.getInstance().maybeUpdateLocalInfo(
|
||||||
|
AccountSessionManager.getInstance().getLastActiveAccount());
|
||||||
|
showFragmentForExternalShare(intent.getExtras());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean fromNotification = intent.getBooleanExtra("fromNotification", false);
|
||||||
|
boolean hasNotification = intent.hasExtra("notification");
|
||||||
|
if(fromNotification){
|
||||||
|
String accountID=intent.getStringExtra("accountID");
|
||||||
|
try{
|
||||||
|
session=AccountSessionManager.getInstance().getAccount(accountID);
|
||||||
|
if(!hasNotification) args.putString("tab", "notifications");
|
||||||
|
}catch(IllegalStateException x){
|
||||||
|
session=AccountSessionManager.getInstance().getLastActiveAccount();
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
session=AccountSessionManager.getInstance().getLastActiveAccount();
|
||||||
|
}
|
||||||
|
AccountSessionManager.getInstance().maybeUpdateLocalInfo(session);
|
||||||
|
args.putString("account", session.getID());
|
||||||
|
Fragment fragment=session.activated ? new HomeFragment() : new AccountActivationFragment();
|
||||||
|
fragment.setArguments(args);
|
||||||
|
if(fromNotification && hasNotification){
|
||||||
|
Notification notification=Parcels.unwrap(intent.getParcelableExtra("notification"));
|
||||||
|
showFragmentForNotification(notification, session.getID());
|
||||||
|
} else if (intent.getBooleanExtra("compose", false)){
|
||||||
|
showCompose();
|
||||||
|
} else if (Intent.ACTION_VIEW.equals(intent.getAction())){
|
||||||
|
handleURL(intent.getData(), null);
|
||||||
|
} else {
|
||||||
|
showFragmentClearingBackStack(fragment);
|
||||||
|
maybeRequestNotificationsPermission();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(GithubSelfUpdater.needSelfUpdating()){
|
if(GithubSelfUpdater.needSelfUpdating()){
|
||||||
@@ -101,7 +143,7 @@ public class MainActivity extends FragmentStackActivity implements ProvidesAssis
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void openSearchQuery(String q, String accountID, int progressText, boolean fromSearch){
|
public void openSearchQuery(String q, String accountID, int progressText, boolean fromSearch){
|
||||||
new GetSearchResults(q, null, true, null, 0, 0)
|
new GetSearchResults(q, null, true)
|
||||||
.setCallback(new Callback<>(){
|
.setCallback(new Callback<>(){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(SearchResults result){
|
public void onSuccess(SearchResults result){
|
||||||
@@ -217,81 +259,4 @@ public class MainActivity extends FragmentStackActivity implements ProvidesAssis
|
|||||||
Fragment fragment = getCurrentFragment();
|
Fragment fragment = getCurrentFragment();
|
||||||
if (fragment != null) callFragmentToProvideAssistContent(fragment, assistContent);
|
if (fragment != null) callFragmentToProvideAssistContent(fragment, assistContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AccountSession getCurrentSession(){
|
|
||||||
AccountSession session;
|
|
||||||
Bundle args=new Bundle();
|
|
||||||
Intent intent=getIntent();
|
|
||||||
if(intent.hasExtra("fromExternalShare")) {
|
|
||||||
return AccountSessionManager.getInstance()
|
|
||||||
.getAccount(intent.getStringExtra("account"));
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean fromNotification = intent.getBooleanExtra("fromNotification", false);
|
|
||||||
boolean hasNotification = intent.hasExtra("notification");
|
|
||||||
if(fromNotification){
|
|
||||||
String accountID=intent.getStringExtra("accountID");
|
|
||||||
try{
|
|
||||||
session=AccountSessionManager.getInstance().getAccount(accountID);
|
|
||||||
if(!hasNotification) args.putString("tab", "notifications");
|
|
||||||
}catch(IllegalStateException x){
|
|
||||||
session=AccountSessionManager.getInstance().getLastActiveAccount();
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
session=AccountSessionManager.getInstance().getLastActiveAccount();
|
|
||||||
}
|
|
||||||
return session;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void restartActivity(){
|
|
||||||
finish();
|
|
||||||
startActivity(new Intent(this, MainActivity.class));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void restartHomeFragment(){
|
|
||||||
if(AccountSessionManager.getInstance().getLoggedInAccounts().isEmpty()){
|
|
||||||
showFragmentClearingBackStack(new CustomWelcomeFragment());
|
|
||||||
}else{
|
|
||||||
AccountSession session;
|
|
||||||
Bundle args=new Bundle();
|
|
||||||
Intent intent=getIntent();
|
|
||||||
if(intent.hasExtra("fromExternalShare")) {
|
|
||||||
AccountSessionManager.getInstance()
|
|
||||||
.setLastActiveAccountID(intent.getStringExtra("account"));
|
|
||||||
AccountSessionManager.getInstance().maybeUpdateLocalInfo(
|
|
||||||
AccountSessionManager.getInstance().getLastActiveAccount());
|
|
||||||
showFragmentForExternalShare(intent.getExtras());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean fromNotification = intent.getBooleanExtra("fromNotification", false);
|
|
||||||
boolean hasNotification = intent.hasExtra("notification");
|
|
||||||
if(fromNotification){
|
|
||||||
String accountID=intent.getStringExtra("accountID");
|
|
||||||
try{
|
|
||||||
session=AccountSessionManager.getInstance().getAccount(accountID);
|
|
||||||
if(!hasNotification) args.putString("tab", "notifications");
|
|
||||||
}catch(IllegalStateException x){
|
|
||||||
session=AccountSessionManager.getInstance().getLastActiveAccount();
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
session=AccountSessionManager.getInstance().getLastActiveAccount();
|
|
||||||
}
|
|
||||||
AccountSessionManager.getInstance().maybeUpdateLocalInfo(session);
|
|
||||||
args.putString("account", session.getID());
|
|
||||||
Fragment fragment=session.activated ? new HomeFragment() : new AccountActivationFragment();
|
|
||||||
fragment.setArguments(args);
|
|
||||||
if(fromNotification && hasNotification){
|
|
||||||
Notification notification=Parcels.unwrap(intent.getParcelableExtra("notification"));
|
|
||||||
showFragmentForNotification(notification, session.getID());
|
|
||||||
} else if (intent.getBooleanExtra("compose", false)){
|
|
||||||
showCompose();
|
|
||||||
} else if (Intent.ACTION_VIEW.equals(intent.getAction())){
|
|
||||||
handleURL(intent.getData(), null);
|
|
||||||
} else {
|
|
||||||
showFragmentClearingBackStack(fragment);
|
|
||||||
maybeRequestNotificationsPermission();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
@@ -174,8 +176,6 @@ public class PushNotificationReceiver extends BroadcastReceiver{
|
|||||||
List<NotificationChannel> channels=Arrays.stream(PushNotification.Type.values())
|
List<NotificationChannel> channels=Arrays.stream(PushNotification.Type.values())
|
||||||
.map(type->{
|
.map(type->{
|
||||||
NotificationChannel channel=new NotificationChannel(accountID+"_"+type, context.getString(type.localizedName), NotificationManager.IMPORTANCE_DEFAULT);
|
NotificationChannel channel=new NotificationChannel(accountID+"_"+type, context.getString(type.localizedName), NotificationManager.IMPORTANCE_DEFAULT);
|
||||||
channel.setLightColor(context.getColor(R.color.primary_700));
|
|
||||||
channel.enableLights(true);
|
|
||||||
channel.setGroup(accountID);
|
channel.setGroup(accountID);
|
||||||
return channel;
|
return channel;
|
||||||
})
|
})
|
||||||
@@ -205,12 +205,11 @@ public class PushNotificationReceiver extends BroadcastReceiver{
|
|||||||
.setShowWhen(true)
|
.setShowWhen(true)
|
||||||
.setCategory(Notification.CATEGORY_SOCIAL)
|
.setCategory(Notification.CATEGORY_SOCIAL)
|
||||||
.setAutoCancel(true)
|
.setAutoCancel(true)
|
||||||
.setLights(UiUtils.getThemeColor(context, android.R.attr.colorAccent), 500, 1000)
|
|
||||||
.setColor(UiUtils.getThemeColor(context, android.R.attr.colorAccent));
|
.setColor(UiUtils.getThemeColor(context, android.R.attr.colorAccent));
|
||||||
|
|
||||||
if (!GlobalUserPreferences.uniformNotificationIcon) {
|
if (!GlobalUserPreferences.uniformNotificationIcon) {
|
||||||
builder.setSmallIcon(switch (pn.notificationType) {
|
builder.setSmallIcon(switch (pn.notificationType) {
|
||||||
case FAVORITE -> GlobalUserPreferences.likeIcon ? R.drawable.ic_fluent_heart_24_filled : R.drawable.ic_fluent_star_24_filled;
|
case FAVORITE -> R.drawable.ic_fluent_star_24_filled;
|
||||||
case REBLOG -> R.drawable.ic_fluent_arrow_repeat_all_24_filled;
|
case REBLOG -> R.drawable.ic_fluent_arrow_repeat_all_24_filled;
|
||||||
case FOLLOW -> R.drawable.ic_fluent_person_add_24_filled;
|
case FOLLOW -> R.drawable.ic_fluent_person_add_24_filled;
|
||||||
case MENTION -> R.drawable.ic_fluent_mention_24_filled;
|
case MENTION -> R.drawable.ic_fluent_mention_24_filled;
|
||||||
@@ -322,8 +321,8 @@ public class PushNotificationReceiver extends BroadcastReceiver{
|
|||||||
|
|
||||||
CreateStatus.Request req=new CreateStatus.Request();
|
CreateStatus.Request req=new CreateStatus.Request();
|
||||||
req.status = initialText + input.toString();
|
req.status = initialText + input.toString();
|
||||||
req.language = notification.status.language;
|
req.language = preferences.postingDefaultLanguage;
|
||||||
req.visibility = notification.status.visibility;
|
req.visibility = preferences.postingDefaultVisibility;
|
||||||
req.inReplyToId = notification.status.id;
|
req.inReplyToId = notification.status.id;
|
||||||
|
|
||||||
if (notification.status.hasSpoiler() &&
|
if (notification.status.hasSpoiler() &&
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ import java.io.IOException;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import me.grishka.appkit.api.Callback;
|
import me.grishka.appkit.api.Callback;
|
||||||
@@ -70,11 +69,12 @@ public class CacheController{
|
|||||||
Status status=MastodonAPIController.gson.fromJson(cursor.getString(0), Status.class);
|
Status status=MastodonAPIController.gson.fromJson(cursor.getString(0), Status.class);
|
||||||
status.postprocess();
|
status.postprocess();
|
||||||
int flags=cursor.getInt(1);
|
int flags=cursor.getInt(1);
|
||||||
status.hasGapAfter=((flags & POST_FLAG_GAP_AFTER)!=0) ? status.id : null;
|
status.hasGapAfter=((flags & POST_FLAG_GAP_AFTER)!=0);
|
||||||
newMaxID=status.id;
|
newMaxID=status.id;
|
||||||
result.add(status);
|
result.add(status);
|
||||||
}while(cursor.moveToNext());
|
}while(cursor.moveToNext());
|
||||||
String _newMaxID=newMaxID;
|
String _newMaxID=newMaxID;
|
||||||
|
AccountSessionManager.get(accountID).filterStatuses(result, FilterContext.HOME);
|
||||||
uiHandler.post(()->callback.onSuccess(new CacheablePaginatedResponse<>(result, _newMaxID, true)));
|
uiHandler.post(()->callback.onSuccess(new CacheablePaginatedResponse<>(result, _newMaxID, true)));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -86,7 +86,9 @@ public class CacheController{
|
|||||||
.setCallback(new Callback<>(){
|
.setCallback(new Callback<>(){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<Status> result){
|
public void onSuccess(List<Status> result){
|
||||||
callback.onSuccess(new CacheablePaginatedResponse<>(result, result.isEmpty() ? null : result.get(result.size()-1).id, false));
|
ArrayList<Status> filtered=new ArrayList<>(result);
|
||||||
|
AccountSessionManager.get(accountID).filterStatuses(filtered, FilterContext.HOME);
|
||||||
|
callback.onSuccess(new CacheablePaginatedResponse<>(filtered, result.isEmpty() ? null : result.get(result.size()-1).id, false));
|
||||||
putHomeTimeline(result, maxID==null);
|
putHomeTimeline(result, maxID==null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,7 +116,7 @@ public class CacheController{
|
|||||||
values.put("id", s.id);
|
values.put("id", s.id);
|
||||||
values.put("json", MastodonAPIController.gson.toJson(s));
|
values.put("json", MastodonAPIController.gson.toJson(s));
|
||||||
int flags=0;
|
int flags=0;
|
||||||
if(Objects.equals(s.hasGapAfter, s.id))
|
if(s.hasGapAfter)
|
||||||
flags|=POST_FLAG_GAP_AFTER;
|
flags|=POST_FLAG_GAP_AFTER;
|
||||||
values.put("flags", flags);
|
values.put("flags", flags);
|
||||||
values.put("time", s.createdAt.getEpochSecond());
|
values.put("time", s.createdAt.getEpochSecond());
|
||||||
|
|||||||
@@ -130,11 +130,12 @@ public class PushSubscriptionManager{
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String endpoint = "https://app.joinmastodon.org/relay-to/fcm/"+deviceToken+"/";
|
String endpoint = "https://app.joinmastodon.org/relay-to/fcm/"+deviceToken+"/"+accountID;
|
||||||
registerAccountForPush(subscription, endpoint);
|
registerAccountForPush(subscription, endpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void registerAccountForPush(PushSubscription subscription, String 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;
|
||||||
@@ -163,13 +164,7 @@ 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(endpoint,
|
||||||
//work-around for adding the randomAccountId
|
|
||||||
String newEndpoint = endpoint;
|
|
||||||
if (endpoint.startsWith("https://app.joinmastodon.org/relay-to/fcm/"))
|
|
||||||
newEndpoint += pushAccountID;
|
|
||||||
|
|
||||||
new RegisterForPushNotifications(newEndpoint,
|
|
||||||
encodedPublicKey,
|
encodedPublicKey,
|
||||||
encodedAuthKey,
|
encodedAuthKey,
|
||||||
subscription==null ? PushSubscription.Alerts.ofAll() : subscription.alerts,
|
subscription==null ? PushSubscription.Alerts.ofAll() : subscription.alerts,
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
package org.joinmastodon.android.api.requests.accounts;
|
|
||||||
|
|
||||||
import com.google.gson.reflect.TypeToken;
|
|
||||||
|
|
||||||
import org.joinmastodon.android.api.requests.HeaderPaginationRequest;
|
|
||||||
import org.joinmastodon.android.model.Account;
|
|
||||||
|
|
||||||
public class GetAccountBlocks extends HeaderPaginationRequest<Account>{
|
|
||||||
public GetAccountBlocks(String maxID, int limit){
|
|
||||||
super(HttpMethod.GET, "/blocks", new TypeToken<>(){});
|
|
||||||
if(maxID!=null)
|
|
||||||
addQueryParameter("max_id", maxID);
|
|
||||||
if(limit>0)
|
|
||||||
addQueryParameter("limit", limit+"");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
package org.joinmastodon.android.api.requests.accounts;
|
|
||||||
|
|
||||||
import com.google.gson.reflect.TypeToken;
|
|
||||||
|
|
||||||
import org.joinmastodon.android.api.requests.HeaderPaginationRequest;
|
|
||||||
import org.joinmastodon.android.model.Account;
|
|
||||||
|
|
||||||
public class GetAccountMutes extends HeaderPaginationRequest<Account>{
|
|
||||||
public GetAccountMutes(String maxID, int limit){
|
|
||||||
super(HttpMethod.GET, "/mutes/", new TypeToken<>(){});
|
|
||||||
if(maxID!=null)
|
|
||||||
addQueryParameter("max_id", maxID);
|
|
||||||
if(limit>0)
|
|
||||||
addQueryParameter("limit", limit+"");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -6,19 +6,18 @@ import org.joinmastodon.android.model.Preferences;
|
|||||||
import org.joinmastodon.android.model.StatusPrivacy;
|
import org.joinmastodon.android.model.StatusPrivacy;
|
||||||
|
|
||||||
public class UpdateAccountCredentialsPreferences extends MastodonAPIRequest<Account>{
|
public class UpdateAccountCredentialsPreferences extends MastodonAPIRequest<Account>{
|
||||||
public UpdateAccountCredentialsPreferences(Preferences preferences, Boolean locked, Boolean discoverable, Boolean indexable){
|
public UpdateAccountCredentialsPreferences(Preferences preferences, Boolean locked, Boolean discoverable){
|
||||||
super(HttpMethod.PATCH, "/accounts/update_credentials", Account.class);
|
super(HttpMethod.PATCH, "/accounts/update_credentials", Account.class);
|
||||||
setRequestBody(new Request(locked, discoverable, indexable, new RequestSource(preferences.postingDefaultVisibility, preferences.postingDefaultLanguage)));
|
setRequestBody(new Request(locked, discoverable, new RequestSource(preferences.postingDefaultVisibility, preferences.postingDefaultLanguage)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class Request{
|
private static class Request{
|
||||||
public Boolean locked, discoverable, indexable;
|
public Boolean locked, discoverable;
|
||||||
public RequestSource source;
|
public RequestSource source;
|
||||||
|
|
||||||
public Request(Boolean locked, Boolean discoverable, Boolean indexable, RequestSource source){
|
public Request(Boolean locked, Boolean discoverable, RequestSource source){
|
||||||
this.locked=locked;
|
this.locked=locked;
|
||||||
this.discoverable=discoverable;
|
this.discoverable=discoverable;
|
||||||
this.indexable=indexable;
|
|
||||||
this.source=source;
|
this.source=source;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,9 +6,6 @@ import org.joinmastodon.android.model.FilterContext;
|
|||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import androidx.annotation.Keep;
|
|
||||||
|
|
||||||
@Keep
|
|
||||||
class FilterRequest{
|
class FilterRequest{
|
||||||
public String title;
|
public String title;
|
||||||
public EnumSet<FilterContext> context;
|
public EnumSet<FilterContext> context;
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import okhttp3.MultipartBody;
|
|||||||
import okhttp3.RequestBody;
|
import okhttp3.RequestBody;
|
||||||
|
|
||||||
public class PleromaMarkNotificationsRead extends MastodonAPIRequest<List<Notification>> {
|
public class PleromaMarkNotificationsRead extends MastodonAPIRequest<List<Notification>> {
|
||||||
private final String maxID;
|
private String maxID;
|
||||||
public PleromaMarkNotificationsRead(String maxID) {
|
public PleromaMarkNotificationsRead(String maxID) {
|
||||||
super(HttpMethod.POST, "/pleroma/notifications/read", new TypeToken<>(){});
|
super(HttpMethod.POST, "/pleroma/notifications/read", new TypeToken<>(){});
|
||||||
this.maxID = maxID;
|
this.maxID = maxID;
|
||||||
|
|||||||
@@ -4,19 +4,13 @@ import org.joinmastodon.android.api.MastodonAPIRequest;
|
|||||||
import org.joinmastodon.android.model.SearchResults;
|
import org.joinmastodon.android.model.SearchResults;
|
||||||
|
|
||||||
public class GetSearchResults extends MastodonAPIRequest<SearchResults>{
|
public class GetSearchResults extends MastodonAPIRequest<SearchResults>{
|
||||||
public GetSearchResults(String query, Type type, boolean resolve, String maxID, int offset, int count){
|
public GetSearchResults(String query, Type type, boolean resolve){
|
||||||
super(HttpMethod.GET, "/search", SearchResults.class);
|
super(HttpMethod.GET, "/search", SearchResults.class);
|
||||||
addQueryParameter("q", query);
|
addQueryParameter("q", query);
|
||||||
if(type!=null)
|
if(type!=null)
|
||||||
addQueryParameter("type", type.name().toLowerCase());
|
addQueryParameter("type", type.name().toLowerCase());
|
||||||
if(resolve)
|
if(resolve)
|
||||||
addQueryParameter("resolve", "true");
|
addQueryParameter("resolve", "true");
|
||||||
if(maxID!=null)
|
|
||||||
addQueryParameter("max_id", maxID);
|
|
||||||
if(offset>0)
|
|
||||||
addQueryParameter("offset", String.valueOf(offset));
|
|
||||||
if(count>0)
|
|
||||||
addQueryParameter("limit", String.valueOf(count));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public GetSearchResults limit(int limit){
|
public GetSearchResults limit(int limit){
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
package org.joinmastodon.android.api.requests.statuses;
|
package org.joinmastodon.android.api.requests.statuses;
|
||||||
|
|
||||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||||
import org.joinmastodon.android.model.Translation;
|
import org.joinmastodon.android.model.TranslatedStatus;
|
||||||
|
|
||||||
import java.util.Map;
|
public class TranslateStatus extends MastodonAPIRequest<TranslatedStatus> {
|
||||||
|
public TranslateStatus(String id) {
|
||||||
public class TranslateStatus extends MastodonAPIRequest<Translation>{
|
super(HttpMethod.POST, "/statuses/"+id+"/translate", TranslatedStatus.class);
|
||||||
public TranslateStatus(String id, String lang){
|
setRequestBody(new Object());
|
||||||
super(HttpMethod.POST, "/statuses/"+id+"/translate", Translation.class);
|
}
|
||||||
setRequestBody(Map.of("lang", lang));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
package org.joinmastodon.android.api.requests.tags;
|
|
||||||
|
|
||||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
|
||||||
import org.joinmastodon.android.model.Hashtag;
|
|
||||||
|
|
||||||
public class GetTag extends MastodonAPIRequest<Hashtag>{
|
|
||||||
public GetTag(String tag){
|
|
||||||
super(HttpMethod.GET, "/tags/"+tag, Hashtag.class);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package org.joinmastodon.android.api.requests.tags;
|
|
||||||
|
|
||||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
|
||||||
import org.joinmastodon.android.model.Hashtag;
|
|
||||||
|
|
||||||
public class SetTagFollowed extends MastodonAPIRequest<Hashtag>{
|
|
||||||
public SetTagFollowed(String tag, boolean followed){
|
|
||||||
super(HttpMethod.POST, "/tags/"+tag+(followed ? "/follow" : "/unfollow"), Hashtag.class);
|
|
||||||
setRequestBody(new Object());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -6,14 +6,9 @@ import static org.joinmastodon.android.api.MastodonAPIController.gson;
|
|||||||
|
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
|
|
||||||
import androidx.annotation.StringRes;
|
|
||||||
|
|
||||||
import com.google.gson.reflect.TypeToken;
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
|
||||||
import org.joinmastodon.android.GlobalUserPreferences;
|
|
||||||
import org.joinmastodon.android.R;
|
|
||||||
import org.joinmastodon.android.model.ContentType;
|
import org.joinmastodon.android.model.ContentType;
|
||||||
import org.joinmastodon.android.model.Emoji;
|
|
||||||
import org.joinmastodon.android.model.TimelineDefinition;
|
import org.joinmastodon.android.model.TimelineDefinition;
|
||||||
|
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
@@ -41,14 +36,12 @@ public class AccountLocalPreferences{
|
|||||||
public String publishButtonText;
|
public String publishButtonText;
|
||||||
public String timelineReplyVisibility; // akkoma-only
|
public String timelineReplyVisibility; // akkoma-only
|
||||||
public boolean keepOnlyLatestNotification;
|
public boolean keepOnlyLatestNotification;
|
||||||
|
|
||||||
public boolean emojiReactionsEnabled;
|
public boolean emojiReactionsEnabled;
|
||||||
public ShowEmojiReactions showEmojiReactions;
|
public ShowEmojiReactions showEmojiReactions;
|
||||||
public ColorPreference color;
|
|
||||||
public ArrayList<Emoji> recentCustomEmoji;
|
|
||||||
|
|
||||||
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();
|
||||||
private final static Type recentCustomEmojiType=new TypeToken<ArrayList<Emoji>>() {}.getType();
|
|
||||||
|
|
||||||
public AccountLocalPreferences(SharedPreferences prefs, AccountSession session){
|
public AccountLocalPreferences(SharedPreferences prefs, AccountSession session){
|
||||||
this.prefs=prefs;
|
this.prefs=prefs;
|
||||||
@@ -73,8 +66,6 @@ public class AccountLocalPreferences{
|
|||||||
keepOnlyLatestNotification=prefs.getBoolean("keepOnlyLatestNotification", false);
|
keepOnlyLatestNotification=prefs.getBoolean("keepOnlyLatestNotification", false);
|
||||||
emojiReactionsEnabled=prefs.getBoolean("emojiReactionsEnabled", session.getInstance().isPresent() && session.getInstance().get().isAkkoma());
|
emojiReactionsEnabled=prefs.getBoolean("emojiReactionsEnabled", session.getInstance().isPresent() && session.getInstance().get().isAkkoma());
|
||||||
showEmojiReactions=ShowEmojiReactions.valueOf(prefs.getString("showEmojiReactions", ShowEmojiReactions.HIDE_EMPTY.name()));
|
showEmojiReactions=ShowEmojiReactions.valueOf(prefs.getString("showEmojiReactions", ShowEmojiReactions.HIDE_EMPTY.name()));
|
||||||
color=prefs.contains("color") ? ColorPreference.valueOf(prefs.getString("color", null)) : null;
|
|
||||||
recentCustomEmoji=fromJson(prefs.getString("recentCustomEmoji", null), recentCustomEmojiType, new ArrayList<>());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getNotificationsPauseEndTime(){
|
public long getNotificationsPauseEndTime(){
|
||||||
@@ -85,10 +76,6 @@ public class AccountLocalPreferences{
|
|||||||
prefs.edit().putLong("notificationsPauseTime", time).apply();
|
prefs.edit().putLong("notificationsPauseTime", time).apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ColorPreference getCurrentColor(){
|
|
||||||
return color!=null ? color : GlobalUserPreferences.color!=null ? GlobalUserPreferences.color : ColorPreference.MATERIAL3;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void save(){
|
public void save(){
|
||||||
prefs.edit()
|
prefs.edit()
|
||||||
.putBoolean("interactionCounts", showInteractionCounts)
|
.putBoolean("interactionCounts", showInteractionCounts)
|
||||||
@@ -112,35 +99,9 @@ public class AccountLocalPreferences{
|
|||||||
.putBoolean("keepOnlyLatestNotification", keepOnlyLatestNotification)
|
.putBoolean("keepOnlyLatestNotification", keepOnlyLatestNotification)
|
||||||
.putBoolean("emojiReactionsEnabled", emojiReactionsEnabled)
|
.putBoolean("emojiReactionsEnabled", emojiReactionsEnabled)
|
||||||
.putString("showEmojiReactions", showEmojiReactions.name())
|
.putString("showEmojiReactions", showEmojiReactions.name())
|
||||||
.putString("color", color!=null ? color.name() : null)
|
|
||||||
.putString("recentCustomEmoji", gson.toJson(recentCustomEmoji))
|
|
||||||
.apply();
|
.apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum ColorPreference{
|
|
||||||
MATERIAL3,
|
|
||||||
PINK,
|
|
||||||
PURPLE,
|
|
||||||
GREEN,
|
|
||||||
BLUE,
|
|
||||||
BROWN,
|
|
||||||
RED,
|
|
||||||
YELLOW;
|
|
||||||
|
|
||||||
public @StringRes int getName() {
|
|
||||||
return switch(this){
|
|
||||||
case MATERIAL3 -> R.string.sk_color_palette_material3;
|
|
||||||
case PINK -> R.string.sk_color_palette_pink;
|
|
||||||
case PURPLE -> R.string.sk_color_palette_purple;
|
|
||||||
case GREEN -> R.string.sk_color_palette_green;
|
|
||||||
case BLUE -> R.string.sk_color_palette_blue;
|
|
||||||
case BROWN -> R.string.sk_color_palette_brown;
|
|
||||||
case RED -> R.string.sk_color_palette_red;
|
|
||||||
case YELLOW -> R.string.sk_color_palette_yellow;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum ShowEmojiReactions{
|
public enum ShowEmojiReactions{
|
||||||
HIDE_EMPTY,
|
HIDE_EMPTY,
|
||||||
ONLY_OPENED,
|
ONLY_OPENED,
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ import java.util.Objects;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
import me.grishka.appkit.api.Callback;
|
import me.grishka.appkit.api.Callback;
|
||||||
import me.grishka.appkit.api.ErrorResponse;
|
import me.grishka.appkit.api.ErrorResponse;
|
||||||
@@ -218,7 +219,7 @@ public class AccountSession{
|
|||||||
|
|
||||||
public void savePreferencesIfPending(){
|
public void savePreferencesIfPending(){
|
||||||
if(preferencesNeedSaving){
|
if(preferencesNeedSaving){
|
||||||
new UpdateAccountCredentialsPreferences(preferences, null, self.discoverable, self.source.indexable)
|
new UpdateAccountCredentialsPreferences(preferences, null, null)
|
||||||
.setCallback(new Callback<>(){
|
.setCallback(new Callback<>(){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Account result){
|
public void onSuccess(Account result){
|
||||||
@@ -254,75 +255,52 @@ public class AccountSession{
|
|||||||
filterStatusContainingObjects(objects, extractor, context, null);
|
filterStatusContainingObjects(objects, extractor, context, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean statusIsOnOwnProfile(Status s, Account profile){
|
|
||||||
return self != null && profile != null && s.account != null
|
|
||||||
&& Objects.equals(self.id, profile.id) && Objects.equals(self.id, s.account.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isFilteredType(Status s){
|
|
||||||
return (!localPreferences.showReplies && s.inReplyToId != null)
|
|
||||||
|| (!localPreferences.showBoosts && s.reblog != null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T> void filterStatusContainingObjects(List<T> objects, Function<T, Status> extractor, FilterContext context, Account profile){
|
public <T> void filterStatusContainingObjects(List<T> objects, Function<T, Status> extractor, FilterContext context, Account profile){
|
||||||
if(!localPreferences.serverSideFiltersSupported) for(T obj:objects){
|
Predicate<Status> statusIsOnOwnProfile = (s) -> self != null && profile != null && s.account != null
|
||||||
|
&& Objects.equals(self.id, profile.id) && Objects.equals(self.id, s.account.id);
|
||||||
|
|
||||||
|
if(getLocalPreferences().serverSideFiltersSupported){
|
||||||
|
// Even with server-side filters, clients are expected to remove statuses that match a filter that hides them
|
||||||
|
objects.removeIf(o->{
|
||||||
|
Status s=extractor.apply(o);
|
||||||
|
if(s==null)
|
||||||
|
return false;
|
||||||
|
if(s.filtered==null)
|
||||||
|
return false;
|
||||||
|
// don't hide own posts in own profile
|
||||||
|
if (statusIsOnOwnProfile.test(s))
|
||||||
|
return false;
|
||||||
|
for(FilterResult filter:s.filtered){
|
||||||
|
if(filter.filter.isActive() && filter.filter.filterAction==FilterAction.HIDE)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(wordFilters==null)
|
||||||
|
return;
|
||||||
|
for(T obj:objects){
|
||||||
Status s=extractor.apply(obj);
|
Status s=extractor.apply(obj);
|
||||||
if(s!=null && s.filtered!=null){
|
if(s!=null && s.filtered!=null){
|
||||||
localPreferences.serverSideFiltersSupported=true;
|
getLocalPreferences().serverSideFiltersSupported=true;
|
||||||
localPreferences.save();
|
getLocalPreferences().save();
|
||||||
break;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
objects.removeIf(o->{
|
||||||
List<T> removeUs=new ArrayList<>();
|
Status s=extractor.apply(o);
|
||||||
for(int i=0; i<objects.size(); i++){
|
if(s==null)
|
||||||
T o=objects.get(i);
|
return false;
|
||||||
if(filterStatusContainingObject(o, extractor, context, profile)){
|
// don't hide own posts in own profile
|
||||||
Status s=extractor.apply(o);
|
if (statusIsOnOwnProfile.test(s))
|
||||||
removeUs.add(o);
|
return false;
|
||||||
if(s!=null && s.hasGapAfter!=null && i>0){
|
for(LegacyFilter filter:wordFilters){
|
||||||
// oops, we're about to remove an item that has a gap after...
|
|
||||||
// gotta find the previous status that's not also about to be removed
|
|
||||||
for(int j=i-1; j>=0; j--){
|
|
||||||
T p=objects.get(j);
|
|
||||||
Status prev=extractor.apply(objects.get(j));
|
|
||||||
if(prev!=null && !removeUs.contains(p)){
|
|
||||||
prev.hasGapAfter=s.hasGapAfter;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
objects.removeAll(removeUs);
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T> boolean filterStatusContainingObject(T object, Function<T, Status> extractor, FilterContext context, Account profile){
|
|
||||||
Status s=extractor.apply(object);
|
|
||||||
if(s==null)
|
|
||||||
return false;
|
|
||||||
// don't hide own posts in own profile
|
|
||||||
if(statusIsOnOwnProfile(s, profile))
|
|
||||||
return false;
|
|
||||||
if(isFilteredType(s) && (context == FilterContext.HOME || context == FilterContext.PUBLIC))
|
|
||||||
return true;
|
|
||||||
// Even with server-side filters, clients are expected to remove statuses that match a filter that hides them
|
|
||||||
if(localPreferences.serverSideFiltersSupported){
|
|
||||||
for(FilterResult filter : s.filtered){
|
|
||||||
if(filter.filter.isActive() && filter.filter.filterAction==FilterAction.HIDE)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}else if(wordFilters!=null){
|
|
||||||
for(LegacyFilter filter : wordFilters){
|
|
||||||
if(filter.context.contains(context) && filter.matches(s) && filter.isActive())
|
if(filter.context.contains(context) && filter.matches(s) && filter.isActive())
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
return false;
|
||||||
return false;
|
});
|
||||||
}
|
|
||||||
|
|
||||||
public void updateAccountInfo(){
|
|
||||||
AccountSessionManager.getInstance().updateSessionLocalInfo(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<Instance> getInstance() {
|
public Optional<Instance> getInstance() {
|
||||||
@@ -335,10 +313,4 @@ public class AccountSession{
|
|||||||
.authority(getInstance().map(i -> i.normalizedUri).orElse(domain))
|
.authority(getInstance().map(i -> i.normalizedUri).orElse(domain))
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getDefaultAvatarUrl() {
|
|
||||||
return getInstance()
|
|
||||||
.map(instance->"https://"+domain+(instance.isAkkoma() ? "/images/avi.png" : "/avatars/original/missing.png"))
|
|
||||||
.orElse("");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -303,7 +303,8 @@ public class AccountSessionManager{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*package*/ void updateSessionLocalInfo(AccountSession session){
|
|
||||||
|
private void updateSessionLocalInfo(AccountSession session){
|
||||||
new GetOwnAccount()
|
new GetOwnAccount()
|
||||||
.setCallback(new Callback<>(){
|
.setCallback(new Callback<>(){
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -9,16 +9,19 @@ import org.joinmastodon.android.R;
|
|||||||
import org.joinmastodon.android.api.requests.accounts.GetAccountStatuses;
|
import org.joinmastodon.android.api.requests.accounts.GetAccountStatuses;
|
||||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||||
import org.joinmastodon.android.events.RemoveAccountPostsEvent;
|
import org.joinmastodon.android.events.RemoveAccountPostsEvent;
|
||||||
|
import org.joinmastodon.android.events.StatusCreatedEvent;
|
||||||
import org.joinmastodon.android.events.StatusUnpinnedEvent;
|
import org.joinmastodon.android.events.StatusUnpinnedEvent;
|
||||||
import org.joinmastodon.android.model.Account;
|
import org.joinmastodon.android.model.Account;
|
||||||
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.HeaderStatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.HeaderStatusDisplayItem;
|
||||||
|
import org.joinmastodon.android.utils.StatusFilterPredicate;
|
||||||
import org.parceler.Parcels;
|
import org.parceler.Parcels;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import me.grishka.appkit.api.SimpleCallback;
|
import me.grishka.appkit.api.SimpleCallback;
|
||||||
|
|
||||||
@@ -52,14 +55,15 @@ public class AccountTimelineFragment extends StatusListFragment{
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doLoadData(int offset, int count){
|
protected void doLoadData(int offset, int count){
|
||||||
currentRequest=new GetAccountStatuses(user.id, getMaxID(), null, count, filter)
|
currentRequest=new GetAccountStatuses(user.id, offset>0 ? getMaxID() : null, null, count, filter)
|
||||||
.setCallback(new SimpleCallback<>(this){
|
.setCallback(new SimpleCallback<>(this){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<Status> result){
|
public void onSuccess(List<Status> result){
|
||||||
if(getActivity()==null) return;
|
if(getActivity()==null) return;
|
||||||
boolean more=applyMaxID(result);
|
AccountSessionManager asm = AccountSessionManager.getInstance();
|
||||||
|
boolean empty=result.isEmpty();
|
||||||
AccountSessionManager.get(accountID).filterStatuses(result, getFilterContext(), user);
|
AccountSessionManager.get(accountID).filterStatuses(result, getFilterContext(), user);
|
||||||
onDataLoaded(result, more);
|
onDataLoaded(result, !empty);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.exec(accountID);
|
.exec(accountID);
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ public class AnnouncementsFragment extends BaseStatusListFragment<Announcement>
|
|||||||
.setCallback(new SimpleCallback<>(this){
|
.setCallback(new SimpleCallback<>(this){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<Announcement> result){
|
public void onSuccess(List<Announcement> result){
|
||||||
if(getActivity()==null) return;
|
if (getActivity() == null) return;
|
||||||
|
|
||||||
// get unread items first
|
// get unread items first
|
||||||
List<Announcement> data = result.stream().filter(a -> !a.read).collect(toList());
|
List<Announcement> data = result.stream().filter(a -> !a.read).collect(toList());
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import android.graphics.Rect;
|
|||||||
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.util.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.WindowInsets;
|
import android.view.WindowInsets;
|
||||||
@@ -21,7 +22,6 @@ import org.joinmastodon.android.GlobalUserPreferences;
|
|||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.requests.accounts.GetAccountRelationships;
|
import org.joinmastodon.android.api.requests.accounts.GetAccountRelationships;
|
||||||
import org.joinmastodon.android.api.requests.polls.SubmitPollVote;
|
import org.joinmastodon.android.api.requests.polls.SubmitPollVote;
|
||||||
import org.joinmastodon.android.api.requests.statuses.TranslateStatus;
|
|
||||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||||
import org.joinmastodon.android.events.PollUpdatedEvent;
|
import org.joinmastodon.android.events.PollUpdatedEvent;
|
||||||
import org.joinmastodon.android.model.Account;
|
import org.joinmastodon.android.model.Account;
|
||||||
@@ -29,10 +29,9 @@ import org.joinmastodon.android.model.DisplayItemsParent;
|
|||||||
import org.joinmastodon.android.model.Poll;
|
import org.joinmastodon.android.model.Poll;
|
||||||
import org.joinmastodon.android.model.Relationship;
|
import org.joinmastodon.android.model.Relationship;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
import org.joinmastodon.android.model.Translation;
|
|
||||||
import org.joinmastodon.android.ui.BetterItemAnimator;
|
import org.joinmastodon.android.ui.BetterItemAnimator;
|
||||||
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
|
|
||||||
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;
|
||||||
@@ -56,7 +55,6 @@ import java.util.ArrayList;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@@ -88,7 +86,6 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
|||||||
protected Rect tmpRect=new Rect();
|
protected Rect tmpRect=new Rect();
|
||||||
protected TypedObjectPool<MediaGridStatusDisplayItem.GridItemType, MediaAttachmentViewController> attachmentViewsPool=new TypedObjectPool<>(this::makeNewMediaAttachmentView);
|
protected TypedObjectPool<MediaGridStatusDisplayItem.GridItemType, MediaAttachmentViewController> attachmentViewsPool=new TypedObjectPool<>(this::makeNewMediaAttachmentView);
|
||||||
protected boolean currentlyScrolling;
|
protected boolean currentlyScrolling;
|
||||||
protected String maxID;
|
|
||||||
|
|
||||||
public BaseStatusListFragment(){
|
public BaseStatusListFragment(){
|
||||||
super(20);
|
super(20);
|
||||||
@@ -155,8 +152,6 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected String getMaxID(){
|
protected String getMaxID(){
|
||||||
if(refreshing) return null;
|
|
||||||
if(maxID!=null) return maxID;
|
|
||||||
if(!preloadedData.isEmpty())
|
if(!preloadedData.isEmpty())
|
||||||
return preloadedData.get(preloadedData.size()-1).getID();
|
return preloadedData.get(preloadedData.size()-1).getID();
|
||||||
else if(!data.isEmpty())
|
else if(!data.isEmpty())
|
||||||
@@ -165,12 +160,6 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean applyMaxID(List<Status> result){
|
|
||||||
boolean empty=result.isEmpty();
|
|
||||||
if(!empty) maxID=result.get(result.size()-1).id;
|
|
||||||
return !empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract List<StatusDisplayItem> buildDisplayItems(T s);
|
protected abstract List<StatusDisplayItem> buildDisplayItems(T s);
|
||||||
protected abstract void addAccountToKnown(T s);
|
protected abstract void addAccountToKnown(T s);
|
||||||
|
|
||||||
@@ -557,21 +546,21 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
|||||||
|
|
||||||
public void onVisibilityIconClick(HeaderStatusDisplayItem.Holder holder) {
|
public void onVisibilityIconClick(HeaderStatusDisplayItem.Holder holder) {
|
||||||
Status status = holder.getItem().status;
|
Status status = holder.getItem().status;
|
||||||
if(holder.getItem().hasVisibilityToggle) holder.animateVisibilityToggle(false);
|
MediaGridStatusDisplayItem.Holder mediaGrid = findHolderOfType(holder.getItemID(), MediaGridStatusDisplayItem.Holder.class);
|
||||||
MediaGridStatusDisplayItem.Holder mediaGrid=findHolderOfType(holder.getItemID(), MediaGridStatusDisplayItem.Holder.class);
|
if (mediaGrid != null) {
|
||||||
if(mediaGrid!=null){
|
if (!status.sensitiveRevealed) mediaGrid.revealSensitive();
|
||||||
if(!status.sensitiveRevealed) mediaGrid.revealSensitive();
|
|
||||||
else mediaGrid.hideSensitive();
|
else mediaGrid.hideSensitive();
|
||||||
}else{
|
} else {
|
||||||
status.sensitiveRevealed=false;
|
// media grid's methods normally change the status' state - we still want to be able
|
||||||
notifyItemChangedAfter(holder.getItem(), MediaGridStatusDisplayItem.class);
|
// to do this if the media grid is not bound, tho - so, doing it ourselves here
|
||||||
|
status.sensitiveRevealed = !status.sensitiveRevealed;
|
||||||
}
|
}
|
||||||
|
holder.rebind();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onSensitiveRevealed(MediaGridStatusDisplayItem.Holder holder) {
|
public void onSensitiveRevealed(MediaGridStatusDisplayItem.Holder holder) {
|
||||||
HeaderStatusDisplayItem.Holder header=findHolderOfType(holder.getItemID(), HeaderStatusDisplayItem.Holder.class);
|
HeaderStatusDisplayItem.Holder header = findHolderOfType(holder.getItemID(), HeaderStatusDisplayItem.Holder.class);
|
||||||
if(header!=null && header.getItem().hasVisibilityToggle) header.animateVisibilityToggle(true);
|
if(header != null) header.rebind();
|
||||||
else notifyItemChangedBefore(holder.getItem(), HeaderStatusDisplayItem.class);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void toggleSpoiler(Status status, String itemID){
|
protected void toggleSpoiler(Status status, String itemID){
|
||||||
@@ -580,8 +569,8 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
|||||||
status.sensitiveRevealed = false;
|
status.sensitiveRevealed = false;
|
||||||
|
|
||||||
SpoilerStatusDisplayItem.Holder spoiler=findHolderOfType(itemID, SpoilerStatusDisplayItem.Holder.class);
|
SpoilerStatusDisplayItem.Holder spoiler=findHolderOfType(itemID, SpoilerStatusDisplayItem.Holder.class);
|
||||||
if(spoiler!=null) spoiler.rebind();
|
if(spoiler!=null)
|
||||||
else notifyItemChanged(itemID, SpoilerStatusDisplayItem.class);
|
spoiler.rebind();
|
||||||
SpoilerStatusDisplayItem spoilerItem=Objects.requireNonNull(findItemOfType(itemID, SpoilerStatusDisplayItem.class));
|
SpoilerStatusDisplayItem spoilerItem=Objects.requireNonNull(findItemOfType(itemID, SpoilerStatusDisplayItem.class));
|
||||||
|
|
||||||
int index=displayItems.indexOf(spoilerItem);
|
int index=displayItems.indexOf(spoilerItem);
|
||||||
@@ -593,32 +582,42 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
|||||||
adapter.notifyItemRangeRemoved(index+1, spoilerItem.contentItems.size());
|
adapter.notifyItemRangeRemoved(index+1, spoilerItem.contentItems.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
notifyItemChanged(itemID, TextStatusDisplayItem.class);
|
TextStatusDisplayItem.Holder text=findHolderOfType(itemID, TextStatusDisplayItem.Holder.class);
|
||||||
|
if(text!=null)
|
||||||
|
adapter.notifyItemChanged(text.getAbsoluteAdapterPosition()-getMainAdapterOffset());
|
||||||
HeaderStatusDisplayItem.Holder header=findHolderOfType(itemID, HeaderStatusDisplayItem.Holder.class);
|
HeaderStatusDisplayItem.Holder header=findHolderOfType(itemID, HeaderStatusDisplayItem.Holder.class);
|
||||||
if(header!=null) header.rebind();
|
if(header!=null)
|
||||||
else notifyItemChanged(itemID, HeaderStatusDisplayItem.class);
|
header.rebind();
|
||||||
|
|
||||||
list.invalidateItemDecorations();
|
list.invalidateItemDecorations();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onEnableExpandable(TextStatusDisplayItem.Holder holder, boolean expandable) {
|
public void onEnableExpandable(TextStatusDisplayItem.Holder holder, boolean expandable) {
|
||||||
Status s=holder.getItem().status;
|
if (holder.getItem().status.textExpandable != expandable && list != null) {
|
||||||
if(s.textExpandable!=expandable && list!=null) {
|
holder.getItem().status.textExpandable = expandable;
|
||||||
s.textExpandable=expandable;
|
HeaderStatusDisplayItem.Holder header = findHolderOfType(holder.getItemID(), HeaderStatusDisplayItem.Holder.class);
|
||||||
HeaderStatusDisplayItem.Holder header=findHolderOfType(holder.getItemID(), HeaderStatusDisplayItem.Holder.class);
|
if (header != null) header.rebind();
|
||||||
if(header!=null) header.bindCollapseButton();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onToggleExpanded(Status status, String itemID) {
|
public void onToggleExpanded(Status status, String itemID) {
|
||||||
status.textExpanded = !status.textExpanded;
|
status.textExpanded = !status.textExpanded;
|
||||||
notifyItemChanged(itemID, TextStatusDisplayItem.class);
|
TextStatusDisplayItem.Holder text=findHolderOfType(itemID, TextStatusDisplayItem.Holder.class);
|
||||||
HeaderStatusDisplayItem.Holder header=findHolderOfType(itemID, HeaderStatusDisplayItem.Holder.class);
|
HeaderStatusDisplayItem.Holder header=findHolderOfType(itemID, HeaderStatusDisplayItem.Holder.class);
|
||||||
if(header!=null) header.animateExpandToggle();
|
if (text != null) text.rebind();
|
||||||
else notifyItemChanged(itemID, HeaderStatusDisplayItem.class);
|
if (header != null) header.rebind();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onGapClick(GapStatusDisplayItem.Holder item, boolean downwards){}
|
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 onWarningClick(WarningFilteredStatusDisplayItem.Holder warning){
|
public void onWarningClick(WarningFilteredStatusDisplayItem.Holder warning){
|
||||||
int startPos = warning.getAbsoluteAdapterPosition();
|
int startPos = warning.getAbsoluteAdapterPosition();
|
||||||
@@ -674,61 +673,9 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Use this as a fallback if findHolderOfType fails to find the ViewHolder.
|
|
||||||
* It might still be bound but off-screen and therefore not a child of the RecyclerView -
|
|
||||||
* resulting in the ViewHolder displaying an outdated state once scrolled back into view.
|
|
||||||
*/
|
|
||||||
protected <I extends StatusDisplayItem> int notifyItemChanged(String id, Class<I> type){
|
|
||||||
boolean encounteredParent=false;
|
|
||||||
for(int i=0; i<displayItems.size(); i++){
|
|
||||||
StatusDisplayItem item=displayItems.get(i);
|
|
||||||
boolean idEquals=id.equals(item.parentID);
|
|
||||||
if(!encounteredParent && idEquals) encounteredParent=true; // reached top of the parent
|
|
||||||
else if(encounteredParent && !idEquals) break; // passed by bottom of the parent. man muss ja wissen wann schluss is
|
|
||||||
if(idEquals && type.isInstance(item)){
|
|
||||||
adapter.notifyItemChanged(i);
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected <I extends StatusDisplayItem> int notifyItemChangedAfter(StatusDisplayItem afterThis, Class<I> type){
|
|
||||||
int startIndex=displayItems.indexOf(afterThis);
|
|
||||||
if(startIndex == -1) throw new IllegalStateException("notifyItemChangedAfter didn't find the passed StatusDisplayItem");
|
|
||||||
String parentID=afterThis.parentID;
|
|
||||||
for(int i=startIndex; i<displayItems.size(); i++){
|
|
||||||
StatusDisplayItem item=displayItems.get(i);
|
|
||||||
if(!parentID.equals(item.parentID)) break; // didn't find anything
|
|
||||||
if(type.isInstance(item)){
|
|
||||||
// found it
|
|
||||||
adapter.notifyItemChanged(i);
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected <I extends StatusDisplayItem> int notifyItemChangedBefore(StatusDisplayItem beforeThis, Class<I> type){
|
|
||||||
int startIndex=displayItems.indexOf(beforeThis);
|
|
||||||
if(startIndex == -1) throw new IllegalStateException("notifyItemChangedBefore didn't find the passed StatusDisplayItem");
|
|
||||||
String parentID=beforeThis.parentID;
|
|
||||||
for(int i=startIndex; i>=0; i--){
|
|
||||||
StatusDisplayItem item=displayItems.get(i);
|
|
||||||
if(!parentID.equals(item.parentID)) break; // didn't find anything
|
|
||||||
if(type.isInstance(item)){
|
|
||||||
// found it
|
|
||||||
adapter.notifyItemChanged(i);
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
protected <I extends StatusDisplayItem, H extends StatusDisplayItem.Holder<I>> H findHolderOfType(String id, Class<H> type){
|
protected <I extends StatusDisplayItem, H extends StatusDisplayItem.Holder<I>> H findHolderOfType(String id, Class<H> type){
|
||||||
for(int i=0; i<list.getChildCount(); i++){
|
for(int i=0;i<list.getChildCount();i++){
|
||||||
RecyclerView.ViewHolder holder=list.getChildViewHolder(list.getChildAt(i));
|
RecyclerView.ViewHolder holder=list.getChildViewHolder(list.getChildAt(i));
|
||||||
if(holder instanceof StatusDisplayItem.Holder<?> itemHolder && itemHolder.getItemID().equals(id) && type.isInstance(holder))
|
if(holder instanceof StatusDisplayItem.Holder<?> itemHolder && itemHolder.getItemID().equals(id) && type.isInstance(holder))
|
||||||
return type.cast(holder);
|
return type.cast(holder);
|
||||||
@@ -825,67 +772,6 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
|||||||
assistContent.setWebUri(getWebUri(getSession().getInstanceUri().buildUpon()));
|
assistContent.setWebUri(getWebUri(getSession().getInstanceUri().buildUpon()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void togglePostTranslation(Status status, String itemID){
|
|
||||||
switch(status.translationState){
|
|
||||||
case LOADING -> {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
case SHOWN -> {
|
|
||||||
status.translationState=Status.TranslationState.HIDDEN;
|
|
||||||
}
|
|
||||||
case HIDDEN -> {
|
|
||||||
if(status.translation!=null){
|
|
||||||
status.translationState=Status.TranslationState.SHOWN;
|
|
||||||
}else{
|
|
||||||
status.translationState=Status.TranslationState.LOADING;
|
|
||||||
new TranslateStatus(status.getContentStatus().id, Locale.getDefault().getLanguage())
|
|
||||||
.setCallback(new Callback<>(){
|
|
||||||
@Override
|
|
||||||
public void onSuccess(Translation result){
|
|
||||||
if(getActivity()==null)
|
|
||||||
return;
|
|
||||||
status.translation=result;
|
|
||||||
status.translationState=Status.TranslationState.SHOWN;
|
|
||||||
TextStatusDisplayItem.Holder text=findHolderOfType(itemID, TextStatusDisplayItem.Holder.class);
|
|
||||||
if(text!=null){
|
|
||||||
text.updateTranslation(true);
|
|
||||||
imgLoader.bindViewHolder((ImageLoaderRecyclerAdapter) list.getAdapter(), text, text.getAbsoluteAdapterPosition());
|
|
||||||
}else{
|
|
||||||
notifyItemChanged(itemID, TextStatusDisplayItem.class);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onError(ErrorResponse error){
|
|
||||||
if(getActivity()==null)
|
|
||||||
return;
|
|
||||||
status.translationState=Status.TranslationState.HIDDEN;
|
|
||||||
TextStatusDisplayItem.Holder text=findHolderOfType(itemID, TextStatusDisplayItem.Holder.class);
|
|
||||||
if(text!=null){
|
|
||||||
text.updateTranslation(true);
|
|
||||||
}else{
|
|
||||||
notifyItemChanged(itemID, TextStatusDisplayItem.class);
|
|
||||||
}
|
|
||||||
new M3AlertDialogBuilder(getActivity())
|
|
||||||
.setTitle(R.string.error)
|
|
||||||
.setMessage(R.string.translation_failed)
|
|
||||||
.setPositiveButton(R.string.ok, null)
|
|
||||||
.show();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.exec(accountID);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TextStatusDisplayItem.Holder text=findHolderOfType(itemID, TextStatusDisplayItem.Holder.class);
|
|
||||||
if(text!=null){
|
|
||||||
text.updateTranslation(true);
|
|
||||||
imgLoader.bindViewHolder((ImageLoaderRecyclerAdapter) list.getAdapter(), text, text.getAbsoluteAdapterPosition());
|
|
||||||
}else{
|
|
||||||
notifyItemChanged(itemID, TextStatusDisplayItem.class);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void rebuildAllDisplayItems(){
|
public void rebuildAllDisplayItems(){
|
||||||
displayItems.clear();
|
displayItems.clear();
|
||||||
for(T item:data){
|
for(T item:data){
|
||||||
@@ -901,7 +787,7 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
|
|||||||
if(getContext()==null) return;
|
if(getContext()==null) return;
|
||||||
super.onDataLoaded(d, more);
|
super.onDataLoaded(d, more);
|
||||||
// more available, but the page isn't even full yet? seems wrong, let's load some more
|
// more available, but the page isn't even full yet? seems wrong, let's load some more
|
||||||
if(more && data.size() < itemsPerPage){
|
if(more && d.size() < itemsPerPage){
|
||||||
preloader.onScrolledToLastItem();
|
preloader.onScrolledToLastItem();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ public class BookmarkedStatusListFragment extends StatusListFragment{
|
|||||||
.setCallback(new SimpleCallback<>(this){
|
.setCallback(new SimpleCallback<>(this){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(HeaderPaginationList<Status> result){
|
public void onSuccess(HeaderPaginationList<Status> result){
|
||||||
if(getActivity()==null) return;
|
if (getActivity() == null) return;
|
||||||
if(result.nextPageUri!=null)
|
if(result.nextPageUri!=null)
|
||||||
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
|
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ import android.text.TextWatcher;
|
|||||||
import android.text.format.DateFormat;
|
import android.text.format.DateFormat;
|
||||||
import android.text.style.BackgroundColorSpan;
|
import android.text.style.BackgroundColorSpan;
|
||||||
import android.text.style.ForegroundColorSpan;
|
import android.text.style.ForegroundColorSpan;
|
||||||
import android.view.HapticFeedbackConstants;
|
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
@@ -162,7 +161,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
private int charCount, charLimit, trimmedCharCount;
|
private int charCount, charLimit, trimmedCharCount;
|
||||||
|
|
||||||
private Button publishButton, languageButton, scheduleTimeBtn;
|
private Button publishButton, languageButton, scheduleTimeBtn;
|
||||||
private PopupMenu contentTypePopup, visibilityPopup, draftOptionsPopup;
|
private PopupMenu languagePopup, contentTypePopup, visibilityPopup, draftOptionsPopup;
|
||||||
private ImageButton mediaBtn, pollBtn, emojiBtn, spoilerBtn, draftsBtn, scheduleDraftDismiss, contentTypeBtn;
|
private ImageButton mediaBtn, pollBtn, emojiBtn, spoilerBtn, draftsBtn, scheduleDraftDismiss, contentTypeBtn;
|
||||||
private View sensitiveBtn;
|
private View sensitiveBtn;
|
||||||
private TextView replyText;
|
private TextView replyText;
|
||||||
@@ -294,7 +293,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
@Override
|
@Override
|
||||||
public View onCreateContentView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
|
public View onCreateContentView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
|
||||||
creatingView=true;
|
creatingView=true;
|
||||||
emojiKeyboard=new CustomEmojiPopupKeyboard(getActivity(), accountID, customEmojis, instanceDomain);
|
emojiKeyboard=new CustomEmojiPopupKeyboard(getActivity(), customEmojis, instanceDomain);
|
||||||
emojiKeyboard.setListener(new CustomEmojiPopupKeyboard.Listener(){
|
emojiKeyboard.setListener(new CustomEmojiPopupKeyboard.Listener(){
|
||||||
@Override
|
@Override
|
||||||
public void onEmojiSelected(Emoji emoji){
|
public void onEmojiSelected(Emoji emoji){
|
||||||
@@ -827,14 +826,6 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
publishButton = wrap.findViewById(R.id.publish_btn);
|
publishButton = wrap.findViewById(R.id.publish_btn);
|
||||||
languageButton = wrap.findViewById(R.id.language_btn);
|
languageButton = wrap.findViewById(R.id.language_btn);
|
||||||
languageButton.setOnClickListener(v->showLanguageAlert());
|
languageButton.setOnClickListener(v->showLanguageAlert());
|
||||||
languageButton.setOnLongClickListener(v->{
|
|
||||||
languageButton.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
|
|
||||||
if(!getLocalPrefs().bottomEncoding){
|
|
||||||
getLocalPrefs().bottomEncoding=true;
|
|
||||||
getLocalPrefs().save();
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
|
|
||||||
publishButton.setOnClickListener(v -> {
|
publishButton.setOnClickListener(v -> {
|
||||||
if(GlobalUserPreferences.altTextReminders && editingStatus==null)
|
if(GlobalUserPreferences.altTextReminders && editingStatus==null)
|
||||||
@@ -1317,8 +1308,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
boolean usePhotoPicker=photoPicker && UiUtils.isPhotoPickerAvailable();
|
boolean usePhotoPicker=photoPicker && UiUtils.isPhotoPickerAvailable();
|
||||||
if(usePhotoPicker){
|
if(usePhotoPicker){
|
||||||
intent=new Intent(MediaStore.ACTION_PICK_IMAGES);
|
intent=new Intent(MediaStore.ACTION_PICK_IMAGES);
|
||||||
if(mediaViewController.getMaxAttachments()-mediaViewController.getMediaAttachmentsCount()>1)
|
intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, mediaViewController.getMaxAttachments()-mediaViewController.getMediaAttachmentsCount());
|
||||||
intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, mediaViewController.getMaxAttachments()-mediaViewController.getMediaAttachmentsCount());
|
|
||||||
}else{
|
}else{
|
||||||
intent=new Intent(Intent.ACTION_GET_CONTENT);
|
intent=new Intent(Intent.ACTION_GET_CONTENT);
|
||||||
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||||
|
|||||||
@@ -26,12 +26,12 @@ import android.widget.ImageView;
|
|||||||
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.MastodonAPIController;
|
import org.joinmastodon.android.api.MastodonAPIController;
|
||||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
|
||||||
import org.joinmastodon.android.model.Attachment;
|
import org.joinmastodon.android.model.Attachment;
|
||||||
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
|
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
|
||||||
import org.joinmastodon.android.ui.photoviewer.PhotoViewer;
|
import org.joinmastodon.android.ui.photoviewer.PhotoViewer;
|
||||||
import org.joinmastodon.android.ui.utils.ColorPalette;
|
import org.joinmastodon.android.ui.utils.ColorPalette;
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
|
import org.joinmastodon.android.ui.views.FixedAspectRatioImageView;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
||||||
@@ -54,17 +54,16 @@ public class ComposeImageDescriptionFragment extends MastodonToolbarFragment imp
|
|||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState){
|
public void onCreate(Bundle savedInstanceState){
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
accountID=getArguments().getString("account");
|
||||||
|
attachmentID=getArguments().getString("attachment");
|
||||||
setHasOptionsMenu(true);
|
setHasOptionsMenu(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAttach(Activity activity){
|
public void onAttach(Activity activity){
|
||||||
super.onAttach(activity);
|
super.onAttach(activity);
|
||||||
accountID=getArguments().getString("account");
|
|
||||||
attachmentID=getArguments().getString("attachment");
|
|
||||||
themeWrapper=new ContextThemeWrapper(activity, R.style.Theme_Mastodon_Dark);
|
themeWrapper=new ContextThemeWrapper(activity, R.style.Theme_Mastodon_Dark);
|
||||||
ColorPalette.palettes.get(AccountSessionManager.get(accountID).getLocalPreferences().getCurrentColor())
|
ColorPalette.palettes.get(GlobalUserPreferences.color).apply(themeWrapper, GlobalUserPreferences.ThemePreference.DARK);
|
||||||
.apply(themeWrapper, GlobalUserPreferences.ThemePreference.DARK);
|
|
||||||
setTitle(R.string.add_alt_text);
|
setTitle(R.string.add_alt_text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -169,7 +169,7 @@ public class EditTimelinesFragment extends MastodonRecyclerFragment<TimelineDefi
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateOptionsMenu() {
|
private void updateOptionsMenu() {
|
||||||
if(getActivity()==null) return;
|
if (getActivity() == null) return;
|
||||||
optionsMenu.clear();
|
optionsMenu.clear();
|
||||||
timelineByMenuItem.clear();
|
timelineByMenuItem.clear();
|
||||||
|
|
||||||
@@ -239,21 +239,16 @@ public class EditTimelinesFragment extends MastodonRecyclerFragment<TimelineDefi
|
|||||||
|
|
||||||
private boolean setTagListContent(NachoTextView editText, @Nullable List<String> tags) {
|
private boolean setTagListContent(NachoTextView editText, @Nullable List<String> tags) {
|
||||||
if (tags == null || tags.isEmpty()) return false;
|
if (tags == null || tags.isEmpty()) return false;
|
||||||
editText.setText(tags);
|
editText.setText(String.join(",", tags));
|
||||||
editText.chipifyAllUnterminatedTokens();
|
editText.chipifyAllUnterminatedTokens();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private NachoTextView prepareChipTextView(NachoTextView nacho) {
|
private NachoTextView prepareChipTextView(NachoTextView nacho) {
|
||||||
//I’ll Be Back
|
nacho.addChipTerminator(',', BEHAVIOR_CHIPIFY_ALL);
|
||||||
nacho.setChipTerminators(
|
nacho.addChipTerminator('\n', BEHAVIOR_CHIPIFY_ALL);
|
||||||
Map.of(
|
nacho.addChipTerminator(' ', BEHAVIOR_CHIPIFY_ALL);
|
||||||
',', BEHAVIOR_CHIPIFY_ALL,
|
nacho.addChipTerminator(';', BEHAVIOR_CHIPIFY_ALL);
|
||||||
'\n', BEHAVIOR_CHIPIFY_ALL,
|
|
||||||
' ', BEHAVIOR_CHIPIFY_ALL,
|
|
||||||
';', BEHAVIOR_CHIPIFY_ALL
|
|
||||||
)
|
|
||||||
);
|
|
||||||
nacho.enableEditChipOnTouch(true, true);
|
nacho.enableEditChipOnTouch(true, true);
|
||||||
nacho.setOnFocusChangeListener((v, hasFocus) -> nacho.chipifyAllUnterminatedTokens());
|
nacho.setOnFocusChangeListener((v, hasFocus) -> nacho.chipifyAllUnterminatedTokens());
|
||||||
return nacho;
|
return nacho;
|
||||||
@@ -349,7 +344,7 @@ public class EditTimelinesFragment extends MastodonRecyclerFragment<TimelineDefi
|
|||||||
mainHashtag = name;
|
mainHashtag = name;
|
||||||
name = null;
|
name = null;
|
||||||
}
|
}
|
||||||
if (TextUtils.isEmpty(mainHashtag) && (item != null && item.getType() == TimelineDefinition.TimelineType.HASHTAG)) {
|
if (TextUtils.isEmpty(mainHashtag)) {
|
||||||
Toast.makeText(ctx, R.string.sk_add_timeline_tag_error_empty, Toast.LENGTH_SHORT).show();
|
Toast.makeText(ctx, R.string.sk_add_timeline_tag_error_empty, Toast.LENGTH_SHORT).show();
|
||||||
onSave.accept(null);
|
onSave.accept(null);
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ public class FavoritedStatusListFragment extends StatusListFragment{
|
|||||||
.setCallback(new SimpleCallback<>(this){
|
.setCallback(new SimpleCallback<>(this){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(HeaderPaginationList<Status> result){
|
public void onSuccess(HeaderPaginationList<Status> result){
|
||||||
if(getActivity()==null) return;
|
if (getActivity() == null) return;
|
||||||
if(result.nextPageUri!=null)
|
if(result.nextPageUri!=null)
|
||||||
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
|
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -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, Objects.requireNonNull(findItemOfType(id, HashtagStatusDisplayItem.class)).tag);
|
UiUtils.openHashtagTimeline(getActivity(), accountID, hashtag, data.stream().filter(h -> Objects.equals(h.name, hashtag)).findAny().map(h -> h.following).orElse(null));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -17,10 +17,8 @@ import android.widget.TextView;
|
|||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.requests.accounts.GetAccountRelationships;
|
import org.joinmastodon.android.api.requests.accounts.GetAccountRelationships;
|
||||||
import org.joinmastodon.android.api.requests.accounts.GetFollowRequests;
|
import org.joinmastodon.android.api.requests.accounts.GetFollowRequests;
|
||||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
|
||||||
import org.joinmastodon.android.model.Account;
|
import org.joinmastodon.android.model.Account;
|
||||||
import org.joinmastodon.android.model.HeaderPaginationList;
|
import org.joinmastodon.android.model.HeaderPaginationList;
|
||||||
import org.joinmastodon.android.model.Instance;
|
|
||||||
import org.joinmastodon.android.model.Relationship;
|
import org.joinmastodon.android.model.Relationship;
|
||||||
import org.joinmastodon.android.ui.OutlineProviders;
|
import org.joinmastodon.android.ui.OutlineProviders;
|
||||||
import org.joinmastodon.android.ui.text.HtmlParser;
|
import org.joinmastodon.android.ui.text.HtmlParser;
|
||||||
@@ -83,7 +81,7 @@ public class FollowRequestsListFragment extends MastodonRecyclerFragment<FollowR
|
|||||||
.setCallback(new SimpleCallback<>(this){
|
.setCallback(new SimpleCallback<>(this){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(HeaderPaginationList<Account> result){
|
public void onSuccess(HeaderPaginationList<Account> result){
|
||||||
if(getActivity()==null) return;
|
if (getActivity() == null) return;
|
||||||
if(result.nextPageUri!=null)
|
if(result.nextPageUri!=null)
|
||||||
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
|
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
|
||||||
else
|
else
|
||||||
@@ -359,9 +357,8 @@ public class FollowRequestsListFragment extends MastodonRecyclerFragment<FollowR
|
|||||||
|
|
||||||
public AccountWrapper(Account account){
|
public AccountWrapper(Account account){
|
||||||
this.account=account;
|
this.account=account;
|
||||||
avaRequest=new UrlImageLoaderRequest(
|
if(!TextUtils.isEmpty(account.avatar))
|
||||||
TextUtils.isEmpty(account.avatar) ? AccountSessionManager.get(getAccountID()).getDefaultAvatarUrl() : account.avatar,
|
avaRequest=new UrlImageLoaderRequest(account.avatar, V.dp(50), V.dp(50));
|
||||||
V.dp(50), V.dp(50));
|
|
||||||
if(!TextUtils.isEmpty(account.header))
|
if(!TextUtils.isEmpty(account.header))
|
||||||
coverRequest=new UrlImageLoaderRequest(account.header, 1000, 1000);
|
coverRequest=new UrlImageLoaderRequest(account.header, 1000, 1000);
|
||||||
parsedBio=HtmlParser.parse(account.note, account.emojis, Collections.emptyList(), Collections.emptyList(), accountID);
|
parsedBio=HtmlParser.parse(account.note, account.emojis, Collections.emptyList(), Collections.emptyList(), accountID);
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ public class FollowedHashtagsFragment extends MastodonRecyclerFragment<Hashtag>
|
|||||||
.setCallback(new SimpleCallback<>(this){
|
.setCallback(new SimpleCallback<>(this){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(HeaderPaginationList<Hashtag> result){
|
public void onSuccess(HeaderPaginationList<Hashtag> result){
|
||||||
if(getActivity()==null) return;
|
if (getActivity() == null) return;
|
||||||
if(result.nextPageUri!=null)
|
if(result.nextPageUri!=null)
|
||||||
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
|
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
|
||||||
else
|
else
|
||||||
@@ -121,7 +121,7 @@ public class FollowedHashtagsFragment extends MastodonRecyclerFragment<Hashtag>
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClick() {
|
public void onClick() {
|
||||||
UiUtils.openHashtagTimeline(getActivity(), accountID, item.name);
|
UiUtils.openHashtagTimeline(getActivity(), accountID, item.name, item.following);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,65 +1,46 @@
|
|||||||
package org.joinmastodon.android.fragments;
|
package org.joinmastodon.android.fragments;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.res.TypedArray;
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.SpannableStringBuilder;
|
|
||||||
import android.view.HapticFeedbackConstants;
|
import android.view.HapticFeedbackConstants;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.ImageButton;
|
|
||||||
import android.widget.ProgressBar;
|
|
||||||
import android.widget.TextView;
|
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.E;
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.MastodonErrorResponse;
|
import org.joinmastodon.android.api.requests.tags.GetHashtag;
|
||||||
import org.joinmastodon.android.api.requests.tags.GetTag;
|
import org.joinmastodon.android.api.requests.tags.SetHashtagFollowed;
|
||||||
import org.joinmastodon.android.api.requests.tags.SetTagFollowed;
|
|
||||||
import org.joinmastodon.android.api.requests.timelines.GetHashtagTimeline;
|
import org.joinmastodon.android.api.requests.timelines.GetHashtagTimeline;
|
||||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
import org.joinmastodon.android.events.HashtagUpdatedEvent;
|
||||||
import org.joinmastodon.android.model.FilterContext;
|
import org.joinmastodon.android.model.FilterContext;
|
||||||
import org.joinmastodon.android.model.Hashtag;
|
import org.joinmastodon.android.model.Hashtag;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
import org.joinmastodon.android.model.TimelineDefinition;
|
import org.joinmastodon.android.model.TimelineDefinition;
|
||||||
import org.joinmastodon.android.ui.text.SpacerSpan;
|
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
import org.joinmastodon.android.ui.views.ProgressBarButton;
|
import org.joinmastodon.android.utils.StatusFilterPredicate;
|
||||||
import org.parceler.Parcels;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
|
||||||
import me.grishka.appkit.Nav;
|
import me.grishka.appkit.Nav;
|
||||||
import me.grishka.appkit.api.Callback;
|
import me.grishka.appkit.api.Callback;
|
||||||
import me.grishka.appkit.api.ErrorResponse;
|
import me.grishka.appkit.api.ErrorResponse;
|
||||||
import me.grishka.appkit.api.SimpleCallback;
|
import me.grishka.appkit.api.SimpleCallback;
|
||||||
import me.grishka.appkit.utils.MergeRecyclerAdapter;
|
|
||||||
import me.grishka.appkit.utils.SingleViewRecyclerAdapter;
|
|
||||||
import me.grishka.appkit.utils.V;
|
import me.grishka.appkit.utils.V;
|
||||||
|
|
||||||
public class HashtagTimelineFragment extends PinnableStatusListFragment{
|
public class HashtagTimelineFragment extends PinnableStatusListFragment {
|
||||||
private Hashtag hashtag;
|
private String hashtag;
|
||||||
private String hashtagName;
|
|
||||||
private TextView headerTitle, headerSubtitle;
|
|
||||||
private ProgressBarButton followButton;
|
|
||||||
private ProgressBar followProgress;
|
|
||||||
private MenuItem followMenuItem, pinMenuItem;
|
|
||||||
private boolean followRequestRunning;
|
|
||||||
private boolean toolbarContentVisible;
|
|
||||||
|
|
||||||
private List<String> any;
|
private List<String> any;
|
||||||
private List<String> all;
|
private List<String> all;
|
||||||
private List<String> none;
|
private List<String> none;
|
||||||
private boolean following;
|
private boolean following;
|
||||||
private boolean localOnly;
|
private boolean localOnly;
|
||||||
private Menu optionsMenu;
|
private MenuItem followButton;
|
||||||
private MenuInflater optionsMenuInflater;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean wantsComposeButton() {
|
protected boolean wantsComposeButton() {
|
||||||
@@ -69,36 +50,89 @@ public class HashtagTimelineFragment extends PinnableStatusListFragment{
|
|||||||
@Override
|
@Override
|
||||||
public void onAttach(Activity activity){
|
public void onAttach(Activity activity){
|
||||||
super.onAttach(activity);
|
super.onAttach(activity);
|
||||||
|
updateTitle(getArguments().getString("hashtag"));
|
||||||
following=getArguments().getBoolean("following", false);
|
following=getArguments().getBoolean("following", false);
|
||||||
localOnly=getArguments().getBoolean("localOnly", false);
|
localOnly=getArguments().getBoolean("localOnly", false);
|
||||||
any=getArguments().getStringArrayList("any");
|
any=getArguments().getStringArrayList("any");
|
||||||
all=getArguments().getStringArrayList("all");
|
all=getArguments().getStringArrayList("all");
|
||||||
none=getArguments().getStringArrayList("none");
|
none=getArguments().getStringArrayList("none");
|
||||||
if(getArguments().containsKey("hashtag")){
|
|
||||||
hashtag=Parcels.unwrap(getArguments().getParcelable("hashtag"));
|
|
||||||
hashtagName=hashtag.name;
|
|
||||||
}else{
|
|
||||||
hashtagName=getArguments().getString("hashtagName");
|
|
||||||
}
|
|
||||||
setTitle('#'+hashtagName);
|
|
||||||
setHasOptionsMenu(true);
|
setHasOptionsMenu(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateTitle(String hashtagName) {
|
||||||
|
hashtag = hashtagName;
|
||||||
|
setTitle('#'+hashtag);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateFollowingState(boolean newFollowing) {
|
||||||
|
this.following = newFollowing;
|
||||||
|
followButton.setTitle(getString(newFollowing ? R.string.unfollow_user : R.string.follow_user, "#" + hashtag));
|
||||||
|
followButton.setIcon(newFollowing ? R.drawable.ic_fluent_person_delete_24_filled : R.drawable.ic_fluent_person_add_24_regular);
|
||||||
|
E.post(new HashtagUpdatedEvent(hashtag, following));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||||
|
inflater.inflate(R.menu.hashtag_timeline, menu);
|
||||||
|
super.onCreateOptionsMenu(menu, inflater);
|
||||||
|
followButton = menu.findItem(R.id.follow_hashtag);
|
||||||
|
updateFollowingState(following);
|
||||||
|
|
||||||
|
new GetHashtag(hashtag).setCallback(new Callback<>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(Hashtag hashtag) {
|
||||||
|
if (getActivity() == null) return;
|
||||||
|
updateTitle(hashtag.name);
|
||||||
|
updateFollowingState(hashtag.following);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(ErrorResponse error) {
|
||||||
|
error.showToast(getActivity());
|
||||||
|
}
|
||||||
|
}).exec(accountID);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
if (super.onOptionsItemSelected(item)) return true;
|
||||||
|
if (item.getItemId() == R.id.follow_hashtag) {
|
||||||
|
updateFollowingState(!following);
|
||||||
|
getToolbar().performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK);
|
||||||
|
new SetHashtagFollowed(hashtag, following).setCallback(new Callback<>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(Hashtag i) {
|
||||||
|
if (getActivity() == null) return;
|
||||||
|
if (i.following == following) Toast.makeText(getActivity(), getString(i.following ? R.string.followed_user : R.string.unfollowed_user, "#" + i.name), Toast.LENGTH_SHORT).show();
|
||||||
|
updateFollowingState(i.following);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(ErrorResponse error) {
|
||||||
|
error.showToast(getActivity());
|
||||||
|
updateFollowingState(!following);
|
||||||
|
}
|
||||||
|
}).exec(accountID);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected TimelineDefinition makeTimelineDefinition() {
|
protected TimelineDefinition makeTimelineDefinition() {
|
||||||
return TimelineDefinition.ofHashtag(hashtagName);
|
return TimelineDefinition.ofHashtag(hashtag);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doLoadData(int offset, int count){
|
protected void doLoadData(int offset, int count){
|
||||||
currentRequest=new GetHashtagTimeline(hashtagName, getMaxID(), null, count, any, all, none, localOnly, getLocalPrefs().timelineReplyVisibility)
|
currentRequest=new GetHashtagTimeline(hashtag, offset==0 ? null : getMaxID(), null, count, any, all, none, localOnly, getLocalPrefs().timelineReplyVisibility)
|
||||||
.setCallback(new SimpleCallback<>(this){
|
.setCallback(new SimpleCallback<>(this){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<Status> result){
|
public void onSuccess(List<Status> result){
|
||||||
if(getActivity()==null) return;
|
if (getActivity() == null) return;
|
||||||
boolean more=applyMaxID(result);
|
result=result.stream().filter(new StatusFilterPredicate(accountID, getFilterContext())).collect(Collectors.toList());
|
||||||
AccountSessionManager.get(accountID).filterStatuses(result, getFilterContext());
|
onDataLoaded(result, !result.isEmpty());
|
||||||
onDataLoaded(result, more);
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.exec(accountID);
|
.exec(accountID);
|
||||||
@@ -112,40 +146,15 @@ public class HashtagTimelineFragment extends PinnableStatusListFragment{
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void loadData(){
|
public boolean onFabLongClick(View v) {
|
||||||
reloadTag();
|
return UiUtils.pickAccountForCompose(getActivity(), accountID, '#'+hashtag+' ');
|
||||||
super.loadData();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onViewCreated(View view, Bundle savedInstanceState){
|
|
||||||
super.onViewCreated(view, savedInstanceState);
|
|
||||||
fab=view.findViewById(R.id.fab);
|
|
||||||
fab.setOnClickListener(this::onFabClick);
|
|
||||||
|
|
||||||
if(getParentFragment() instanceof HomeTabFragment) return;
|
|
||||||
|
|
||||||
list.addOnScrollListener(new RecyclerView.OnScrollListener(){
|
|
||||||
@Override
|
|
||||||
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy){
|
|
||||||
View topChild=recyclerView.getChildAt(0);
|
|
||||||
int firstChildPos=recyclerView.getChildAdapterPosition(topChild);
|
|
||||||
float newAlpha=firstChildPos>0 ? 1f : Math.min(1f, -topChild.getTop()/(float)headerTitle.getHeight());
|
|
||||||
toolbarTitleView.setAlpha(newAlpha);
|
|
||||||
boolean newToolbarVisibility=newAlpha>0.5f;
|
|
||||||
if(newToolbarVisibility!=toolbarContentVisible){
|
|
||||||
toolbarContentVisible=newToolbarVisibility;
|
|
||||||
createOptionsMenu();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFabClick(View v){
|
public void onFabClick(View v){
|
||||||
Bundle args=new Bundle();
|
Bundle args=new Bundle();
|
||||||
args.putString("account", accountID);
|
args.putString("account", accountID);
|
||||||
args.putString("prefilledText", '#'+hashtagName+' ');
|
args.putString("prefilledText", '#'+hashtag+' ');
|
||||||
Nav.go(getActivity(), ComposeFragment.class, args);
|
Nav.go(getActivity(), ComposeFragment.class, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,202 +170,6 @@ public class HashtagTimelineFragment extends PinnableStatusListFragment{
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Uri getWebUri(Uri.Builder base) {
|
public Uri getWebUri(Uri.Builder base) {
|
||||||
return base.path((isInstanceAkkoma() ? "/tag/" : "/tags/") + hashtag).build();
|
return base.path((isInstanceAkkoma() ? "/tag/" : "/tags") + hashtag).build();
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected RecyclerView.Adapter getAdapter(){
|
|
||||||
View header=getActivity().getLayoutInflater().inflate(R.layout.header_hashtag_timeline, list, false);
|
|
||||||
headerTitle=header.findViewById(R.id.title);
|
|
||||||
headerSubtitle=header.findViewById(R.id.subtitle);
|
|
||||||
followButton=header.findViewById(R.id.profile_action_btn);
|
|
||||||
followProgress=header.findViewById(R.id.action_progress);
|
|
||||||
|
|
||||||
headerTitle.setText("#"+hashtagName);
|
|
||||||
followButton.setVisibility(View.GONE);
|
|
||||||
followButton.setOnClickListener(v->{
|
|
||||||
if(hashtag==null)
|
|
||||||
return;
|
|
||||||
setFollowed(!hashtag.following);
|
|
||||||
});
|
|
||||||
followButton.setOnLongClickListener(v->{
|
|
||||||
if(hashtag==null) return false;
|
|
||||||
UiUtils.pickAccount(getActivity(), accountID, R.string.sk_follow_as, R.drawable.ic_fluent_person_add_28_regular, session -> {
|
|
||||||
new SetTagFollowed(hashtagName, true).setCallback(new Callback<>(){
|
|
||||||
@Override
|
|
||||||
public void onSuccess(Hashtag hashtag) {
|
|
||||||
Toast.makeText(
|
|
||||||
getActivity(),
|
|
||||||
getString(R.string.sk_followed_as, session.self.getShortUsername()),
|
|
||||||
Toast.LENGTH_SHORT
|
|
||||||
).show();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onError(ErrorResponse error) {
|
|
||||||
error.showToast(getActivity());
|
|
||||||
}
|
|
||||||
}).exec(session.getID());
|
|
||||||
}, null);
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
updateHeader();
|
|
||||||
|
|
||||||
MergeRecyclerAdapter mergeAdapter=new MergeRecyclerAdapter();
|
|
||||||
if(!(getParentFragment() instanceof HomeTabFragment)){
|
|
||||||
mergeAdapter.addAdapter(new SingleViewRecyclerAdapter(header));
|
|
||||||
}
|
|
||||||
mergeAdapter.addAdapter(super.getAdapter());
|
|
||||||
return mergeAdapter;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected int getMainAdapterOffset(){
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createOptionsMenu(){
|
|
||||||
optionsMenu.clear();
|
|
||||||
optionsMenuInflater.inflate(R.menu.hashtag_timeline, optionsMenu);
|
|
||||||
followMenuItem=optionsMenu.findItem(R.id.follow_hashtag);
|
|
||||||
pinMenuItem=optionsMenu.findItem(R.id.pin);
|
|
||||||
followMenuItem.setVisible(toolbarContentVisible);
|
|
||||||
pinMenuItem.setShowAsAction(toolbarContentVisible ? MenuItem.SHOW_AS_ACTION_NEVER : MenuItem.SHOW_AS_ACTION_ALWAYS);
|
|
||||||
super.updatePinButton(pinMenuItem);
|
|
||||||
if(toolbarContentVisible){
|
|
||||||
UiUtils.enableOptionsMenuIcons(getContext(), optionsMenu);
|
|
||||||
}else{
|
|
||||||
UiUtils.enableOptionsMenuIcons(getContext(), optionsMenu, R.id.pin);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updatePinButton(MenuItem pin){
|
|
||||||
super.updatePinButton(pin);
|
|
||||||
if(toolbarContentVisible) UiUtils.insetPopupMenuIcon(getContext(), pin);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater){
|
|
||||||
inflater.inflate(R.menu.hashtag_timeline, menu);
|
|
||||||
super.onCreateOptionsMenu(menu, inflater);
|
|
||||||
optionsMenu=menu;
|
|
||||||
optionsMenuInflater=inflater;
|
|
||||||
createOptionsMenu();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onOptionsItemSelected(MenuItem item){
|
|
||||||
if (super.onOptionsItemSelected(item)) return true;
|
|
||||||
if (item.getItemId() == R.id.follow_hashtag && hashtag!=null) {
|
|
||||||
setFollowed(!hashtag.following);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onUpdateToolbar(){
|
|
||||||
super.onUpdateToolbar();
|
|
||||||
toolbarTitleView.setAlpha(toolbarContentVisible ? 1f : 0f);
|
|
||||||
createOptionsMenu();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateHeader(){
|
|
||||||
if(hashtag==null || getActivity()==null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if(hashtag.history!=null && !hashtag.history.isEmpty()){
|
|
||||||
int weekPosts=hashtag.history.stream().mapToInt(h->h.uses).sum();
|
|
||||||
int todayPosts=hashtag.history.get(0).uses;
|
|
||||||
int numAccounts=hashtag.history.stream().mapToInt(h->h.accounts).sum();
|
|
||||||
int hSpace=V.dp(8);
|
|
||||||
SpannableStringBuilder ssb=new SpannableStringBuilder();
|
|
||||||
ssb.append(getResources().getQuantityString(R.plurals.x_posts, weekPosts, weekPosts));
|
|
||||||
ssb.append(" ", new SpacerSpan(hSpace, 0), 0);
|
|
||||||
ssb.append('·');
|
|
||||||
ssb.append(" ", new SpacerSpan(hSpace, 0), 0);
|
|
||||||
ssb.append(getResources().getQuantityString(R.plurals.x_participants, numAccounts, numAccounts));
|
|
||||||
ssb.append(" ", new SpacerSpan(hSpace, 0), 0);
|
|
||||||
ssb.append('·');
|
|
||||||
ssb.append(" ", new SpacerSpan(hSpace, 0), 0);
|
|
||||||
ssb.append(getResources().getQuantityString(R.plurals.x_posts_today, todayPosts, todayPosts));
|
|
||||||
headerSubtitle.setText(ssb);
|
|
||||||
}
|
|
||||||
|
|
||||||
int styleRes;
|
|
||||||
followButton.setVisibility(View.VISIBLE);
|
|
||||||
if(hashtag.following){
|
|
||||||
followButton.setText(R.string.button_following);
|
|
||||||
styleRes=R.style.Widget_Mastodon_M3_Button_Tonal;
|
|
||||||
}else{
|
|
||||||
followButton.setText(R.string.button_follow);
|
|
||||||
styleRes=R.style.Widget_Mastodon_M3_Button_Filled;
|
|
||||||
}
|
|
||||||
TypedArray ta=followButton.getContext().obtainStyledAttributes(styleRes, new int[]{android.R.attr.background});
|
|
||||||
followButton.setBackground(ta.getDrawable(0));
|
|
||||||
ta.recycle();
|
|
||||||
ta=followButton.getContext().obtainStyledAttributes(styleRes, new int[]{android.R.attr.textColor});
|
|
||||||
followButton.setTextColor(ta.getColorStateList(0));
|
|
||||||
followProgress.setIndeterminateTintList(ta.getColorStateList(0));
|
|
||||||
ta.recycle();
|
|
||||||
|
|
||||||
followButton.setTextVisible(true);
|
|
||||||
followProgress.setVisibility(View.GONE);
|
|
||||||
if(followMenuItem!=null){
|
|
||||||
followMenuItem.setTitle(getString(hashtag.following ? R.string.unfollow_user : R.string.follow_user, "#"+hashtagName));
|
|
||||||
followMenuItem.setIcon(hashtag.following ? R.drawable.ic_fluent_person_delete_24_filled : R.drawable.ic_fluent_person_add_24_regular);
|
|
||||||
UiUtils.insetPopupMenuIcon(getContext(), followMenuItem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void reloadTag(){
|
|
||||||
new GetTag(hashtagName)
|
|
||||||
.setCallback(new Callback<>(){
|
|
||||||
@Override
|
|
||||||
public void onSuccess(Hashtag result){
|
|
||||||
hashtag=result;
|
|
||||||
updateHeader();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onError(ErrorResponse error){
|
|
||||||
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.exec(accountID);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setFollowed(boolean followed){
|
|
||||||
if(followRequestRunning)
|
|
||||||
return;
|
|
||||||
getToolbar().performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK);
|
|
||||||
followButton.setTextVisible(false);
|
|
||||||
followProgress.setVisibility(View.VISIBLE);
|
|
||||||
followRequestRunning=true;
|
|
||||||
new SetTagFollowed(hashtagName, followed)
|
|
||||||
.setCallback(new Callback<>(){
|
|
||||||
@Override
|
|
||||||
public void onSuccess(Hashtag result){
|
|
||||||
if(getActivity()==null)
|
|
||||||
return;
|
|
||||||
hashtag=result;
|
|
||||||
updateHeader();
|
|
||||||
followRequestRunning=false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onError(ErrorResponse error){
|
|
||||||
if(getActivity()==null)
|
|
||||||
return;
|
|
||||||
if(error instanceof MastodonErrorResponse er && "Duplicate record".equals(er.error)){
|
|
||||||
hashtag.following=true;
|
|
||||||
}else{
|
|
||||||
error.showToast(getActivity());
|
|
||||||
}
|
|
||||||
updateHeader();
|
|
||||||
followRequestRunning=false;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.exec(accountID);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -184,7 +184,6 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tabBar.selectTab(currentTab);
|
|
||||||
|
|
||||||
return content;
|
return content;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -287,7 +287,7 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
|||||||
new GetAnnouncements(false).setCallback(new Callback<>() {
|
new GetAnnouncements(false).setCallback(new Callback<>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<Announcement> result) {
|
public void onSuccess(List<Announcement> result) {
|
||||||
if(getActivity()==null) return;
|
if (getActivity() == null) return;
|
||||||
if (result.stream().anyMatch(a -> !a.read)) {
|
if (result.stream().anyMatch(a -> !a.read)) {
|
||||||
announcementsBadged = true;
|
announcementsBadged = true;
|
||||||
announcements.setVisible(false);
|
announcements.setVisible(false);
|
||||||
@@ -381,7 +381,7 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateOverflowMenu() {
|
private void updateOverflowMenu() {
|
||||||
if(getActivity()==null) return;
|
if (getActivity() == null) return;
|
||||||
Menu m = overflowPopup.getMenu();
|
Menu m = overflowPopup.getMenu();
|
||||||
m.clear();
|
m.clear();
|
||||||
overflowPopup.inflate(R.menu.home_overflow);
|
overflowPopup.inflate(R.menu.home_overflow);
|
||||||
@@ -524,7 +524,9 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
|
|||||||
if (list.repliesPolicy != null) args.putInt("repliesPolicy", list.repliesPolicy.ordinal());
|
if (list.repliesPolicy != null) args.putInt("repliesPolicy", list.repliesPolicy.ordinal());
|
||||||
Nav.go(getActivity(), ListTimelineFragment.class, args);
|
Nav.go(getActivity(), ListTimelineFragment.class, args);
|
||||||
} else if ((hashtag = hashtagsItems.get(id)) != null) {
|
} else if ((hashtag = hashtagsItems.get(id)) != null) {
|
||||||
UiUtils.openHashtagTimeline(getContext(), accountID, hashtag);
|
args.putString("hashtag", hashtag.name);
|
||||||
|
args.putBoolean("following", hashtag.following);
|
||||||
|
Nav.go(getActivity(), HashtagTimelineFragment.class, args);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,26 +11,27 @@ import androidx.recyclerview.widget.RecyclerView;
|
|||||||
import org.joinmastodon.android.GlobalUserPreferences;
|
import org.joinmastodon.android.GlobalUserPreferences;
|
||||||
import org.joinmastodon.android.api.requests.markers.SaveMarkers;
|
import org.joinmastodon.android.api.requests.markers.SaveMarkers;
|
||||||
import org.joinmastodon.android.api.requests.timelines.GetHomeTimeline;
|
import org.joinmastodon.android.api.requests.timelines.GetHomeTimeline;
|
||||||
|
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.StatusCreatedEvent;
|
||||||
import org.joinmastodon.android.model.CacheablePaginatedResponse;
|
import org.joinmastodon.android.model.CacheablePaginatedResponse;
|
||||||
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.model.TimelineMarkers;
|
import org.joinmastodon.android.model.TimelineMarkers;
|
||||||
import org.joinmastodon.android.ui.displayitems.GapStatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.GapStatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
|
||||||
|
import org.joinmastodon.android.utils.StatusFilterPredicate;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import me.grishka.appkit.api.Callback;
|
import me.grishka.appkit.api.Callback;
|
||||||
import me.grishka.appkit.api.ErrorResponse;
|
import me.grishka.appkit.api.ErrorResponse;
|
||||||
import me.grishka.appkit.api.SimpleCallback;
|
import me.grishka.appkit.api.SimpleCallback;
|
||||||
|
import me.grishka.appkit.utils.V;
|
||||||
|
|
||||||
public class HomeTimelineFragment extends StatusListFragment {
|
public class HomeTimelineFragment extends StatusListFragment {
|
||||||
private HomeTabFragment parent;
|
private HomeTabFragment parent;
|
||||||
@@ -49,6 +50,16 @@ public class HomeTimelineFragment extends StatusListFragment {
|
|||||||
loadData();
|
loadData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean typeFilterPredicate(Status s) {
|
||||||
|
AccountLocalPreferences lp=getLocalPrefs();
|
||||||
|
return (lp.showReplies || s.inReplyToId == null) &&
|
||||||
|
(lp.showBoosts || s.reblog == null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Status> filterPosts(List<Status> items) {
|
||||||
|
return items.stream().filter(this::typeFilterPredicate).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doLoadData(int offset, int count){
|
protected void doLoadData(int offset, int count){
|
||||||
AccountSessionManager.getInstance()
|
AccountSessionManager.getInstance()
|
||||||
@@ -56,11 +67,10 @@ public class HomeTimelineFragment extends StatusListFragment {
|
|||||||
.getHomeTimeline(offset>0 ? maxID : null, count, refreshing, new SimpleCallback<>(this){
|
.getHomeTimeline(offset>0 ? maxID : null, count, refreshing, new SimpleCallback<>(this){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(CacheablePaginatedResponse<List<Status>> result){
|
public void onSuccess(CacheablePaginatedResponse<List<Status>> result){
|
||||||
if(getActivity()==null) return;
|
if (getActivity() == null) return;
|
||||||
boolean empty=result.items.isEmpty();
|
List<Status> filteredItems = filterPosts(result.items);
|
||||||
maxID=result.maxID;
|
maxID=result.maxID;
|
||||||
AccountSessionManager.get(accountID).filterStatuses(result.items, getFilterContext());
|
onDataLoaded(filteredItems, !result.items.isEmpty());
|
||||||
onDataLoaded(result.items, !empty);
|
|
||||||
if(result.isFromCache())
|
if(result.isFromCache())
|
||||||
loadNewPosts();
|
loadNewPosts();
|
||||||
}
|
}
|
||||||
@@ -133,26 +143,23 @@ public class HomeTimelineFragment extends StatusListFragment {
|
|||||||
public void onSuccess(List<Status> result){
|
public void onSuccess(List<Status> result){
|
||||||
currentRequest=null;
|
currentRequest=null;
|
||||||
dataLoading=false;
|
dataLoading=false;
|
||||||
|
result = filterPosts(result);
|
||||||
if(result.isEmpty() || getActivity()==null)
|
if(result.isEmpty() || getActivity()==null)
|
||||||
return;
|
return;
|
||||||
Status last=result.get(result.size()-1);
|
Status last=result.get(result.size()-1);
|
||||||
List<Status> toAdd;
|
List<Status> toAdd;
|
||||||
if(!data.isEmpty() && last.id.equals(data.get(0).id)){ // This part intersects with the existing one
|
if(!data.isEmpty() && last.id.equals(data.get(0).id)){ // This part intersects with the existing one
|
||||||
toAdd=new ArrayList<>(result.subList(0, result.size()-1)); // Remove the already known last post
|
toAdd=result.subList(0, result.size()-1); // Remove the already known last post
|
||||||
}else{
|
}else{
|
||||||
last.hasGapAfter=last.id;
|
result.get(result.size()-1).hasGapAfter=true;
|
||||||
toAdd=result;
|
toAdd=result;
|
||||||
}
|
}
|
||||||
List<String> existingIds=data.stream().map(Status::getID).collect(Collectors.toList());
|
|
||||||
toAdd.removeIf(s->existingIds.contains(s.getID()));
|
|
||||||
List<Status> toAddUnfiltered=new ArrayList<>(toAdd);
|
|
||||||
AccountSessionManager.get(accountID).filterStatuses(toAdd, getFilterContext());
|
AccountSessionManager.get(accountID).filterStatuses(toAdd, getFilterContext());
|
||||||
if(!toAdd.isEmpty()){
|
if(!toAdd.isEmpty()){
|
||||||
prependItems(toAdd, true);
|
prependItems(toAdd, true);
|
||||||
if(parent != null && GlobalUserPreferences.showNewPostsButton) parent.showNewPostsButton();
|
if (parent != null && GlobalUserPreferences.showNewPostsButton) parent.showNewPostsButton();
|
||||||
|
AccountSessionManager.getInstance().getAccount(accountID).getCacheController().putHomeTimeline(toAdd, false);
|
||||||
}
|
}
|
||||||
if(toAddUnfiltered.isEmpty())
|
|
||||||
AccountSessionManager.getInstance().getAccount(accountID).getCacheController().putHomeTimeline(toAddUnfiltered, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -169,23 +176,15 @@ public class HomeTimelineFragment extends StatusListFragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onGapClick(GapStatusDisplayItem.Holder item, boolean downwards){
|
public void onGapClick(GapStatusDisplayItem.Holder item){
|
||||||
if(dataLoading)
|
if(dataLoading)
|
||||||
return;
|
return;
|
||||||
|
item.getItem().loading=true;
|
||||||
|
V.setVisibilityAnimated(item.progress, View.VISIBLE);
|
||||||
|
V.setVisibilityAnimated(item.text, View.GONE);
|
||||||
GapStatusDisplayItem gap=item.getItem();
|
GapStatusDisplayItem gap=item.getItem();
|
||||||
gap.loading=true;
|
|
||||||
dataLoading=true;
|
dataLoading=true;
|
||||||
|
currentRequest=new GetHomeTimeline(item.getItemID(), null, 20, null, getLocalPrefs().timelineReplyVisibility)
|
||||||
String maxID=null;
|
|
||||||
String minID=null;
|
|
||||||
if (downwards) {
|
|
||||||
maxID=item.getItem().getMaxID();
|
|
||||||
} else {
|
|
||||||
int gapPos=displayItems.indexOf(gap);
|
|
||||||
StatusDisplayItem nextItem=displayItems.get(gapPos + 1);
|
|
||||||
minID=nextItem.parentID;
|
|
||||||
}
|
|
||||||
currentRequest=new GetHomeTimeline(maxID, minID, 20, null, getLocalPrefs().timelineReplyVisibility)
|
|
||||||
.setCallback(new Callback<>(){
|
.setCallback(new Callback<>(){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<Status> result){
|
public void onSuccess(List<Status> result){
|
||||||
@@ -196,107 +195,61 @@ public class HomeTimelineFragment extends StatusListFragment {
|
|||||||
int gapPos=displayItems.indexOf(gap);
|
int gapPos=displayItems.indexOf(gap);
|
||||||
if(gapPos==-1)
|
if(gapPos==-1)
|
||||||
return;
|
return;
|
||||||
AccountSessionManager.get(accountID).filterStatuses(result, getFilterContext());
|
|
||||||
if(result.isEmpty()){
|
if(result.isEmpty()){
|
||||||
displayItems.remove(gapPos);
|
displayItems.remove(gapPos);
|
||||||
adapter.notifyItemRemoved(getMainAdapterOffset()+gapPos);
|
adapter.notifyItemRemoved(getMainAdapterOffset()+gapPos);
|
||||||
Status gapStatus=getStatusByID(gap.parentID);
|
Status gapStatus=getStatusByID(gap.parentID);
|
||||||
if(gapStatus!=null){
|
if(gapStatus!=null){
|
||||||
gapStatus.hasGapAfter=null;
|
gapStatus.hasGapAfter=false;
|
||||||
AccountSessionManager.getInstance().getAccount(accountID).getCacheController().putHomeTimeline(Collections.singletonList(gapStatus), false);
|
AccountSessionManager.getInstance().getAccount(accountID).getCacheController().putHomeTimeline(Collections.singletonList(gapStatus), false);
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
if(downwards) {
|
Set<String> idsBelowGap=new HashSet<>();
|
||||||
Set<String> idsBelowGap=new HashSet<>();
|
boolean belowGap=false;
|
||||||
boolean belowGap=false;
|
int gapPostIndex=0;
|
||||||
int gapPostIndex=0;
|
for(Status s:data){
|
||||||
for(Status s:data){
|
if(belowGap){
|
||||||
if(belowGap){
|
idsBelowGap.add(s.id);
|
||||||
idsBelowGap.add(s.id);
|
}else if(s.id.equals(gap.parentID)){
|
||||||
}else if(s.id.equals(gap.parentID)){
|
belowGap=true;
|
||||||
belowGap=true;
|
s.hasGapAfter=false;
|
||||||
s.hasGapAfter=null;
|
AccountSessionManager.getInstance().getAccount(accountID).getCacheController().putHomeTimeline(Collections.singletonList(s), false);
|
||||||
AccountSessionManager.getInstance().getAccount(accountID).getCacheController().putHomeTimeline(Collections.singletonList(s), false);
|
|
||||||
}else{
|
|
||||||
gapPostIndex++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
int endIndex=0;
|
|
||||||
for(Status s:result){
|
|
||||||
endIndex++;
|
|
||||||
if(idsBelowGap.contains(s.id))
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if(endIndex==result.size()){
|
|
||||||
Status last=result.get(result.size()-1);
|
|
||||||
last.hasGapAfter=last.id;
|
|
||||||
}else{
|
}else{
|
||||||
result=result.subList(0, endIndex);
|
gapPostIndex++;
|
||||||
}
|
}
|
||||||
AccountSessionManager.get(accountID).filterStatuses(result, FilterContext.HOME);
|
|
||||||
List<StatusDisplayItem> targetList=displayItems.subList(gapPos, gapPos+1);
|
|
||||||
targetList.clear();
|
|
||||||
List<Status> insertedPosts=data.subList(gapPostIndex+1, gapPostIndex+1);
|
|
||||||
for(Status s:result){
|
|
||||||
if(idsBelowGap.contains(s.id))
|
|
||||||
break;
|
|
||||||
targetList.addAll(buildDisplayItems(s));
|
|
||||||
insertedPosts.add(s);
|
|
||||||
}
|
|
||||||
if(targetList.isEmpty()){
|
|
||||||
// oops. We didn't add new posts, but at least we know there are none.
|
|
||||||
adapter.notifyItemRemoved(getMainAdapterOffset()+gapPos);
|
|
||||||
}else{
|
|
||||||
adapter.notifyItemChanged(getMainAdapterOffset()+gapPos);
|
|
||||||
adapter.notifyItemRangeInserted(getMainAdapterOffset()+gapPos+1, targetList.size()-1);
|
|
||||||
}
|
|
||||||
AccountSessionManager.getInstance().getAccount(accountID).getCacheController().putHomeTimeline(insertedPosts, false);
|
|
||||||
} else {
|
|
||||||
String aboveGapID = gap.parentID;
|
|
||||||
int gapPostIndex = 0;
|
|
||||||
for (;gapPostIndex<data.size();gapPostIndex++){
|
|
||||||
if (Objects.equals(aboveGapID, data.get(gapPostIndex).id)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// find if there's an overlap between the new data and the current data
|
|
||||||
int indexOfGapInResponse = 0;
|
|
||||||
for (;indexOfGapInResponse<result.size();indexOfGapInResponse++){
|
|
||||||
if (Objects.equals(aboveGapID, result.get(indexOfGapInResponse).id)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// there is an overlap between new and current data
|
|
||||||
List<StatusDisplayItem> targetList=displayItems.subList(gapPos, gapPos+1);
|
|
||||||
if(indexOfGapInResponse<result.size()){
|
|
||||||
result=result.subList(indexOfGapInResponse+1,result.size());
|
|
||||||
Optional<Status> gapStatus=data.stream()
|
|
||||||
.filter(s->Objects.equals(s.id, gap.parentID))
|
|
||||||
.findFirst();
|
|
||||||
if (gapStatus.isPresent()) {
|
|
||||||
gapStatus.get().hasGapAfter=null;
|
|
||||||
AccountSessionManager.getInstance().getAccount(accountID).getCacheController().putHomeTimeline(Collections.singletonList(gapStatus.get()), false);
|
|
||||||
}
|
|
||||||
targetList.clear();
|
|
||||||
} else {
|
|
||||||
gap.loading=false;
|
|
||||||
}
|
|
||||||
List<Status> insertedPosts=data.subList(gapPostIndex+1, gapPostIndex+1);
|
|
||||||
for(Status s:result){
|
|
||||||
targetList.addAll(buildDisplayItems(s));
|
|
||||||
insertedPosts.add(s);
|
|
||||||
}
|
|
||||||
AccountSessionManager.get(accountID).filterStatuses(insertedPosts, FilterContext.HOME);
|
|
||||||
if(targetList.isEmpty()){
|
|
||||||
// oops. We didn't add new posts, but at least we know there are none.
|
|
||||||
adapter.notifyItemRemoved(getMainAdapterOffset()+gapPos);
|
|
||||||
}else{
|
|
||||||
adapter.notifyItemChanged(getMainAdapterOffset()+gapPos);
|
|
||||||
adapter.notifyItemRangeInserted(getMainAdapterOffset()+gapPos+1, targetList.size()-1);
|
|
||||||
}
|
|
||||||
list.scrollToPosition(getMainAdapterOffset()+gapPos+targetList.size());
|
|
||||||
AccountSessionManager.getInstance().getAccount(accountID).getCacheController().putHomeTimeline(insertedPosts, false);
|
|
||||||
}
|
}
|
||||||
|
int endIndex=0;
|
||||||
|
for(Status s:result){
|
||||||
|
endIndex++;
|
||||||
|
if(idsBelowGap.contains(s.id))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(endIndex==result.size()){
|
||||||
|
result.get(result.size()-1).hasGapAfter=true;
|
||||||
|
}else{
|
||||||
|
result=result.subList(0, endIndex);
|
||||||
|
}
|
||||||
|
List<StatusDisplayItem> targetList=displayItems.subList(gapPos, gapPos+1);
|
||||||
|
targetList.clear();
|
||||||
|
List<Status> insertedPosts=data.subList(gapPostIndex+1, gapPostIndex+1);
|
||||||
|
StatusFilterPredicate filterPredicate=new StatusFilterPredicate(accountID, getFilterContext());
|
||||||
|
for(Status s:result){
|
||||||
|
if(idsBelowGap.contains(s.id))
|
||||||
|
break;
|
||||||
|
if(typeFilterPredicate(s) && filterPredicate.test(s)){
|
||||||
|
targetList.addAll(buildDisplayItems(s));
|
||||||
|
insertedPosts.add(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AccountSessionManager.get(accountID).filterStatuses(insertedPosts, getFilterContext());
|
||||||
|
if(targetList.isEmpty()){
|
||||||
|
// oops. We didn't add new posts, but at least we know there are none.
|
||||||
|
adapter.notifyItemRemoved(getMainAdapterOffset()+gapPos);
|
||||||
|
}else{
|
||||||
|
adapter.notifyItemChanged(getMainAdapterOffset()+gapPos);
|
||||||
|
adapter.notifyItemRangeInserted(getMainAdapterOffset()+gapPos+1, targetList.size()-1);
|
||||||
|
}
|
||||||
|
AccountSessionManager.getInstance().getAccount(accountID).getCacheController().putHomeTimeline(insertedPosts, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ import org.joinmastodon.android.R;
|
|||||||
import org.joinmastodon.android.api.requests.lists.GetList;
|
import org.joinmastodon.android.api.requests.lists.GetList;
|
||||||
import org.joinmastodon.android.api.requests.lists.UpdateList;
|
import org.joinmastodon.android.api.requests.lists.UpdateList;
|
||||||
import org.joinmastodon.android.api.requests.timelines.GetListTimeline;
|
import org.joinmastodon.android.api.requests.timelines.GetListTimeline;
|
||||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
|
||||||
import org.joinmastodon.android.events.ListDeletedEvent;
|
import org.joinmastodon.android.events.ListDeletedEvent;
|
||||||
import org.joinmastodon.android.events.ListUpdatedCreatedEvent;
|
import org.joinmastodon.android.events.ListUpdatedCreatedEvent;
|
||||||
import org.joinmastodon.android.model.FilterContext;
|
import org.joinmastodon.android.model.FilterContext;
|
||||||
@@ -26,8 +25,10 @@ import org.joinmastodon.android.model.TimelineDefinition;
|
|||||||
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
|
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
import org.joinmastodon.android.ui.views.ListEditor;
|
import org.joinmastodon.android.ui.views.ListEditor;
|
||||||
|
import org.joinmastodon.android.utils.StatusFilterPredicate;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import me.grishka.appkit.Nav;
|
import me.grishka.appkit.Nav;
|
||||||
import me.grishka.appkit.api.Callback;
|
import me.grishka.appkit.api.Callback;
|
||||||
@@ -62,7 +63,7 @@ public class ListTimelineFragment extends PinnableStatusListFragment {
|
|||||||
new GetList(listID).setCallback(new Callback<>() {
|
new GetList(listID).setCallback(new Callback<>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(ListTimeline listTimeline) {
|
public void onSuccess(ListTimeline listTimeline) {
|
||||||
if(getActivity()==null) return;
|
if (getActivity() == null) return;
|
||||||
// TODO: save updated info
|
// TODO: save updated info
|
||||||
if (!listTimeline.title.equals(listTitle)) setTitle(listTimeline.title);
|
if (!listTimeline.title.equals(listTitle)) setTitle(listTimeline.title);
|
||||||
if (listTimeline.repliesPolicy != null && !listTimeline.repliesPolicy.equals(repliesPolicy)) {
|
if (listTimeline.repliesPolicy != null && !listTimeline.repliesPolicy.equals(repliesPolicy)) {
|
||||||
@@ -100,7 +101,7 @@ public class ListTimelineFragment extends PinnableStatusListFragment {
|
|||||||
new UpdateList(listID, newTitle, editor.isExclusive(), editor.getRepliesPolicy()).setCallback(new Callback<>() {
|
new UpdateList(listID, newTitle, editor.isExclusive(), editor.getRepliesPolicy()).setCallback(new Callback<>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(ListTimeline list) {
|
public void onSuccess(ListTimeline list) {
|
||||||
if(getActivity()==null) return;
|
if (getActivity() == null) return;
|
||||||
setTitle(list.title);
|
setTitle(list.title);
|
||||||
listTitle = list.title;
|
listTitle = list.title;
|
||||||
repliesPolicy = list.repliesPolicy;
|
repliesPolicy = list.repliesPolicy;
|
||||||
@@ -133,14 +134,13 @@ public class ListTimelineFragment extends PinnableStatusListFragment {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doLoadData(int offset, int count) {
|
protected void doLoadData(int offset, int count) {
|
||||||
currentRequest=new GetListTimeline(listID, getMaxID(), null, count, null, getLocalPrefs().timelineReplyVisibility)
|
currentRequest=new GetListTimeline(listID, offset==0 ? null : getMaxID(), null, count, null, getLocalPrefs().timelineReplyVisibility)
|
||||||
.setCallback(new SimpleCallback<>(this) {
|
.setCallback(new SimpleCallback<>(this) {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<Status> result) {
|
public void onSuccess(List<Status> result) {
|
||||||
if(getActivity()==null) return;
|
if (getActivity() == null) return;
|
||||||
boolean more=applyMaxID(result);
|
result=result.stream().filter(new StatusFilterPredicate(accountID, getFilterContext())).collect(Collectors.toList());
|
||||||
AccountSessionManager.get(accountID).filterStatuses(result, getFilterContext());
|
onDataLoaded(result, !result.isEmpty());
|
||||||
onDataLoaded(result, more);
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.exec(accountID);
|
.exec(accountID);
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ public class ListsFragment extends MastodonRecyclerFragment<ListTimeline> implem
|
|||||||
.setCallback(new SimpleCallback<>(this) {
|
.setCallback(new SimpleCallback<>(this) {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<ListTimeline> lists) {
|
public void onSuccess(List<ListTimeline> lists) {
|
||||||
if(getActivity()==null) return;
|
if (getActivity() == null) return;
|
||||||
for (ListTimeline l : lists) userInListBefore.put(l.id, true);
|
for (ListTimeline l : lists) userInListBefore.put(l.id, true);
|
||||||
userInList.putAll(userInListBefore);
|
userInList.putAll(userInListBefore);
|
||||||
if (profileAccountId == null || !lists.isEmpty()) onDataLoaded(lists, false);
|
if (profileAccountId == null || !lists.isEmpty()) onDataLoaded(lists, false);
|
||||||
@@ -149,7 +149,7 @@ public class ListsFragment extends MastodonRecyclerFragment<ListTimeline> implem
|
|||||||
currentRequest=new GetLists().setCallback(new SimpleCallback<>(ListsFragment.this) {
|
currentRequest=new GetLists().setCallback(new SimpleCallback<>(ListsFragment.this) {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<ListTimeline> allLists) {
|
public void onSuccess(List<ListTimeline> allLists) {
|
||||||
if(getActivity()==null) return;
|
if (getActivity() == null) return;
|
||||||
List<ListTimeline> newLists = new ArrayList<>();
|
List<ListTimeline> newLists = new ArrayList<>();
|
||||||
for (ListTimeline l : allLists) {
|
for (ListTimeline l : allLists) {
|
||||||
if (lists.stream().noneMatch(e -> e.id.equals(l.id))) newLists.add(l);
|
if (lists.stream().noneMatch(e -> e.id.equals(l.id))) newLists.add(l);
|
||||||
|
|||||||
@@ -254,7 +254,7 @@ public class NotificationsFragment extends MastodonToolbarFragment implements Sc
|
|||||||
new GetFollowRequests(null, 1).setCallback(new Callback<>() {
|
new GetFollowRequests(null, 1).setCallback(new Callback<>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(HeaderPaginationList<Account> accounts) {
|
public void onSuccess(HeaderPaginationList<Account> accounts) {
|
||||||
if(getActivity()==null) return;
|
if (getActivity() == null) return;
|
||||||
getToolbar().getMenu().findItem(R.id.follow_requests).setVisible(!accounts.isEmpty());
|
getToolbar().getMenu().findItem(R.id.follow_requests).setVisible(!accounts.isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
|
|||||||
if (n.type == Notification.Type.FOLLOW_REQUEST) {
|
if (n.type == Notification.Type.FOLLOW_REQUEST) {
|
||||||
ArrayList<StatusDisplayItem> items = new ArrayList<>();
|
ArrayList<StatusDisplayItem> items = new ArrayList<>();
|
||||||
items.add(titleItem);
|
items.add(titleItem);
|
||||||
items.add(new AccountCardStatusDisplayItem(n.id, this, accountID, n.account, n));
|
items.add(new AccountCardStatusDisplayItem(n.id, this, n.account, n));
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
if(n.status!=null){
|
if(n.status!=null){
|
||||||
@@ -134,7 +134,6 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
|
|||||||
return;
|
return;
|
||||||
maxID=result.maxID;
|
maxID=result.maxID;
|
||||||
onDataLoaded(result.items.stream().filter(n->n.type!=null).collect(Collectors.toList()), !result.items.isEmpty());
|
onDataLoaded(result.items.stream().filter(n->n.type!=null).collect(Collectors.toList()), !result.items.isEmpty());
|
||||||
if(bannerHelper!=null) bannerHelper.onBannerBecameVisible();
|
|
||||||
reloadingFromCache=false;
|
reloadingFromCache=false;
|
||||||
if (getParentFragment() instanceof NotificationsFragment nf) {
|
if (getParentFragment() instanceof NotificationsFragment nf) {
|
||||||
nf.updateMarkAllReadButton();
|
nf.updateMarkAllReadButton();
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import android.os.Bundle;
|
|||||||
|
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.requests.accounts.GetAccountStatuses;
|
import org.joinmastodon.android.api.requests.accounts.GetAccountStatuses;
|
||||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
|
||||||
import org.joinmastodon.android.model.Account;
|
import org.joinmastodon.android.model.Account;
|
||||||
import org.joinmastodon.android.model.FilterContext;
|
import org.joinmastodon.android.model.FilterContext;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
@@ -36,8 +35,6 @@ public class PinnedPostsListFragment extends StatusListFragment{
|
|||||||
.setCallback(new SimpleCallback<>(this){
|
.setCallback(new SimpleCallback<>(this){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<Status> result){
|
public void onSuccess(List<Status> result){
|
||||||
if(getActivity()==null) return;
|
|
||||||
AccountSessionManager.get(accountID).filterStatuses(result, getFilterContext());
|
|
||||||
onDataLoaded(result, false);
|
onDataLoaded(result, false);
|
||||||
}
|
}
|
||||||
}).exec(accountID);
|
}).exec(accountID);
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import android.os.Build;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.SpannableStringBuilder;
|
import android.text.SpannableStringBuilder;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
import android.text.style.ImageSpan;
|
||||||
import android.transition.ChangeBounds;
|
import android.transition.ChangeBounds;
|
||||||
import android.transition.Fade;
|
import android.transition.Fade;
|
||||||
import android.transition.TransitionManager;
|
import android.transition.TransitionManager;
|
||||||
@@ -59,10 +60,8 @@ import org.joinmastodon.android.api.requests.accounts.SetAccountFollowed;
|
|||||||
import org.joinmastodon.android.api.requests.accounts.UpdateAccountCredentials;
|
import org.joinmastodon.android.api.requests.accounts.UpdateAccountCredentials;
|
||||||
import org.joinmastodon.android.api.requests.instance.GetInstance;
|
import org.joinmastodon.android.api.requests.instance.GetInstance;
|
||||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||||
import org.joinmastodon.android.fragments.account_list.BlockedAccountsListFragment;
|
|
||||||
import org.joinmastodon.android.fragments.account_list.FollowerListFragment;
|
import org.joinmastodon.android.fragments.account_list.FollowerListFragment;
|
||||||
import org.joinmastodon.android.fragments.account_list.FollowingListFragment;
|
import org.joinmastodon.android.fragments.account_list.FollowingListFragment;
|
||||||
import org.joinmastodon.android.fragments.account_list.MutedAccountsListFragment;
|
|
||||||
import org.joinmastodon.android.fragments.report.ReportReasonChoiceFragment;
|
import org.joinmastodon.android.fragments.report.ReportReasonChoiceFragment;
|
||||||
import org.joinmastodon.android.fragments.settings.SettingsServerFragment;
|
import org.joinmastodon.android.fragments.settings.SettingsServerFragment;
|
||||||
import org.joinmastodon.android.model.Account;
|
import org.joinmastodon.android.model.Account;
|
||||||
@@ -133,7 +132,6 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
private ImageView avatar;
|
private ImageView avatar;
|
||||||
private CoverImageView cover;
|
private CoverImageView cover;
|
||||||
private View avatarBorder;
|
private View avatarBorder;
|
||||||
private View usernameWrap;
|
|
||||||
private TextView name, username, bio, followersCount, followersLabel, followingCount, followingLabel;
|
private TextView name, username, bio, followersCount, followersLabel, followingCount, followingLabel;
|
||||||
private ImageView lockIcon, botIcon;
|
private ImageView lockIcon, botIcon;
|
||||||
private ProgressBarButton actionButton, notifyButton;
|
private ProgressBarButton actionButton, notifyButton;
|
||||||
@@ -234,7 +232,6 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
cover=content.findViewById(R.id.cover);
|
cover=content.findViewById(R.id.cover);
|
||||||
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);
|
||||||
usernameWrap=content.findViewById(R.id.username_wrap);
|
|
||||||
username=content.findViewById(R.id.username);
|
username=content.findViewById(R.id.username);
|
||||||
lockIcon=content.findViewById(R.id.lock_icon);
|
lockIcon=content.findViewById(R.id.lock_icon);
|
||||||
botIcon=content.findViewById(R.id.bot_icon);
|
botIcon=content.findViewById(R.id.bot_icon);
|
||||||
@@ -307,12 +304,6 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
|
|
||||||
tabbar.setTabTextColors(UiUtils.getThemeColor(getActivity(), R.attr.colorM3OnSurfaceVariant), UiUtils.getThemeColor(getActivity(), R.attr.colorM3Primary));
|
tabbar.setTabTextColors(UiUtils.getThemeColor(getActivity(), R.attr.colorM3OnSurfaceVariant), UiUtils.getThemeColor(getActivity(), R.attr.colorM3Primary));
|
||||||
tabbar.setTabTextSize(V.dp(14));
|
tabbar.setTabTextSize(V.dp(14));
|
||||||
tabLayoutMediator=new TabLayoutMediator(tabbar, pager, (tab, position)->tab.setText(switch(position){
|
|
||||||
case 0 -> R.string.profile_featured;
|
|
||||||
case 1 -> R.string.profile_timeline;
|
|
||||||
case 2 -> R.string.profile_about;
|
|
||||||
default -> throw new IllegalStateException();
|
|
||||||
}));
|
|
||||||
tabLayoutMediator=new TabLayoutMediator(tabbar, pager, new TabLayoutMediator.TabConfigurationStrategy(){
|
tabLayoutMediator=new TabLayoutMediator(tabbar, pager, new TabLayoutMediator.TabConfigurationStrategy(){
|
||||||
@Override
|
@Override
|
||||||
public void onConfigureTab(@NonNull TabLayout.Tab tab, int position){
|
public void onConfigureTab(@NonNull TabLayout.Tab tab, int position){
|
||||||
@@ -326,19 +317,6 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
if (position == 4) tab.view.setVisibility(View.GONE);
|
if (position == 4) tab.view.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
tabbar.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener(){
|
|
||||||
@Override
|
|
||||||
public void onTabSelected(TabLayout.Tab tab){}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onTabUnselected(TabLayout.Tab tab){}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onTabReselected(TabLayout.Tab tab){
|
|
||||||
if(getFragmentForPage(tab.getPosition()) instanceof ScrollableToTop stt)
|
|
||||||
stt.scrollToTop();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
cover.setOutlineProvider(new ViewOutlineProvider(){
|
cover.setOutlineProvider(new ViewOutlineProvider(){
|
||||||
@Override
|
@Override
|
||||||
@@ -399,7 +377,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
content.findViewById(R.id.username_wrap).setOnLongClickListener(v->{
|
username.setOnLongClickListener(v->{
|
||||||
String usernameString=account.acct;
|
String usernameString=account.acct;
|
||||||
if(!usernameString.contains("@")){
|
if(!usernameString.contains("@")){
|
||||||
usernameString+="@"+domain;
|
usernameString+="@"+domain;
|
||||||
@@ -482,7 +460,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
.setCallback(new SimpleCallback<>(this){
|
.setCallback(new SimpleCallback<>(this){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(Account result){
|
public void onSuccess(Account result){
|
||||||
if(getActivity()==null) return;
|
if (getActivity() == null) return;
|
||||||
onAccountLoaded(result);
|
onAccountLoaded(result);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -623,10 +601,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
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));
|
||||||
ViewImageLoader.load(avatar, null, new UrlImageLoaderRequest(
|
ViewImageLoader.load(avatar, null, new UrlImageLoaderRequest(GlobalUserPreferences.playGifs ? account.avatar : account.avatarStatic, V.dp(100), V.dp(100)));
|
||||||
TextUtils.isEmpty(account.avatar) ? getSession().getDefaultAvatarUrl() :
|
|
||||||
GlobalUserPreferences.playGifs ? account.avatar : account.avatarStatic,
|
|
||||||
V.dp(100), V.dp(100)));
|
|
||||||
ViewImageLoader.load(cover, null, new UrlImageLoaderRequest(GlobalUserPreferences.playGifs ? account.header : account.headerStatic, 1000, 1000));
|
ViewImageLoader.load(cover, null, new UrlImageLoaderRequest(GlobalUserPreferences.playGifs ? account.header : account.headerStatic, 1000, 1000));
|
||||||
SpannableStringBuilder ssb=new SpannableStringBuilder(account.displayName);
|
SpannableStringBuilder ssb=new SpannableStringBuilder(account.displayName);
|
||||||
if(AccountSessionManager.get(accountID).getLocalPreferences().customEmojiInNames)
|
if(AccountSessionManager.get(accountID).getLocalPreferences().customEmojiInNames)
|
||||||
@@ -849,16 +824,6 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
args.putString("profileDisplayUsername", account.getDisplayUsername());
|
args.putString("profileDisplayUsername", account.getDisplayUsername());
|
||||||
}
|
}
|
||||||
Nav.go(getActivity(), ListsFragment.class, args);
|
Nav.go(getActivity(), ListsFragment.class, args);
|
||||||
}else if(id==R.id.muted_accounts){
|
|
||||||
final Bundle args=new Bundle();
|
|
||||||
args.putString("account", accountID);
|
|
||||||
args.putParcelable("targetAccount", Parcels.wrap(account));
|
|
||||||
Nav.go(getActivity(), MutedAccountsListFragment.class, args);
|
|
||||||
}else if(id==R.id.blocked_accounts){
|
|
||||||
final Bundle args=new Bundle();
|
|
||||||
args.putString("account", accountID);
|
|
||||||
args.putParcelable("targetAccount", Parcels.wrap(account));
|
|
||||||
Nav.go(getActivity(), BlockedAccountsListFragment.class, args);
|
|
||||||
}else if(id==R.id.followed_hashtags){
|
}else if(id==R.id.followed_hashtags){
|
||||||
Bundle args=new Bundle();
|
Bundle args=new Bundle();
|
||||||
args.putString("account", accountID);
|
args.putString("account", accountID);
|
||||||
@@ -894,7 +859,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateRelationship(){
|
private void updateRelationship(){
|
||||||
if(getActivity()==null) return;
|
if (getActivity() == null) return;
|
||||||
invalidateOptionsMenu();
|
invalidateOptionsMenu();
|
||||||
actionButton.setVisibility(View.VISIBLE);
|
actionButton.setVisibility(View.VISIBLE);
|
||||||
notifyButton.setVisibility(relationship.following ? View.VISIBLE : View.GONE);
|
notifyButton.setVisibility(relationship.following ? View.VISIBLE : View.GONE);
|
||||||
@@ -1116,7 +1081,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
|
|
||||||
name.setVisibility(View.GONE);
|
name.setVisibility(View.GONE);
|
||||||
rolesView.setVisibility(View.GONE);
|
rolesView.setVisibility(View.GONE);
|
||||||
usernameWrap.setVisibility(View.GONE);
|
username.setVisibility(View.GONE);
|
||||||
bio.setVisibility(View.GONE);
|
bio.setVisibility(View.GONE);
|
||||||
countersLayout.setVisibility(View.GONE);
|
countersLayout.setVisibility(View.GONE);
|
||||||
|
|
||||||
@@ -1165,7 +1130,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
bioEditWrap.setVisibility(View.GONE);
|
bioEditWrap.setVisibility(View.GONE);
|
||||||
name.setVisibility(View.VISIBLE);
|
name.setVisibility(View.VISIBLE);
|
||||||
rolesView.setVisibility(View.VISIBLE);
|
rolesView.setVisibility(View.VISIBLE);
|
||||||
usernameWrap.setVisibility(View.VISIBLE);
|
username.setVisibility(View.VISIBLE);
|
||||||
bio.setVisibility(View.VISIBLE);
|
bio.setVisibility(View.VISIBLE);
|
||||||
countersLayout.setVisibility(View.VISIBLE);
|
countersLayout.setVisibility(View.VISIBLE);
|
||||||
refreshLayout.setEnabled(true);
|
refreshLayout.setEnabled(true);
|
||||||
@@ -1177,7 +1142,6 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
imm.hideSoftInputFromWindow(content.getWindowToken(), 0);
|
imm.hideSoftInputFromWindow(content.getWindowToken(), 0);
|
||||||
V.setVisibilityAnimated(fab, View.VISIBLE);
|
V.setVisibilityAnimated(fab, View.VISIBLE);
|
||||||
bindHeaderView();
|
bindHeaderView();
|
||||||
V.setVisibilityAnimated(fab, View.VISIBLE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void saveAndExitEditMode(){
|
private void saveAndExitEditMode(){
|
||||||
@@ -1192,7 +1156,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
savingEdits=false;
|
savingEdits=false;
|
||||||
account=result;
|
account=result;
|
||||||
AccountSessionManager.getInstance().updateAccountInfo(accountID, account);
|
AccountSessionManager.getInstance().updateAccountInfo(accountID, account);
|
||||||
if(getActivity()==null) return;
|
if (getActivity() == null) return;
|
||||||
exitEditMode();
|
exitEditMode();
|
||||||
setActionProgressVisible(false);
|
setActionProgressVisible(false);
|
||||||
}
|
}
|
||||||
@@ -1265,7 +1229,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
if(ava==null)
|
if(ava==null)
|
||||||
return;
|
return;
|
||||||
int radius=V.dp(25);
|
int radius=V.dp(25);
|
||||||
currentPhotoViewer=new PhotoViewer(getActivity(), createFakeAttachments(TextUtils.isEmpty(account.avatar) ? getSession().getDefaultAvatarUrl() : account.avatar, ava), 0,
|
currentPhotoViewer=new PhotoViewer(getActivity(), createFakeAttachments(account.avatar, ava), 0,
|
||||||
new SingleImagePhotoViewerListener(avatar, avatarBorder, new int[]{radius, radius, radius, radius}, this, ()->currentPhotoViewer=null, ()->ava, null, null));
|
new SingleImagePhotoViewerListener(avatar, avatarBorder, new int[]{radius, radius, radius, radius}, this, ()->currentPhotoViewer=null, ()->ava, null, null));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1337,7 +1301,9 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public SimpleViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
|
public SimpleViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
|
||||||
FrameLayout view=new FrameLayout(parent.getContext());
|
FrameLayout view=tabViews[viewType];
|
||||||
|
if (view.getParent() != null) ((ViewGroup)view.getParent()).removeView(view);
|
||||||
|
view.setVisibility(View.VISIBLE);
|
||||||
view.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
view.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||||
return new SimpleViewHolder(view);
|
return new SimpleViewHolder(view);
|
||||||
}
|
}
|
||||||
@@ -1345,13 +1311,8 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
|
|||||||
@Override
|
@Override
|
||||||
public void onBindViewHolder(@NonNull SimpleViewHolder holder, int position){
|
public void onBindViewHolder(@NonNull SimpleViewHolder holder, int position){
|
||||||
Fragment fragment=getFragmentForPage(position);
|
Fragment fragment=getFragmentForPage(position);
|
||||||
FrameLayout fragmentView=tabViews[position];
|
|
||||||
fragmentView.setVisibility(View.VISIBLE);
|
|
||||||
if(fragmentView.getParent() instanceof ViewGroup parent)
|
|
||||||
parent.removeView(fragmentView);
|
|
||||||
((FrameLayout)holder.itemView).addView(fragmentView);
|
|
||||||
if(!fragment.isAdded()){
|
if(!fragment.isAdded()){
|
||||||
getChildFragmentManager().beginTransaction().add(fragmentView.getId(), fragment).commit();
|
getChildFragmentManager().beginTransaction().add(holder.itemView.getId(), fragment).commit();
|
||||||
holder.itemView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener(){
|
holder.itemView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener(){
|
||||||
@Override
|
@Override
|
||||||
public boolean onPreDraw(){
|
public boolean onPreDraw(){
|
||||||
|
|||||||
@@ -2,11 +2,8 @@ package org.joinmastodon.android.fragments;
|
|||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.view.WindowInsets;
|
|
||||||
import android.widget.ImageButton;
|
import android.widget.ImageButton;
|
||||||
|
|
||||||
import com.squareup.otto.Subscribe;
|
import com.squareup.otto.Subscribe;
|
||||||
@@ -29,7 +26,6 @@ import java.util.List;
|
|||||||
|
|
||||||
import me.grishka.appkit.Nav;
|
import me.grishka.appkit.Nav;
|
||||||
import me.grishka.appkit.api.SimpleCallback;
|
import me.grishka.appkit.api.SimpleCallback;
|
||||||
import me.grishka.appkit.utils.V;
|
|
||||||
|
|
||||||
public class ScheduledStatusListFragment extends BaseStatusListFragment<ScheduledStatus> {
|
public class ScheduledStatusListFragment extends BaseStatusListFragment<ScheduledStatus> {
|
||||||
private String nextMaxID;
|
private String nextMaxID;
|
||||||
@@ -129,7 +125,7 @@ public class ScheduledStatusListFragment extends BaseStatusListFragment<Schedule
|
|||||||
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
|
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
|
||||||
else
|
else
|
||||||
nextMaxID=null;
|
nextMaxID=null;
|
||||||
if(getActivity()==null) return;
|
if (getActivity() == null) return;
|
||||||
onDataLoaded(result, nextMaxID!=null);
|
onDataLoaded(result, nextMaxID!=null);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -190,21 +186,6 @@ public class ScheduledStatusListFragment extends BaseStatusListFragment<Schedule
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onApplyWindowInsets(WindowInsets insets){
|
|
||||||
if(contentView!=null){
|
|
||||||
if(Build.VERSION.SDK_INT>=29 && insets.getTappableElementInsets().bottom==0){
|
|
||||||
int insetBottom=insets.getSystemWindowInsetBottom();
|
|
||||||
((ViewGroup.MarginLayoutParams) list.getLayoutParams()).bottomMargin=insetBottom;
|
|
||||||
((ViewGroup.MarginLayoutParams) fab.getLayoutParams()).bottomMargin=V.dp(16)+insetBottom;
|
|
||||||
insets=insets.inset(0, 0, 0, insetBottom);
|
|
||||||
}else{
|
|
||||||
((ViewGroup.MarginLayoutParams) fab.getLayoutParams()).bottomMargin=V.dp(16);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
super.onApplyWindowInsets(insets);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Uri getWebUri(Uri.Builder base) {
|
public Uri getWebUri(Uri.Builder base) {
|
||||||
// TODO: adapt when frontends finally implement a scheduled posts list
|
// TODO: adapt when frontends finally implement a scheduled posts list
|
||||||
|
|||||||
@@ -47,12 +47,13 @@ public class SplashFragment extends AppKitFragment{
|
|||||||
private ProgressBarButton defaultServerButton;
|
private ProgressBarButton defaultServerButton;
|
||||||
private ProgressBar defaultServerProgress;
|
private ProgressBar defaultServerProgress;
|
||||||
private String chosenDefaultServer=DEFAULT_SERVER;
|
private String chosenDefaultServer=DEFAULT_SERVER;
|
||||||
private boolean loadingDefaultServer, loadedDefaultServer;
|
private boolean loadingDefaultServer;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState){
|
public void onCreate(Bundle savedInstanceState){
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
motionEffect=new InterpolatingMotionEffect(MastodonApp.context);
|
motionEffect=new InterpolatingMotionEffect(MastodonApp.context);
|
||||||
|
loadAndChooseDefaultServer();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@@ -100,8 +101,6 @@ public class SplashFragment extends AppKitFragment{
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if(!loadedDefaultServer && !loadingDefaultServer)
|
|
||||||
loadAndChooseDefaultServer();
|
|
||||||
|
|
||||||
return contentView;
|
return contentView;
|
||||||
}
|
}
|
||||||
@@ -240,7 +239,6 @@ public class SplashFragment extends AppKitFragment{
|
|||||||
private void setChosenDefaultServer(String domain){
|
private void setChosenDefaultServer(String domain){
|
||||||
chosenDefaultServer=domain;
|
chosenDefaultServer=domain;
|
||||||
loadingDefaultServer=false;
|
loadingDefaultServer=false;
|
||||||
loadedDefaultServer=true;
|
|
||||||
if(defaultServerButton!=null && getActivity()!=null){
|
if(defaultServerButton!=null && getActivity()!=null){
|
||||||
defaultServerButton.setTextVisible(true);
|
defaultServerButton.setTextVisible(true);
|
||||||
defaultServerProgress.setVisibility(View.GONE);
|
defaultServerProgress.setVisibility(View.GONE);
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import org.joinmastodon.android.R;
|
|||||||
import org.joinmastodon.android.api.requests.statuses.GetStatusEditHistory;
|
import org.joinmastodon.android.api.requests.statuses.GetStatusEditHistory;
|
||||||
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.DummyStatusDisplayItem;
|
|
||||||
import org.joinmastodon.android.ui.displayitems.ReblogOrReplyLineStatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.ReblogOrReplyLineStatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.utils.InsetStatusItemDecoration;
|
import org.joinmastodon.android.ui.utils.InsetStatusItemDecoration;
|
||||||
@@ -48,8 +47,8 @@ public class StatusEditHistoryFragment extends StatusListFragment{
|
|||||||
.setCallback(new SimpleCallback<>(this){
|
.setCallback(new SimpleCallback<>(this){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<Status> result){
|
public void onSuccess(List<Status> result){
|
||||||
if(getActivity()==null) return;
|
|
||||||
Collections.sort(result, Comparator.comparing((Status s)->s.createdAt).reversed());
|
Collections.sort(result, Comparator.comparing((Status s)->s.createdAt).reversed());
|
||||||
|
if (getActivity() == null) return;
|
||||||
onDataLoaded(result, false);
|
onDataLoaded(result, false);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -144,8 +143,7 @@ public class StatusEditHistoryFragment extends StatusListFragment{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
String sep = getString(R.string.sk_separator);
|
String sep = getString(R.string.sk_separator);
|
||||||
items.add(0, new ReblogOrReplyLineStatusDisplayItem(s.id, this, action+" "+sep+" "+date, Collections.emptyList(), 0, null, null, s));
|
items.add(0, new ReblogOrReplyLineStatusDisplayItem(s.id, this, action+" "+sep+" "+date, Collections.emptyList(), 0, null, null));
|
||||||
items.add(1, new DummyStatusDisplayItem(s.id, this));
|
|
||||||
}
|
}
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,12 +42,10 @@ public abstract class StatusListFragment extends BaseStatusListFragment<Status>
|
|||||||
boolean isMainThreadStatus = this instanceof ThreadFragment t && s.id.equals(t.mainStatus.id);
|
boolean isMainThreadStatus = this instanceof ThreadFragment t && s.id.equals(t.mainStatus.id);
|
||||||
int flags = 0;
|
int flags = 0;
|
||||||
AccountLocalPreferences lp=getLocalPrefs();
|
AccountLocalPreferences lp=getLocalPrefs();
|
||||||
if(GlobalUserPreferences.spectatorMode)
|
if (GlobalUserPreferences.spectatorMode)
|
||||||
flags |= StatusDisplayItem.FLAG_NO_FOOTER;
|
flags |= StatusDisplayItem.FLAG_NO_FOOTER;
|
||||||
if(!lp.emojiReactionsEnabled || lp.showEmojiReactions==ONLY_OPENED)
|
if (!lp.emojiReactionsEnabled || lp.showEmojiReactions==ONLY_OPENED)
|
||||||
flags |= StatusDisplayItem.FLAG_NO_EMOJI_REACTIONS;
|
flags |= StatusDisplayItem.FLAG_NO_EMOJI_REACTIONS;
|
||||||
if(GlobalUserPreferences.translateButtonOpenedOnly)
|
|
||||||
flags |= StatusDisplayItem.FLAG_NO_TRANSLATE;
|
|
||||||
return StatusDisplayItem.buildItems(this, s, accountID, s, knownAccounts, getFilterContext(), isMainThreadStatus ? 0 : flags);
|
return StatusDisplayItem.buildItems(this, s, accountID, s, knownAccounts, getFilterContext(), isMainThreadStatus ? 0 : flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ import org.joinmastodon.android.GlobalUserPreferences.AutoRevealMode;
|
|||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.requests.statuses.GetStatusByID;
|
import org.joinmastodon.android.api.requests.statuses.GetStatusByID;
|
||||||
import org.joinmastodon.android.api.requests.statuses.GetStatusContext;
|
import org.joinmastodon.android.api.requests.statuses.GetStatusContext;
|
||||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
|
||||||
import org.joinmastodon.android.events.StatusCountersUpdatedEvent;
|
import org.joinmastodon.android.events.StatusCountersUpdatedEvent;
|
||||||
|
import org.joinmastodon.android.events.StatusCreatedEvent;
|
||||||
import org.joinmastodon.android.events.StatusUpdatedEvent;
|
import org.joinmastodon.android.events.StatusUpdatedEvent;
|
||||||
import org.joinmastodon.android.model.Account;
|
import org.joinmastodon.android.model.Account;
|
||||||
import org.joinmastodon.android.model.FilterContext;
|
import org.joinmastodon.android.model.FilterContext;
|
||||||
@@ -24,13 +24,13 @@ import org.joinmastodon.android.ui.BetterItemAnimator;
|
|||||||
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.ReblogOrReplyLineStatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.ReblogOrReplyLineStatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.displayitems.SpoilerStatusDisplayItem;
|
|
||||||
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.displayitems.TextStatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.TextStatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.displayitems.WarningFilteredStatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.WarningFilteredStatusDisplayItem;
|
||||||
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.utils.UiUtils;
|
||||||
import org.joinmastodon.android.utils.ProvidesAssistContent;
|
import org.joinmastodon.android.utils.ProvidesAssistContent;
|
||||||
|
import org.joinmastodon.android.utils.StatusFilterPredicate;
|
||||||
import org.parceler.Parcels;
|
import org.parceler.Parcels;
|
||||||
|
|
||||||
import java.util.ArrayDeque;
|
import java.util.ArrayDeque;
|
||||||
@@ -105,12 +105,6 @@ public class ThreadFragment extends StatusListFragment implements ProvidesAssist
|
|||||||
text.textSelectable=true;
|
text.textSelectable=true;
|
||||||
else if(item instanceof FooterStatusDisplayItem footer)
|
else if(item instanceof FooterStatusDisplayItem footer)
|
||||||
footer.hideCounts=true;
|
footer.hideCounts=true;
|
||||||
else if(item instanceof SpoilerStatusDisplayItem spoiler){
|
|
||||||
for(StatusDisplayItem subItem:spoiler.contentItems){
|
|
||||||
if(subItem instanceof TextStatusDisplayItem text)
|
|
||||||
text.textSelectable=true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,8 +188,8 @@ public class ThreadFragment extends StatusListFragment implements ProvidesAssist
|
|||||||
// TODO: figure out how this code works
|
// TODO: figure out how this code works
|
||||||
if (isInstanceAkkoma()) sortStatusContext(mainStatus, result);
|
if (isInstanceAkkoma()) sortStatusContext(mainStatus, result);
|
||||||
|
|
||||||
filterStatuses(result.descendants);
|
result.descendants=filterStatuses(result.descendants);
|
||||||
filterStatuses(result.ancestors);
|
result.ancestors=filterStatuses(result.ancestors);
|
||||||
restoreStatusStates(result.descendants, oldData);
|
restoreStatusStates(result.descendants, oldData);
|
||||||
restoreStatusStates(result.ancestors, oldData);
|
restoreStatusStates(result.ancestors, oldData);
|
||||||
|
|
||||||
@@ -331,8 +325,11 @@ public class ThreadFragment extends StatusListFragment implements ProvidesAssist
|
|||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void filterStatuses(List<Status> statuses){
|
private List<Status> filterStatuses(List<Status> statuses){
|
||||||
AccountSessionManager.get(accountID).filterStatuses(statuses, getFilterContext());
|
StatusFilterPredicate statusFilterPredicate=new StatusFilterPredicate(accountID,getFilterContext());
|
||||||
|
return statuses.stream()
|
||||||
|
.filter(statusFilterPredicate)
|
||||||
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ public abstract class BaseAccountListFragment extends MastodonRecyclerFragment<A
|
|||||||
for(Relationship rel:result){
|
for(Relationship rel:result){
|
||||||
relationships.put(rel.id, rel);
|
relationships.put(rel.id, rel);
|
||||||
}
|
}
|
||||||
if(getActivity()==null) return;
|
if (getActivity() == null) return;
|
||||||
if(list==null)
|
if(list==null)
|
||||||
return;
|
return;
|
||||||
for(int i=0;i<list.getChildCount();i++){
|
for(int i=0;i<list.getChildCount();i++){
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
package org.joinmastodon.android.fragments.account_list;
|
|
||||||
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Bundle;
|
|
||||||
|
|
||||||
import org.joinmastodon.android.R;
|
|
||||||
import org.joinmastodon.android.api.requests.HeaderPaginationRequest;
|
|
||||||
import org.joinmastodon.android.api.requests.accounts.GetAccountBlocks;
|
|
||||||
import org.joinmastodon.android.model.Account;
|
|
||||||
import org.joinmastodon.android.ui.viewholders.AccountViewHolder;
|
|
||||||
|
|
||||||
public class BlockedAccountsListFragment extends AccountRelatedAccountListFragment{
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(Bundle savedInstanceState){
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
setTitle(R.string.sk_blocked_accounts);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public HeaderPaginationRequest<Account> onCreateRequest(String maxID, int count){
|
|
||||||
return new GetAccountBlocks(maxID, count);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onConfigureViewHolder(AccountViewHolder holder){
|
|
||||||
super.onConfigureViewHolder(holder);
|
|
||||||
holder.setStyle(AccountViewHolder.AccessoryType.NONE, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Uri getWebUri(Uri.Builder base) {
|
|
||||||
return super.getWebUri(base).buildUpon()
|
|
||||||
.appendPath("/blocks").build();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -48,7 +48,7 @@ public class ComposeAccountSearchFragment extends BaseAccountListFragment{
|
|||||||
@Override
|
@Override
|
||||||
protected void doLoadData(int offset, int count){
|
protected void doLoadData(int offset, int count){
|
||||||
refreshing=true;
|
refreshing=true;
|
||||||
currentRequest=new GetSearchResults(currentQuery, GetSearchResults.Type.ACCOUNTS, false, null, 0, 0)
|
currentRequest=new GetSearchResults(currentQuery, GetSearchResults.Type.ACCOUNTS, false)
|
||||||
.setCallback(new SimpleCallback<>(this){
|
.setCallback(new SimpleCallback<>(this){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(SearchResults result){
|
public void onSuccess(SearchResults result){
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
package org.joinmastodon.android.fragments.account_list;
|
|
||||||
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Bundle;
|
|
||||||
|
|
||||||
import org.joinmastodon.android.R;
|
|
||||||
import org.joinmastodon.android.api.requests.HeaderPaginationRequest;
|
|
||||||
import org.joinmastodon.android.api.requests.accounts.GetAccountMutes;
|
|
||||||
import org.joinmastodon.android.model.Account;
|
|
||||||
import org.joinmastodon.android.ui.viewholders.AccountViewHolder;
|
|
||||||
|
|
||||||
public class MutedAccountsListFragment extends AccountRelatedAccountListFragment{
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(Bundle savedInstanceState){
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
setTitle(R.string.sk_muted_accounts);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public HeaderPaginationRequest<Account> onCreateRequest(String maxID, int count){
|
|
||||||
return new GetAccountMutes(maxID, count);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onConfigureViewHolder(AccountViewHolder holder){
|
|
||||||
super.onConfigureViewHolder(holder);
|
|
||||||
holder.setStyle(AccountViewHolder.AccessoryType.NONE, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Uri getWebUri(Uri.Builder base) {
|
|
||||||
return super.getWebUri(base).buildUpon()
|
|
||||||
.appendPath("/mutes").build();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -133,7 +133,7 @@ public abstract class PaginatedAccountListFragment<T> extends BaseAccountListFra
|
|||||||
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
|
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
|
||||||
else
|
else
|
||||||
nextMaxID=null;
|
nextMaxID=null;
|
||||||
if(getActivity()==null) return;
|
if (getActivity() == null) return;
|
||||||
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)))
|
||||||
|
|||||||
@@ -6,19 +6,21 @@ import android.os.Bundle;
|
|||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
import org.joinmastodon.android.api.requests.timelines.GetBubbleTimeline;
|
import org.joinmastodon.android.api.requests.timelines.GetBubbleTimeline;
|
||||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
|
||||||
import org.joinmastodon.android.fragments.StatusListFragment;
|
import org.joinmastodon.android.fragments.StatusListFragment;
|
||||||
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.utils.DiscoverInfoBannerHelper;
|
import org.joinmastodon.android.ui.utils.DiscoverInfoBannerHelper;
|
||||||
|
import org.joinmastodon.android.utils.StatusFilterPredicate;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import me.grishka.appkit.api.SimpleCallback;
|
import me.grishka.appkit.api.SimpleCallback;
|
||||||
import me.grishka.appkit.utils.MergeRecyclerAdapter;
|
import me.grishka.appkit.utils.MergeRecyclerAdapter;
|
||||||
|
|
||||||
public class BubbleTimelineFragment extends StatusListFragment {
|
public class BubbleTimelineFragment extends StatusListFragment {
|
||||||
private DiscoverInfoBannerHelper bannerHelper;
|
private DiscoverInfoBannerHelper bannerHelper;
|
||||||
|
private String maxID;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState){
|
public void onCreate(Bundle savedInstanceState){
|
||||||
@@ -34,15 +36,15 @@ public class BubbleTimelineFragment extends StatusListFragment {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doLoadData(int offset, int count){
|
protected void doLoadData(int offset, int count){
|
||||||
currentRequest=new GetBubbleTimeline(getMaxID(), count, getLocalPrefs().timelineReplyVisibility)
|
currentRequest=new GetBubbleTimeline(refreshing ? null : maxID, count, getLocalPrefs().timelineReplyVisibility)
|
||||||
.setCallback(new SimpleCallback<>(this){
|
.setCallback(new SimpleCallback<>(this){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<Status> result){
|
public void onSuccess(List<Status> result){
|
||||||
if(getActivity()==null) return;
|
if(!result.isEmpty())
|
||||||
boolean more=applyMaxID(result);
|
maxID=result.get(result.size()-1).id;
|
||||||
AccountSessionManager.get(accountID).filterStatuses(result, getFilterContext());
|
if (getActivity() == null) return;
|
||||||
onDataLoaded(result, more);
|
result=result.stream().filter(new StatusFilterPredicate(accountID, getFilterContext())).collect(Collectors.toList());
|
||||||
bannerHelper.onBannerBecameVisible();
|
onDataLoaded(result, !result.isEmpty());
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.exec(accountID);
|
.exec(accountID);
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ import android.widget.TextView;
|
|||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.requests.accounts.GetAccountRelationships;
|
import org.joinmastodon.android.api.requests.accounts.GetAccountRelationships;
|
||||||
import org.joinmastodon.android.api.requests.accounts.GetFollowSuggestions;
|
import org.joinmastodon.android.api.requests.accounts.GetFollowSuggestions;
|
||||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
|
||||||
import org.joinmastodon.android.fragments.IsOnTop;
|
import org.joinmastodon.android.fragments.IsOnTop;
|
||||||
import org.joinmastodon.android.fragments.MastodonRecyclerFragment;
|
import org.joinmastodon.android.fragments.MastodonRecyclerFragment;
|
||||||
import org.joinmastodon.android.fragments.ProfileFragment;
|
import org.joinmastodon.android.fragments.ProfileFragment;
|
||||||
@@ -77,7 +76,7 @@ public class DiscoverAccountsFragment extends MastodonRecyclerFragment<DiscoverA
|
|||||||
.setCallback(new SimpleCallback<>(this){
|
.setCallback(new SimpleCallback<>(this){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<FollowSuggestion> result){
|
public void onSuccess(List<FollowSuggestion> result){
|
||||||
if(getActivity()==null) return;
|
if (getActivity() == null) return;
|
||||||
onDataLoaded(result.stream().map(fs->new AccountWrapper(fs.account)).collect(Collectors.toList()), false);
|
onDataLoaded(result.stream().map(fs->new AccountWrapper(fs.account)).collect(Collectors.toList()), false);
|
||||||
loadRelationships();
|
loadRelationships();
|
||||||
}
|
}
|
||||||
@@ -112,7 +111,7 @@ public class DiscoverAccountsFragment extends MastodonRecyclerFragment<DiscoverA
|
|||||||
public void onSuccess(List<Relationship> result){
|
public void onSuccess(List<Relationship> result){
|
||||||
relationshipsRequest=null;
|
relationshipsRequest=null;
|
||||||
relationships=result.stream().collect(Collectors.toMap(rel->rel.id, Function.identity()));
|
relationships=result.stream().collect(Collectors.toMap(rel->rel.id, Function.identity()));
|
||||||
if(getActivity()==null) return;
|
if (getActivity() == null) return;
|
||||||
if(list==null)
|
if(list==null)
|
||||||
return;
|
return;
|
||||||
for(int i=0;i<list.getChildCount();i++){
|
for(int i=0;i<list.getChildCount();i++){
|
||||||
@@ -320,9 +319,8 @@ public class DiscoverAccountsFragment extends MastodonRecyclerFragment<DiscoverA
|
|||||||
|
|
||||||
public AccountWrapper(Account account){
|
public AccountWrapper(Account account){
|
||||||
this.account=account;
|
this.account=account;
|
||||||
avaRequest=new UrlImageLoaderRequest(
|
if(!TextUtils.isEmpty(account.avatar))
|
||||||
TextUtils.isEmpty(account.avatar) ? AccountSessionManager.getInstance().getAccount(accountID).getDefaultAvatarUrl() : account.avatar,
|
avaRequest=new UrlImageLoaderRequest(account.avatar, V.dp(50), V.dp(50));
|
||||||
V.dp(50), V.dp(50));
|
|
||||||
if(!TextUtils.isEmpty(account.header))
|
if(!TextUtils.isEmpty(account.header))
|
||||||
coverRequest=new UrlImageLoaderRequest(account.header, 1000, 1000);
|
coverRequest=new UrlImageLoaderRequest(account.header, 1000, 1000);
|
||||||
parsedBio=HtmlParser.parse(account.note, account.emojis, Collections.emptyList(), Collections.emptyList(), accountID);
|
parsedBio=HtmlParser.parse(account.note, account.emojis, Collections.emptyList(), Collections.emptyList(), accountID);
|
||||||
|
|||||||
@@ -96,6 +96,8 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
|
|||||||
pager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback(){
|
pager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback(){
|
||||||
@Override
|
@Override
|
||||||
public void onPageSelected(int position){
|
public void onPageSelected(int position){
|
||||||
|
if(position==0)
|
||||||
|
return;
|
||||||
Fragment _page=getFragmentForPage(position);
|
Fragment _page=getFragmentForPage(position);
|
||||||
if(_page instanceof BaseRecyclerFragment<?> page){
|
if(_page instanceof BaseRecyclerFragment<?> page){
|
||||||
if(!page.loaded && !page.isDataLoading())
|
if(!page.loaded && !page.isDataLoading())
|
||||||
@@ -287,19 +289,15 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
|
|||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public SimpleViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
|
public SimpleViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
|
||||||
FrameLayout view=new FrameLayout(parent.getContext());
|
FrameLayout view=tabViews[viewType];
|
||||||
|
((ViewGroup)view.getParent()).removeView(view);
|
||||||
|
view.setVisibility(View.VISIBLE);
|
||||||
view.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
view.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||||
return new SimpleViewHolder(view);
|
return new SimpleViewHolder(view);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBindViewHolder(@NonNull SimpleViewHolder holder, int position){
|
public void onBindViewHolder(@NonNull SimpleViewHolder holder, int position){}
|
||||||
FrameLayout view=tabViews[position];
|
|
||||||
if(view.getParent() instanceof ViewGroup parent)
|
|
||||||
parent.removeView(view);
|
|
||||||
view.setVisibility(View.VISIBLE);
|
|
||||||
((FrameLayout)holder.itemView).addView(view, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getItemCount(){
|
public int getItemCount(){
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import android.net.Uri;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
|
||||||
import org.joinmastodon.android.api.requests.trends.GetTrendingStatuses;
|
import org.joinmastodon.android.api.requests.trends.GetTrendingStatuses;
|
||||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
|
||||||
import org.joinmastodon.android.fragments.StatusListFragment;
|
import org.joinmastodon.android.fragments.StatusListFragment;
|
||||||
import org.joinmastodon.android.model.FilterContext;
|
import org.joinmastodon.android.model.FilterContext;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
@@ -18,7 +17,6 @@ import me.grishka.appkit.utils.MergeRecyclerAdapter;
|
|||||||
|
|
||||||
public class DiscoverPostsFragment extends StatusListFragment{
|
public class DiscoverPostsFragment extends StatusListFragment{
|
||||||
private DiscoverInfoBannerHelper bannerHelper;
|
private DiscoverInfoBannerHelper bannerHelper;
|
||||||
private int offset;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState){
|
public void onCreate(Bundle savedInstanceState){
|
||||||
@@ -27,17 +25,12 @@ public class DiscoverPostsFragment extends StatusListFragment{
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doLoadData(int o, int count){
|
protected void doLoadData(int offset, int count){
|
||||||
if(refreshing) offset=0;
|
|
||||||
currentRequest=new GetTrendingStatuses(offset, count)
|
currentRequest=new GetTrendingStatuses(offset, count)
|
||||||
.setCallback(new SimpleCallback<>(this){
|
.setCallback(new SimpleCallback<>(this){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<Status> result){
|
public void onSuccess(List<Status> result){
|
||||||
if(getActivity()==null) return;
|
onDataLoaded(result, !result.isEmpty());
|
||||||
boolean empty=result.isEmpty();
|
|
||||||
offset+=result.size();
|
|
||||||
AccountSessionManager.get(accountID).filterStatuses(result, getFilterContext());
|
|
||||||
onDataLoaded(result, !empty);
|
|
||||||
bannerHelper.onBannerBecameVisible();
|
bannerHelper.onBannerBecameVisible();
|
||||||
}
|
}
|
||||||
}).exec(accountID);
|
}).exec(accountID);
|
||||||
|
|||||||
@@ -29,14 +29,15 @@ public class FederatedTimelineFragment extends StatusListFragment{
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doLoadData(int offset, int count){
|
protected void doLoadData(int offset, int count){
|
||||||
currentRequest=new GetPublicTimeline(false, false, getMaxID(), count, getLocalPrefs().timelineReplyVisibility)
|
currentRequest=new GetPublicTimeline(false, false, refreshing ? null : maxID, count, getLocalPrefs().timelineReplyVisibility)
|
||||||
.setCallback(new SimpleCallback<>(this){
|
.setCallback(new SimpleCallback<>(this){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<Status> result){
|
public void onSuccess(List<Status> result){
|
||||||
if(getActivity()==null) return;
|
if(!result.isEmpty())
|
||||||
boolean more=applyMaxID(result);
|
maxID=result.get(result.size()-1).id;
|
||||||
AccountSessionManager.get(accountID).filterStatuses(result, getFilterContext());
|
boolean empty=result.isEmpty();
|
||||||
onDataLoaded(result, more);
|
AccountSessionManager.get(accountID).filterStatuses(result, FilterContext.PUBLIC);
|
||||||
|
onDataLoaded(result, !empty);
|
||||||
bannerHelper.onBannerBecameVisible();
|
bannerHelper.onBannerBecameVisible();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package org.joinmastodon.android.fragments.discover;
|
|||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
import org.joinmastodon.android.api.requests.timelines.GetPublicTimeline;
|
import org.joinmastodon.android.api.requests.timelines.GetPublicTimeline;
|
||||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||||
@@ -29,14 +30,15 @@ public class LocalTimelineFragment extends StatusListFragment{
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doLoadData(int offset, int count){
|
protected void doLoadData(int offset, int count){
|
||||||
currentRequest=new GetPublicTimeline(true, false, getMaxID(), count, getLocalPrefs().timelineReplyVisibility)
|
currentRequest=new GetPublicTimeline(true, false, refreshing ? null : maxID, count, getLocalPrefs().timelineReplyVisibility)
|
||||||
.setCallback(new SimpleCallback<>(this){
|
.setCallback(new SimpleCallback<>(this){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<Status> result){
|
public void onSuccess(List<Status> result){
|
||||||
if(getActivity()==null) return;
|
if(!result.isEmpty())
|
||||||
boolean more=applyMaxID(result);
|
maxID=result.get(result.size()-1).id;
|
||||||
AccountSessionManager.get(accountID).filterStatuses(result, getFilterContext());
|
boolean empty=result.isEmpty();
|
||||||
onDataLoaded(result, more);
|
AccountSessionManager.get(accountID).filterStatuses(result, FilterContext.PUBLIC);
|
||||||
|
onDataLoaded(result, !empty);
|
||||||
bannerHelper.onBannerBecameVisible();
|
bannerHelper.onBannerBecameVisible();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import org.joinmastodon.android.model.Status;
|
|||||||
import org.joinmastodon.android.ui.displayitems.AccountStatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.AccountStatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.displayitems.HashtagStatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.HashtagStatusDisplayItem;
|
||||||
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
|
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
|
||||||
|
import org.joinmastodon.android.ui.tabs.TabLayout;
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
import org.parceler.Parcels;
|
import org.parceler.Parcels;
|
||||||
|
|
||||||
@@ -30,7 +31,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Objects;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import me.grishka.appkit.Nav;
|
import me.grishka.appkit.Nav;
|
||||||
@@ -96,7 +97,7 @@ public class SearchFragment extends BaseStatusListFragment<SearchResult>{
|
|||||||
args.putParcelable("profileAccount", Parcels.wrap(res.account));
|
args.putParcelable("profileAccount", Parcels.wrap(res.account));
|
||||||
Nav.go(getActivity(), ProfileFragment.class, args);
|
Nav.go(getActivity(), ProfileFragment.class, args);
|
||||||
}
|
}
|
||||||
case HASHTAG -> UiUtils.openHashtagTimeline(getActivity(), accountID, res.hashtag);
|
case HASHTAG -> UiUtils.openHashtagTimeline(getActivity(), accountID, res.hashtag.name, res.hashtag.following);
|
||||||
case STATUS -> {
|
case STATUS -> {
|
||||||
Status status=res.status.getContentStatus();
|
Status status=res.status.getContentStatus();
|
||||||
Bundle args=new Bundle();
|
Bundle args=new Bundle();
|
||||||
@@ -112,7 +113,7 @@ public class SearchFragment extends BaseStatusListFragment<SearchResult>{
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doLoadData(int _offset, int count){
|
protected void doLoadData(int offset, int count){
|
||||||
GetSearchResults.Type type;
|
GetSearchResults.Type type;
|
||||||
if(currentFilter.size()==1){
|
if(currentFilter.size()==1){
|
||||||
type=switch(currentFilter.iterator().next()){
|
type=switch(currentFilter.iterator().next()){
|
||||||
@@ -127,21 +128,7 @@ public class SearchFragment extends BaseStatusListFragment<SearchResult>{
|
|||||||
dataLoaded();
|
dataLoaded();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String maxID=null;
|
currentRequest=new GetSearchResults(currentQuery, type, true)
|
||||||
// TODO server-side bug
|
|
||||||
/*int offset=0;
|
|
||||||
if(_offset>0){
|
|
||||||
if(type==GetSearchResults.Type.STATUSES){
|
|
||||||
if(!preloadedData.isEmpty())
|
|
||||||
maxID=preloadedData.get(preloadedData.size()-1).status.id;
|
|
||||||
else if(!data.isEmpty())
|
|
||||||
maxID=data.get(data.size()-1).status.id;
|
|
||||||
}else{
|
|
||||||
offset=_offset;
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
int offset=_offset;
|
|
||||||
currentRequest=new GetSearchResults(currentQuery, type, type==null, maxID, offset, type==null ? 0 : count)
|
|
||||||
.setCallback(new Callback<>(){
|
.setCallback(new Callback<>(){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(SearchResults result){
|
public void onSuccess(SearchResults result){
|
||||||
@@ -155,15 +142,12 @@ public class SearchFragment extends BaseStatusListFragment<SearchResult>{
|
|||||||
results.add(new SearchResult(tag));
|
results.add(new SearchResult(tag));
|
||||||
}
|
}
|
||||||
if(result.statuses!=null){
|
if(result.statuses!=null){
|
||||||
Set<String> alreadyLoadedStatuses=data.stream().filter(r->r.type==SearchResult.Type.STATUS).map(r->r.status.id).collect(Collectors.toSet());
|
for(Status status:result.statuses)
|
||||||
for(Status status:result.statuses){
|
results.add(new SearchResult(status));
|
||||||
if(!alreadyLoadedStatuses.contains(status.id))
|
|
||||||
results.add(new SearchResult(status));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
prevDisplayItems=new ArrayList<>(displayItems);
|
prevDisplayItems=new ArrayList<>(displayItems);
|
||||||
unfilteredResults=results;
|
unfilteredResults=results;
|
||||||
onDataLoaded(filterSearchResults(results), type!=null && !results.isEmpty());
|
onDataLoaded(filterSearchResults(results), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import android.graphics.Outline;
|
|||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.graphics.drawable.LayerDrawable;
|
import android.graphics.drawable.LayerDrawable;
|
||||||
|
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;
|
||||||
@@ -25,7 +26,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.MainActivity;
|
||||||
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;
|
||||||
@@ -37,16 +38,19 @@ import org.joinmastodon.android.model.SearchResults;
|
|||||||
import org.joinmastodon.android.model.viewmodel.ListItem;
|
import org.joinmastodon.android.model.viewmodel.ListItem;
|
||||||
import org.joinmastodon.android.model.viewmodel.SearchResultViewModel;
|
import org.joinmastodon.android.model.viewmodel.SearchResultViewModel;
|
||||||
import org.joinmastodon.android.ui.DividerItemDecoration;
|
import org.joinmastodon.android.ui.DividerItemDecoration;
|
||||||
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
|
|
||||||
import org.joinmastodon.android.ui.SearchViewHelper;
|
import org.joinmastodon.android.ui.SearchViewHelper;
|
||||||
import org.joinmastodon.android.ui.adapters.GenericListItemsAdapter;
|
import org.joinmastodon.android.ui.adapters.GenericListItemsAdapter;
|
||||||
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.joinmastodon.android.ui.viewholders.AccountViewHolder;
|
import org.joinmastodon.android.ui.viewholders.AccountViewHolder;
|
||||||
import org.joinmastodon.android.ui.viewholders.SimpleListItemViewHolder;
|
import org.joinmastodon.android.ui.viewholders.SimpleListItemViewHolder;
|
||||||
|
import org.parceler.Parcels;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
@@ -122,7 +126,7 @@ public class SearchQueryFragment extends MastodonRecyclerFragment<SearchResultVi
|
|||||||
recentsHeader.setVisible(!data.isEmpty());
|
recentsHeader.setVisible(!data.isEmpty());
|
||||||
});
|
});
|
||||||
}else{
|
}else{
|
||||||
currentRequest=new GetSearchResults(currentQuery, null, false, null, 0, 0)
|
currentRequest=new GetSearchResults(currentQuery, null, false)
|
||||||
.limit(2)
|
.limit(2)
|
||||||
.setCallback(new SimpleCallback<>(this){
|
.setCallback(new SimpleCallback<>(this){
|
||||||
@Override
|
@Override
|
||||||
@@ -377,56 +381,16 @@ public class SearchQueryFragment extends MastodonRecyclerFragment<SearchResultVi
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void openHashtag(SearchResult res){
|
private void openHashtag(SearchResult res){
|
||||||
wrapSuicideDialog(()->{
|
UiUtils.openHashtagTimeline(getActivity(), accountID, res.hashtag.name, res.hashtag.following);
|
||||||
UiUtils.openHashtagTimeline(getActivity(), accountID, res.hashtag);
|
AccountSessionManager.getInstance().getAccount(accountID).getCacheController().putRecentSearch(res);
|
||||||
AccountSessionManager.getInstance().getAccount(accountID).getCacheController().putRecentSearch(res);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isInRecentMode(){
|
private boolean isInRecentMode(){
|
||||||
return TextUtils.isEmpty(currentQuery);
|
return TextUtils.isEmpty(currentQuery);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void wrapSuicideDialog(Runnable r){
|
|
||||||
if(!GlobalUserPreferences.showSuicideHelp || currentQuery==null){
|
|
||||||
r.run();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String[] terms=getContext().getString(R.string.sk_suicide_search_terms).toLowerCase().split(",");
|
|
||||||
String query=currentQuery.trim().toLowerCase();
|
|
||||||
boolean termMatches=false;
|
|
||||||
for(String term : terms){
|
|
||||||
if(query.contains(term)){
|
|
||||||
termMatches=true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!termMatches){
|
|
||||||
r.run();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String url=getContext().getString(R.string.sk_suicide_helplines_url);
|
|
||||||
new M3AlertDialogBuilder(getActivity())
|
|
||||||
.setTitle(R.string.sk_search_suicide_title)
|
|
||||||
.setMessage(R.string.sk_search_suicide_message)
|
|
||||||
.setNegativeButton(R.string.sk_do_not_show_again, (dialog, which)->{
|
|
||||||
GlobalUserPreferences.showSuicideHelp = false;
|
|
||||||
GlobalUserPreferences.save();
|
|
||||||
r.run();
|
|
||||||
})
|
|
||||||
.setNeutralButton(R.string.sk_search_suicide_hotlines, (dialog, which)->UiUtils.launchWebBrowser(getContext(), url))
|
|
||||||
.setPositiveButton(R.string.ok, (dialog, which)->r.run())
|
|
||||||
.setOnDismissListener((dialog)->{})
|
|
||||||
.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onSearchViewEnter(){
|
private void onSearchViewEnter(){
|
||||||
if(TextUtils.isEmpty(currentQuery) || currentQuery.trim().isEmpty())
|
deliverResult(currentQuery, null);
|
||||||
return;
|
|
||||||
wrapSuicideDialog(()->deliverResult(currentQuery, null));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onOpenURLClick(){
|
private void onOpenURLClick(){
|
||||||
@@ -434,12 +398,10 @@ public class SearchQueryFragment extends MastodonRecyclerFragment<SearchResultVi
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void onGoToHashtagClick(){
|
private void onGoToHashtagClick(){
|
||||||
wrapSuicideDialog(()->{
|
String q=searchViewHelper.getQuery();
|
||||||
String q=searchViewHelper.getQuery();
|
if(q.startsWith("#"))
|
||||||
if(q.startsWith("#"))
|
q=q.substring(1);
|
||||||
q=q.substring(1);
|
UiUtils.openHashtagTimeline(getActivity(), accountID, q, null);
|
||||||
UiUtils.openHashtagTimeline(getActivity(), accountID, q);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onGoToAccountClick(){
|
private void onGoToAccountClick(){
|
||||||
@@ -460,11 +422,11 @@ public class SearchQueryFragment extends MastodonRecyclerFragment<SearchResultVi
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void onGoToStatusSearchClick(){
|
private void onGoToStatusSearchClick(){
|
||||||
wrapSuicideDialog(()->deliverResult(searchViewHelper.getQuery(), SearchResult.Type.STATUS));
|
deliverResult(searchViewHelper.getQuery(), SearchResult.Type.STATUS);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onGoToAccountSearchClick(){
|
private void onGoToAccountSearchClick(){
|
||||||
wrapSuicideDialog(()->deliverResult(searchViewHelper.getQuery(), SearchResult.Type.ACCOUNT));
|
deliverResult(searchViewHelper.getQuery(), SearchResult.Type.ACCOUNT);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onClearRecentClick(){
|
private void onClearRecentClick(){
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ public class TrendingHashtagsFragment extends BaseRecyclerFragment<Hashtag> impl
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClick(){
|
public void onClick(){
|
||||||
UiUtils.openHashtagTimeline(getActivity(), accountID, item);
|
UiUtils.openHashtagTimeline(getActivity(), accountID, item.name, item.following);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -165,7 +165,9 @@ public class AccountActivationFragment extends ToolbarFragment{
|
|||||||
private void tryGetAccount(){
|
private void tryGetAccount(){
|
||||||
if(AccountSessionManager.getInstance().tryGetAccount(accountID)==null){
|
if(AccountSessionManager.getInstance().tryGetAccount(accountID)==null){
|
||||||
uiHandler.removeCallbacks(pollRunnable);
|
uiHandler.removeCallbacks(pollRunnable);
|
||||||
((MainActivity)getActivity()).restartHomeFragment();
|
getActivity().finish();
|
||||||
|
Intent intent=new Intent(getActivity(), MainActivity.class);
|
||||||
|
startActivity(intent);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
currentRequest=new GetOwnAccount()
|
currentRequest=new GetOwnAccount()
|
||||||
|
|||||||
@@ -271,7 +271,7 @@ public class SignupFragment extends ToolbarFragment{
|
|||||||
@Override
|
@Override
|
||||||
public void tail(Node node, int depth){
|
public void tail(Node node, int depth){
|
||||||
if(node instanceof Element){
|
if(node instanceof Element){
|
||||||
ssb.setSpan(new LinkSpan("", SignupFragment.this::onGoBackLinkClick, LinkSpan.Type.CUSTOM, null, null, null), spanStart, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
ssb.setSpan(new LinkSpan("", SignupFragment.this::onGoBackLinkClick, LinkSpan.Type.CUSTOM, null), spanStart, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
ssb.setSpan(new TypefaceSpan("sans-serif-medium"), spanStart, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
ssb.setSpan(new TypefaceSpan("sans-serif-medium"), spanStart, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,11 +83,10 @@ public class ReportAddPostsChoiceFragment extends StatusListFragment{
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doLoadData(int offset, int count){
|
protected void doLoadData(int offset, int count){
|
||||||
currentRequest=new GetAccountStatuses(reportAccount.id, getMaxID(), null, count, GetAccountStatuses.Filter.OWN_POSTS_AND_REPLIES)
|
currentRequest=new GetAccountStatuses(reportAccount.id, offset>0 ? getMaxID() : null, null, count, GetAccountStatuses.Filter.OWN_POSTS_AND_REPLIES)
|
||||||
.setCallback(new SimpleCallback<>(this){
|
.setCallback(new SimpleCallback<>(this){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(List<Status> result){
|
public void onSuccess(List<Status> result){
|
||||||
if(getActivity()==null) return;
|
|
||||||
for(Status s:result){
|
for(Status s:result){
|
||||||
s.sensitive=true;
|
s.sensitive=true;
|
||||||
}
|
}
|
||||||
@@ -104,8 +103,8 @@ public class ReportAddPostsChoiceFragment extends StatusListFragment{
|
|||||||
else
|
else
|
||||||
selectedIDs.add(id);
|
selectedIDs.add(id);
|
||||||
CheckableHeaderStatusDisplayItem.Holder holder=findHolderOfType(id, CheckableHeaderStatusDisplayItem.Holder.class);
|
CheckableHeaderStatusDisplayItem.Holder holder=findHolderOfType(id, CheckableHeaderStatusDisplayItem.Holder.class);
|
||||||
if(holder!=null) holder.rebind();
|
if(holder!=null)
|
||||||
else notifyItemChanged(id, CheckableHeaderStatusDisplayItem.class);
|
holder.rebind();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ public class ReportReasonChoiceFragment extends StatusListFragment{
|
|||||||
reportStatus=Parcels.unwrap(getArguments().getParcelable("status"));
|
reportStatus=Parcels.unwrap(getArguments().getParcelable("status"));
|
||||||
if(reportStatus!=null){
|
if(reportStatus!=null){
|
||||||
Status hiddenStatus=reportStatus.clone();
|
Status hiddenStatus=reportStatus.clone();
|
||||||
if(hiddenStatus.spoilerText==null) hiddenStatus.spoilerText=getString(R.string.post_hidden);
|
hiddenStatus.spoilerText=getString(R.string.post_hidden);
|
||||||
onDataLoaded(Collections.singletonList(hiddenStatus));
|
onDataLoaded(Collections.singletonList(hiddenStatus));
|
||||||
setTitle(R.string.report_title_post);
|
setTitle(R.string.report_title_post);
|
||||||
}else{
|
}else{
|
||||||
@@ -168,6 +168,17 @@ public class ReportReasonChoiceFragment extends StatusListFragment{
|
|||||||
((UsableRecyclerView)list).setIncludeMarginsInItemHitbox(false);
|
((UsableRecyclerView)list).setIncludeMarginsInItemHitbox(false);
|
||||||
|
|
||||||
if(reportStatus!=null){
|
if(reportStatus!=null){
|
||||||
|
list.addItemDecoration(new RecyclerView.ItemDecoration(){
|
||||||
|
@Override
|
||||||
|
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state){
|
||||||
|
RecyclerView.ViewHolder holder=parent.getChildViewHolder(view);
|
||||||
|
if(holder instanceof LinkCardStatusDisplayItem.Holder || holder instanceof MediaGridStatusDisplayItem.Holder){
|
||||||
|
outRect.left=V.dp(16);
|
||||||
|
outRect.right=V.dp(16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
list.addItemDecoration(new RecyclerView.ItemDecoration(){
|
list.addItemDecoration(new RecyclerView.ItemDecoration(){
|
||||||
private Paint paint=new Paint(Paint.ANTI_ALIAS_FLAG);
|
private Paint paint=new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
{
|
{
|
||||||
@@ -211,6 +222,10 @@ public class ReportReasonChoiceFragment extends StatusListFragment{
|
|||||||
if(holder instanceof StatusDisplayItem.Holder<?>){
|
if(holder instanceof StatusDisplayItem.Holder<?>){
|
||||||
outRect.left=outRect.right=V.dp(16);
|
outRect.left=outRect.right=V.dp(16);
|
||||||
}
|
}
|
||||||
|
int index=holder.getAbsoluteAdapterPosition()-mergeAdapter.getPositionForAdapter(adapter);
|
||||||
|
if(index==displayItems.size()){
|
||||||
|
outRect.top=V.dp(32);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -236,6 +251,18 @@ public class ReportReasonChoiceFragment extends StatusListFragment{
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onModifyItemViewHolder(BindableViewHolder<StatusDisplayItem> holder){
|
||||||
|
if((Object)holder instanceof MediaGridStatusDisplayItem.Holder h){
|
||||||
|
View layout=h.getLayout();
|
||||||
|
layout.setOutlineProvider(OutlineProviders.roundedRect(8));
|
||||||
|
layout.setClipToOutline(true);
|
||||||
|
View overlay=h.getSensitiveOverlay();
|
||||||
|
overlay.setOutlineProvider(OutlineProviders.roundedRect(8));
|
||||||
|
overlay.setClipToOutline(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void putRelationship(String id, Relationship rel){
|
public void putRelationship(String id, Relationship rel){
|
||||||
super.putRelationship(id, rel);
|
super.putRelationship(id, rel);
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ public class FilterWordsFragment extends BaseSettingsFragment<FilterKeyword> imp
|
|||||||
public void onViewCreated(View view, Bundle savedInstanceState){
|
public void onViewCreated(View view, Bundle savedInstanceState){
|
||||||
super.onViewCreated(view, savedInstanceState);
|
super.onViewCreated(view, savedInstanceState);
|
||||||
fab=view.findViewById(R.id.fab);
|
fab=view.findViewById(R.id.fab);
|
||||||
fab.setImageResource(R.drawable.ic_fluent_add_24_regular);
|
fab.setImageResource(R.drawable.ic_add_24px);
|
||||||
fab.setContentDescription(getString(R.string.add_muted_word));
|
fab.setContentDescription(getString(R.string.add_muted_word));
|
||||||
fab.setOnClickListener(v->onFabClick());
|
fab.setOnClickListener(v->onFabClick());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,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.openHashtagTimeline(getActivity(), accountID, getString(R.string.donate_hashtag))),
|
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),
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import org.joinmastodon.android.utils.MastodonLanguage;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.IntStream;
|
import java.util.stream.IntStream;
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
public class SettingsBehaviorFragment extends BaseSettingsFragment<Void> implements HasAccountID{
|
public class SettingsBehaviorFragment extends BaseSettingsFragment<Void> implements HasAccountID{
|
||||||
private ListItem<Void> languageItem;
|
private ListItem<Void> languageItem;
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package org.joinmastodon.android.fragments.settings;
|
package org.joinmastodon.android.fragments.settings;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.AlertDialog;
|
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
@@ -18,7 +17,6 @@ import org.joinmastodon.android.GlobalUserPreferences;
|
|||||||
import org.joinmastodon.android.MastodonApp;
|
import org.joinmastodon.android.MastodonApp;
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.session.AccountLocalPreferences;
|
import org.joinmastodon.android.api.session.AccountLocalPreferences;
|
||||||
import org.joinmastodon.android.api.session.AccountLocalPreferences.ColorPreference;
|
|
||||||
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.events.StatusDisplaySettingsChangedEvent;
|
||||||
@@ -29,8 +27,6 @@ import org.joinmastodon.android.ui.views.TextInputFrameLayout;
|
|||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Consumer;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import java.util.stream.IntStream;
|
import java.util.stream.IntStream;
|
||||||
|
|
||||||
import me.grishka.appkit.FragmentStackActivity;
|
import me.grishka.appkit.FragmentStackActivity;
|
||||||
@@ -41,7 +37,7 @@ 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, showNavigationLabelsItem, likeIconItem, underlinedLinksItem;
|
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 CheckableListItem<Void> pronounsInUserListingsItem, pronounsInTimelinesItem, pronounsInThreadsItem;
|
||||||
|
|
||||||
@@ -55,7 +51,7 @@ public class SettingsDisplayFragment extends BaseSettingsFragment<Void>{
|
|||||||
lp=s.getLocalPreferences();
|
lp=s.getLocalPreferences();
|
||||||
onDataLoaded(List.of(
|
onDataLoaded(List.of(
|
||||||
themeItem=new ListItem<>(R.string.settings_theme, getAppearanceValue(), R.drawable.ic_fluent_weather_moon_24_regular, this::onAppearanceClick),
|
themeItem=new ListItem<>(R.string.settings_theme, getAppearanceValue(), R.drawable.ic_fluent_weather_moon_24_regular, this::onAppearanceClick),
|
||||||
colorItem=new ListItem<>(getString(R.string.sk_settings_color_palette), getColorPaletteValue(), R.drawable.ic_fluent_color_24_regular, this::onColorClick),
|
colorItem=new ListItem<>(R.string.sk_settings_color_palette, getColorPaletteValue(), R.drawable.ic_fluent_color_24_regular, this::onColorClick),
|
||||||
trueBlackModeItem=new CheckableListItem<>(R.string.sk_settings_true_black, 0, CheckableListItem.Style.SWITCH, GlobalUserPreferences.trueBlackTheme, R.drawable.ic_fluent_dark_theme_24_regular, this::onTrueBlackModeClick, true),
|
trueBlackModeItem=new CheckableListItem<>(R.string.sk_settings_true_black, 0, CheckableListItem.Style.SWITCH, GlobalUserPreferences.trueBlackTheme, R.drawable.ic_fluent_dark_theme_24_regular, this::onTrueBlackModeClick, true),
|
||||||
publishTextItem=new ListItem<>(getString(R.string.sk_settings_publish_button_text), getPublishButtonText(), R.drawable.ic_fluent_send_24_regular, this::onPublishTextClick),
|
publishTextItem=new ListItem<>(getString(R.string.sk_settings_publish_button_text), getPublishButtonText(), R.drawable.ic_fluent_send_24_regular, this::onPublishTextClick),
|
||||||
autoRevealCWsItem=new ListItem<>(R.string.sk_settings_auto_reveal_equal_spoilers, getAutoRevealSpoilersText(), R.drawable.ic_fluent_eye_24_regular, this::onAutoRevealSpoilersClick),
|
autoRevealCWsItem=new ListItem<>(R.string.sk_settings_auto_reveal_equal_spoilers, getAutoRevealSpoilersText(), R.drawable.ic_fluent_eye_24_regular, this::onAutoRevealSpoilersClick),
|
||||||
@@ -72,8 +68,6 @@ 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)),
|
||||||
likeIconItem=new CheckableListItem<>(R.string.sk_settings_like_icon, 0, CheckableListItem.Style.SWITCH, GlobalUserPreferences.likeIcon, R.drawable.ic_fluent_heart_24_regular, ()->toggleCheckableItem(likeIconItem)),
|
|
||||||
underlinedLinksItem=new CheckableListItem<>(R.string.sk_settings_underlined_links, 0, CheckableListItem.Style.SWITCH, GlobalUserPreferences.underlinedLinks, R.drawable.ic_fluent_text_underline_24_regular, ()->toggleCheckableItem(underlinedLinksItem)),
|
|
||||||
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),
|
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)),
|
pronounsInTimelinesItem=new CheckableListItem<>(R.string.sk_settings_display_pronouns_in_timelines, 0, CheckableListItem.Style.CHECKBOX, GlobalUserPreferences.displayPronounsInTimelines, 0, ()->toggleCheckableItem(pronounsInTimelinesItem)),
|
||||||
@@ -100,9 +94,9 @@ public class SettingsDisplayFragment extends BaseSettingsFragment<Void>{
|
|||||||
protected void onHidden(){
|
protected void onHidden(){
|
||||||
super.onHidden();
|
super.onHidden();
|
||||||
|
|
||||||
boolean restartPlease=GlobalUserPreferences.disableM3PillActiveIndicator!=disablePillItem.checked
|
boolean restartPlease=
|
||||||
|| GlobalUserPreferences.showNavigationLabels!=showNavigationLabelsItem.checked
|
GlobalUserPreferences.disableM3PillActiveIndicator!=disablePillItem.checked ||
|
||||||
|| GlobalUserPreferences.likeIcon!=likeIconItem.checked;
|
GlobalUserPreferences.showNavigationLabels!=showNavigationLabelsItem.checked;
|
||||||
|
|
||||||
lp.revealCWs=revealCWsItem.checked;
|
lp.revealCWs=revealCWsItem.checked;
|
||||||
lp.hideSensitiveMedia=hideSensitiveMediaItem.checked;
|
lp.hideSensitiveMedia=hideSensitiveMediaItem.checked;
|
||||||
@@ -118,8 +112,6 @@ public class SettingsDisplayFragment extends BaseSettingsFragment<Void>{
|
|||||||
GlobalUserPreferences.spectatorMode=spectatorModeItem.checked;
|
GlobalUserPreferences.spectatorMode=spectatorModeItem.checked;
|
||||||
GlobalUserPreferences.autoHideFab=hideFabItem.checked;
|
GlobalUserPreferences.autoHideFab=hideFabItem.checked;
|
||||||
GlobalUserPreferences.translateButtonOpenedOnly=translateOpenedItem.checked;
|
GlobalUserPreferences.translateButtonOpenedOnly=translateOpenedItem.checked;
|
||||||
GlobalUserPreferences.likeIcon=likeIconItem.checked;
|
|
||||||
GlobalUserPreferences.underlinedLinks=underlinedLinksItem.checked;
|
|
||||||
GlobalUserPreferences.disableM3PillActiveIndicator=disablePillItem.checked;
|
GlobalUserPreferences.disableM3PillActiveIndicator=disablePillItem.checked;
|
||||||
GlobalUserPreferences.showNavigationLabels=showNavigationLabelsItem.checked;
|
GlobalUserPreferences.showNavigationLabels=showNavigationLabelsItem.checked;
|
||||||
GlobalUserPreferences.displayPronounsInTimelines=pronounsInTimelinesItem.checked;
|
GlobalUserPreferences.displayPronounsInTimelines=pronounsInTimelinesItem.checked;
|
||||||
@@ -138,11 +130,17 @@ public class SettingsDisplayFragment extends BaseSettingsFragment<Void>{
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getColorPaletteValue(){
|
private @StringRes int getColorPaletteValue(){
|
||||||
ColorPreference color=AccountSessionManager.get(accountID).getLocalPreferences().color;
|
return switch(GlobalUserPreferences.color){
|
||||||
return color==null
|
case MATERIAL3 -> R.string.sk_color_palette_material3;
|
||||||
? getString(R.string.sk_settings_color_palette_default, getString(GlobalUserPreferences.color.getName()))
|
case PINK -> R.string.sk_color_palette_pink;
|
||||||
: getString(color.getName());
|
case PURPLE -> R.string.sk_color_palette_purple;
|
||||||
|
case GREEN -> R.string.sk_color_palette_green;
|
||||||
|
case BLUE -> R.string.sk_color_palette_blue;
|
||||||
|
case BROWN -> R.string.sk_color_palette_brown;
|
||||||
|
case RED -> R.string.sk_color_palette_red;
|
||||||
|
case YELLOW -> R.string.sk_color_palette_yellow;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getPublishButtonText() {
|
private String getPublishButtonText() {
|
||||||
@@ -198,40 +196,26 @@ public class SettingsDisplayFragment extends BaseSettingsFragment<Void>{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void onColorClick(){
|
private void onColorClick(){
|
||||||
boolean multiple=AccountSessionManager.getInstance().getLoggedInAccounts().size() > 1;
|
int selected=GlobalUserPreferences.color.ordinal();
|
||||||
int indexOffset=multiple ? 1 : 0;
|
|
||||||
int selected=lp.color==null ? 0 : lp.color.ordinal() + indexOffset;
|
|
||||||
int[] newSelected={selected};
|
int[] newSelected={selected};
|
||||||
List<String> items=Arrays.stream(ColorPreference.values()).map(ColorPreference::getName).map(this::getString).collect(Collectors.toList());
|
String[] names=Arrays.stream(GlobalUserPreferences.ColorPreference.values()).map(GlobalUserPreferences.ColorPreference::getName).map(this::getString).toArray(String[]::new);
|
||||||
if(multiple)
|
new M3AlertDialogBuilder(getActivity())
|
||||||
items.add(0, getString(R.string.sk_settings_color_palette_default, items.get(GlobalUserPreferences.color.ordinal())));
|
.setTitle(R.string.settings_theme)
|
||||||
|
.setSingleChoiceItems(names,
|
||||||
Consumer<Boolean> save=(asDefault)->{
|
|
||||||
boolean defaultSelected=multiple && newSelected[0]==0;
|
|
||||||
ColorPreference pref=defaultSelected ? null : ColorPreference.values()[newSelected[0]-indexOffset];
|
|
||||||
if(pref!=lp.color){
|
|
||||||
ColorPreference prev=lp.color;
|
|
||||||
lp.color=asDefault ? null : pref;
|
|
||||||
lp.save();
|
|
||||||
if((asDefault || !multiple) && pref!=null){
|
|
||||||
GlobalUserPreferences.color=pref;
|
|
||||||
GlobalUserPreferences.save();
|
|
||||||
}
|
|
||||||
colorItem.subtitle=getColorPaletteValue();
|
|
||||||
rebindItem(colorItem);
|
|
||||||
if(prev==null && pref!=null) restartActivityToApplyNewTheme();
|
|
||||||
else maybeApplyNewThemeRightNow(null, prev, null);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
AlertDialog.Builder alert=new M3AlertDialogBuilder(getActivity())
|
|
||||||
.setTitle(R.string.sk_settings_color_palette)
|
|
||||||
.setSingleChoiceItems(items.toArray(String[]::new),
|
|
||||||
selected, (dlg, item)->newSelected[0]=item)
|
selected, (dlg, item)->newSelected[0]=item)
|
||||||
.setPositiveButton(R.string.ok, (dlg, item)->save.accept(false))
|
.setPositiveButton(R.string.ok, (dlg, item)->{
|
||||||
.setNegativeButton(R.string.cancel, null);
|
GlobalUserPreferences.ColorPreference pref=GlobalUserPreferences.ColorPreference.values()[newSelected[0]];
|
||||||
if(multiple) alert.setNeutralButton(R.string.sk_set_as_default, (dlg, item)->save.accept(true));
|
if(pref!=GlobalUserPreferences.color){
|
||||||
alert.show();
|
GlobalUserPreferences.ColorPreference prev=GlobalUserPreferences.color;
|
||||||
|
GlobalUserPreferences.color=pref;
|
||||||
|
GlobalUserPreferences.save();
|
||||||
|
colorItem.subtitleRes=getColorPaletteValue();
|
||||||
|
rebindItem(colorItem);
|
||||||
|
maybeApplyNewThemeRightNow(null, prev, null);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.setNegativeButton(R.string.cancel, null)
|
||||||
|
.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onPublishTextClick(){
|
private void onPublishTextClick(){
|
||||||
@@ -273,17 +257,17 @@ public class SettingsDisplayFragment extends BaseSettingsFragment<Void>{
|
|||||||
.show();
|
.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void maybeApplyNewThemeRightNow(GlobalUserPreferences.ThemePreference prevTheme, ColorPreference prevColor, Boolean prevTrueBlack){
|
private void maybeApplyNewThemeRightNow(GlobalUserPreferences.ThemePreference prevTheme, GlobalUserPreferences.ColorPreference prevColor, Boolean prevTrueBlack){
|
||||||
if(prevTheme==null) prevTheme=GlobalUserPreferences.theme;
|
if(prevTheme==null) prevTheme=GlobalUserPreferences.theme;
|
||||||
if(prevTrueBlack==null) prevTrueBlack=GlobalUserPreferences.trueBlackTheme;
|
if(prevTrueBlack==null) prevTrueBlack=GlobalUserPreferences.trueBlackTheme;
|
||||||
if(prevColor==null) prevColor=lp.getCurrentColor();
|
if(prevColor==null) prevColor=GlobalUserPreferences.color;
|
||||||
|
|
||||||
boolean isCurrentDark=prevTheme==GlobalUserPreferences.ThemePreference.DARK ||
|
boolean isCurrentDark=prevTheme==GlobalUserPreferences.ThemePreference.DARK ||
|
||||||
(prevTheme==GlobalUserPreferences.ThemePreference.AUTO && Build.VERSION.SDK_INT>=30 && getResources().getConfiguration().isNightModeActive());
|
(prevTheme==GlobalUserPreferences.ThemePreference.AUTO && Build.VERSION.SDK_INT>=30 && getResources().getConfiguration().isNightModeActive());
|
||||||
boolean isNewDark=GlobalUserPreferences.theme==GlobalUserPreferences.ThemePreference.DARK ||
|
boolean isNewDark=GlobalUserPreferences.theme==GlobalUserPreferences.ThemePreference.DARK ||
|
||||||
(GlobalUserPreferences.theme==GlobalUserPreferences.ThemePreference.AUTO && Build.VERSION.SDK_INT>=30 && getResources().getConfiguration().isNightModeActive());
|
(GlobalUserPreferences.theme==GlobalUserPreferences.ThemePreference.AUTO && Build.VERSION.SDK_INT>=30 && getResources().getConfiguration().isNightModeActive());
|
||||||
boolean isNewBlack=GlobalUserPreferences.trueBlackTheme;
|
boolean isNewBlack=GlobalUserPreferences.trueBlackTheme;
|
||||||
if(isCurrentDark!=isNewDark || prevColor!=lp.getCurrentColor() || (isNewDark && prevTrueBlack!=isNewBlack)){
|
if(isCurrentDark!=isNewDark || prevColor!=GlobalUserPreferences.color || (isNewDark && prevTrueBlack!=isNewBlack)){
|
||||||
restartActivityToApplyNewTheme();
|
restartActivityToApplyNewTheme();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ public class SettingsFiltersFragment extends BaseSettingsFragment<Filter>{
|
|||||||
MergeRecyclerAdapter adapter=new MergeRecyclerAdapter();
|
MergeRecyclerAdapter adapter=new MergeRecyclerAdapter();
|
||||||
adapter.addAdapter(super.getAdapter());
|
adapter.addAdapter(super.getAdapter());
|
||||||
adapter.addAdapter(new GenericListItemsAdapter<>(Collections.singletonList(
|
adapter.addAdapter(new GenericListItemsAdapter<>(Collections.singletonList(
|
||||||
new ListItem<Void>(R.string.settings_add_filter, 0, R.drawable.ic_fluent_add_24_regular, this::onAddFilterClick)
|
new ListItem<Void>(R.string.settings_add_filter, 0, R.drawable.ic_add_24px, this::onAddFilterClick)
|
||||||
)));
|
)));
|
||||||
return adapter;
|
return adapter;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,7 +55,6 @@ public class SettingsMainFragment extends BaseSettingsFragment<Void>{
|
|||||||
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_privacy, 0, R.drawable.ic_fluent_shield_24_regular, this::onPrivacyClick),
|
|
||||||
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),
|
||||||
@@ -64,15 +63,13 @@ public class SettingsMainFragment extends BaseSettingsFragment<Void>{
|
|||||||
|
|
||||||
Instance instance = AccountSessionManager.getInstance().getInstanceInfo(account.domain);
|
Instance instance = AccountSessionManager.getInstance().getInstanceInfo(account.domain);
|
||||||
if (!instance.isAkkoma())
|
if (!instance.isAkkoma())
|
||||||
data.add(3, new ListItem<>(R.string.settings_filters, 0, R.drawable.ic_fluent_filter_24_regular, this::onFiltersClick));
|
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
AccountSession session=AccountSessionManager.get(accountID);
|
account.reloadPreferences(null);
|
||||||
session.reloadPreferences(null);
|
|
||||||
session.updateAccountInfo();
|
|
||||||
E.register(this);
|
E.register(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,10 +133,6 @@ public class SettingsMainFragment extends BaseSettingsFragment<Void>{
|
|||||||
Nav.go(getActivity(), SettingsDisplayFragment.class, makeFragmentArgs());
|
Nav.go(getActivity(), SettingsDisplayFragment.class, makeFragmentArgs());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onPrivacyClick(){
|
|
||||||
Nav.go(getActivity(), SettingsPrivacyFragment.class, makeFragmentArgs());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onFiltersClick(){
|
private void onFiltersClick(){
|
||||||
Nav.go(getActivity(), SettingsFiltersFragment.class, makeFragmentArgs());
|
Nav.go(getActivity(), SettingsFiltersFragment.class, makeFragmentArgs());
|
||||||
}
|
}
|
||||||
@@ -162,7 +155,9 @@ public class SettingsMainFragment extends BaseSettingsFragment<Void>{
|
|||||||
.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)->account.logOut(getActivity(), ()->{
|
.setPositiveButton(R.string.log_out, (dialog, which)->account.logOut(getActivity(), ()->{
|
||||||
loggedOut=true;
|
loggedOut=true;
|
||||||
((MainActivity)getActivity()).restartActivity();
|
getActivity().finish();
|
||||||
|
Intent intent=new Intent(getActivity(), MainActivity.class);
|
||||||
|
startActivity(intent);
|
||||||
}))
|
}))
|
||||||
.setNegativeButton(R.string.cancel, null)
|
.setNegativeButton(R.string.cancel, null)
|
||||||
.show();
|
.show();
|
||||||
|
|||||||
@@ -1,41 +0,0 @@
|
|||||||
package org.joinmastodon.android.fragments.settings;
|
|
||||||
|
|
||||||
import android.os.Bundle;
|
|
||||||
|
|
||||||
import org.joinmastodon.android.R;
|
|
||||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
|
||||||
import org.joinmastodon.android.model.Account;
|
|
||||||
import org.joinmastodon.android.model.viewmodel.CheckableListItem;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class SettingsPrivacyFragment extends BaseSettingsFragment<Void>{
|
|
||||||
private CheckableListItem<Void> discoverableItem, indexableItem;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(Bundle savedInstanceState){
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
setTitle(R.string.settings_privacy);
|
|
||||||
Account self=AccountSessionManager.get(accountID).self;
|
|
||||||
onDataLoaded(List.of(
|
|
||||||
discoverableItem=new CheckableListItem<>(R.string.settings_discoverable, 0, CheckableListItem.Style.SWITCH, self.discoverable, R.drawable.ic_fluent_thumb_like_dislike_24_regular, ()->toggleCheckableItem(discoverableItem)),
|
|
||||||
indexableItem=new CheckableListItem<>(R.string.settings_indexable, 0, CheckableListItem.Style.SWITCH, self.source.indexable!=null ? self.source.indexable : true, R.drawable.ic_fluent_search_24_regular, ()->toggleCheckableItem(indexableItem))
|
|
||||||
));
|
|
||||||
if(self.source.indexable==null)
|
|
||||||
indexableItem.isEnabled=false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doLoadData(int offset, int count){}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPause(){
|
|
||||||
super.onPause();
|
|
||||||
Account self=AccountSessionManager.get(accountID).self;
|
|
||||||
if(self.discoverable!=discoverableItem.checked || (self.source.indexable!=null && self.source.indexable!=indexableItem.checked)){
|
|
||||||
self.discoverable=discoverableItem.checked;
|
|
||||||
self.source.indexable=indexableItem.checked;
|
|
||||||
AccountSessionManager.get(accountID).savePreferencesLater();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -153,21 +153,18 @@ public class SettingsServerFragment extends AppKitFragment{
|
|||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public SimpleViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
|
public SimpleViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType){
|
||||||
FrameLayout view=new FrameLayout(parent.getContext());
|
FrameLayout view=tabViews[viewType];
|
||||||
|
((ViewGroup)view.getParent()).removeView(view);
|
||||||
|
view.setVisibility(View.VISIBLE);
|
||||||
view.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
view.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||||
return new SimpleViewHolder(view);
|
return new SimpleViewHolder(view);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBindViewHolder(@NonNull SimpleViewHolder holder, int position){
|
public void onBindViewHolder(@NonNull SimpleViewHolder holder, int position){
|
||||||
FrameLayout view=tabViews[position];
|
|
||||||
if(view.getParent() instanceof ViewGroup parent)
|
|
||||||
parent.removeView(view);
|
|
||||||
view.setVisibility(View.VISIBLE);
|
|
||||||
((FrameLayout)holder.itemView).addView(view, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
|
||||||
Fragment fragment=getFragmentForPage(position);
|
Fragment fragment=getFragmentForPage(position);
|
||||||
if(!fragment.isAdded()){
|
if(!fragment.isAdded()){
|
||||||
getChildFragmentManager().beginTransaction().add(view.getId(), fragment).commit();
|
getChildFragmentManager().beginTransaction().add(holder.itemView.getId(), fragment).commit();
|
||||||
holder.itemView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener(){
|
holder.itemView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener(){
|
||||||
@Override
|
@Override
|
||||||
public boolean onPreDraw(){
|
public boolean onPreDraw(){
|
||||||
|
|||||||
@@ -7,17 +7,12 @@ import androidx.annotation.Nullable;
|
|||||||
|
|
||||||
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.requests.instance.GetInstance;
|
|
||||||
import org.parceler.Parcel;
|
import org.parceler.Parcel;
|
||||||
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import me.grishka.appkit.api.Callback;
|
|
||||||
import me.grishka.appkit.api.ErrorResponse;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a user of Mastodon and their associated profile.
|
* Represents a user of Mastodon and their associated profile.
|
||||||
*/
|
*/
|
||||||
@@ -28,22 +23,22 @@ public class Account extends BaseModel implements Searchable{
|
|||||||
/**
|
/**
|
||||||
* The account id
|
* The account id
|
||||||
*/
|
*/
|
||||||
// @RequiredField
|
@RequiredField
|
||||||
public String id;
|
public String id;
|
||||||
/**
|
/**
|
||||||
* The username of the account, not including domain.
|
* The username of the account, not including domain.
|
||||||
*/
|
*/
|
||||||
// @RequiredField
|
@RequiredField
|
||||||
public String username;
|
public String username;
|
||||||
/**
|
/**
|
||||||
* The Webfinger account URI. Equal to username for local users, or username@domain for remote users.
|
* The Webfinger account URI. Equal to username for local users, or username@domain for remote users.
|
||||||
*/
|
*/
|
||||||
// @RequiredField
|
@RequiredField
|
||||||
public String acct;
|
public String acct;
|
||||||
/**
|
/**
|
||||||
* The location of the user's profile page.
|
* The location of the user's profile page.
|
||||||
*/
|
*/
|
||||||
// @RequiredField
|
@RequiredField
|
||||||
public String url;
|
public String url;
|
||||||
|
|
||||||
// Display attributes
|
// Display attributes
|
||||||
@@ -56,12 +51,12 @@ public class Account extends BaseModel implements Searchable{
|
|||||||
/**
|
/**
|
||||||
* The profile's bio / description.
|
* The profile's bio / description.
|
||||||
*/
|
*/
|
||||||
// @RequiredField
|
@RequiredField
|
||||||
public String note;
|
public String note;
|
||||||
/**
|
/**
|
||||||
* An image icon that is shown next to statuses and in the profile.
|
* An image icon that is shown next to statuses and in the profile.
|
||||||
*/
|
*/
|
||||||
// @RequiredField
|
@RequiredField
|
||||||
public String avatar;
|
public String avatar;
|
||||||
/**
|
/**
|
||||||
* A static version of the avatar. Equal to avatar if its value is a static image; different if avatar is an animated GIF.
|
* A static version of the avatar. Equal to avatar if its value is a static image; different if avatar is an animated GIF.
|
||||||
@@ -139,7 +134,6 @@ public class Account extends BaseModel implements Searchable{
|
|||||||
* When a timed mute will expire, if applicable.
|
* When a timed mute will expire, if applicable.
|
||||||
*/
|
*/
|
||||||
public Instant muteExpiresAt;
|
public Instant muteExpiresAt;
|
||||||
public boolean noindex;
|
|
||||||
|
|
||||||
public List<Role> roles;
|
public List<Role> roles;
|
||||||
|
|
||||||
@@ -163,26 +157,16 @@ public class Account extends BaseModel implements Searchable{
|
|||||||
if(fields!=null){
|
if(fields!=null){
|
||||||
for(AccountField f:fields)
|
for(AccountField f:fields)
|
||||||
f.postprocess();
|
f.postprocess();
|
||||||
} else {
|
|
||||||
fields = Collections.emptyList();
|
|
||||||
}
|
}
|
||||||
if(emojis!=null){
|
if(emojis!=null){
|
||||||
for(Emoji e:emojis)
|
for(Emoji e:emojis)
|
||||||
e.postprocess();
|
e.postprocess();
|
||||||
} else {
|
|
||||||
emojis = Collections.emptyList();
|
|
||||||
}
|
}
|
||||||
if(moved!=null)
|
if(moved!=null)
|
||||||
moved.postprocess();
|
moved.postprocess();
|
||||||
if(fqn == null) fqn = getFullyQualifiedName();
|
|
||||||
if(id == null) id = "";
|
|
||||||
if(username == null) username = "";
|
|
||||||
if(TextUtils.isEmpty(displayName))
|
if(TextUtils.isEmpty(displayName))
|
||||||
displayName = !TextUtils.isEmpty(username) ? username : "";
|
displayName=username;
|
||||||
if(acct == null) acct = "";
|
if(fqn == null) fqn = getFullyQualifiedName();
|
||||||
if(url == null) url = "";
|
|
||||||
if(note == null) note = "";
|
|
||||||
if(avatar == null) avatar = "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isLocal(){
|
public boolean isLocal(){
|
||||||
@@ -207,8 +191,6 @@ public class Account extends BaseModel implements Searchable{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String getFullyQualifiedName() {
|
public String getFullyQualifiedName() {
|
||||||
if (TextUtils.isEmpty(acct))
|
|
||||||
return "";
|
|
||||||
return fqn != null ? fqn : acct.split("@")[0] + "@" + getDomainFromURL();
|
return fqn != null ? fqn : acct.split("@")[0] + "@" + getDomainFromURL();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -239,7 +221,6 @@ public class Account extends BaseModel implements Searchable{
|
|||||||
", source="+source+
|
", source="+source+
|
||||||
", suspended="+suspended+
|
", suspended="+suspended+
|
||||||
", muteExpiresAt="+muteExpiresAt+
|
", muteExpiresAt="+muteExpiresAt+
|
||||||
", noindex="+noindex+
|
|
||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,34 +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 boolean hasKnownDimensions(){
|
|
||||||
return meta!=null && (
|
|
||||||
(meta.height>0 && meta.width>0)
|
|
||||||
|| (meta.original!=null && meta.original.height>0 && meta.original.width>0)
|
|
||||||
|| (meta.small!=null && meta.small.height>0 && meta.small.width>0)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public double getDuration(){
|
public double getDuration(){
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package org.joinmastodon.android.model;
|
package org.joinmastodon.android.model;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class EmojiCategory{
|
public class EmojiCategory{
|
||||||
@@ -11,8 +10,4 @@ public class EmojiCategory{
|
|||||||
this.title=title;
|
this.title=title;
|
||||||
this.emojis=emojis;
|
this.emojis=emojis;
|
||||||
}
|
}
|
||||||
public EmojiCategory(EmojiCategory category){
|
|
||||||
this.title = category.title;
|
|
||||||
this.emojis = new ArrayList<>(category.emojis);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +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();
|
if(keywordMatches==null) keywordMatches=List.of();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ public class Hashtag extends BaseModel implements DisplayItemsParent{
|
|||||||
", following="+following+
|
", following="+following+
|
||||||
", history="+history+
|
", history="+history+
|
||||||
", statusesCount="+statusesCount+
|
", statusesCount="+statusesCount+
|
||||||
", following="+following+
|
|
||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,19 +30,4 @@ public class Hashtag extends BaseModel implements DisplayItemsParent{
|
|||||||
public String getID(){
|
public String getID(){
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o){
|
|
||||||
if(this==o) return true;
|
|
||||||
if(o==null || getClass()!=o.getClass()) return false;
|
|
||||||
|
|
||||||
Hashtag hashtag=(Hashtag) o;
|
|
||||||
|
|
||||||
return name.equals(hashtag.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode(){
|
|
||||||
return name.hashCode();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,22 +20,4 @@ public class Mention extends BaseModel{
|
|||||||
", url='"+url+'\''+
|
", url='"+url+'\''+
|
||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o){
|
|
||||||
if(this==o) return true;
|
|
||||||
if(o==null || getClass()!=o.getClass()) return false;
|
|
||||||
|
|
||||||
Mention mention=(Mention) o;
|
|
||||||
|
|
||||||
if(!id.equals(mention.id)) return false;
|
|
||||||
return url.equals(mention.url);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode(){
|
|
||||||
int result=id.hashCode();
|
|
||||||
result=31*result+url.hashCode();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,8 +37,6 @@ public class Source extends BaseModel{
|
|||||||
* The number of pending follow requests.
|
* The number of pending follow requests.
|
||||||
*/
|
*/
|
||||||
public int followRequestCount;
|
public int followRequestCount;
|
||||||
public Boolean indexable;
|
|
||||||
public boolean hideCollections;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void postprocess() throws ObjectValidationException{
|
public void postprocess() throws ObjectValidationException{
|
||||||
@@ -56,8 +54,6 @@ public class Source extends BaseModel{
|
|||||||
", sensitive="+sensitive+
|
", sensitive="+sensitive+
|
||||||
", language='"+language+'\''+
|
", language='"+language+'\''+
|
||||||
", followRequestCount="+followRequestCount+
|
", followRequestCount="+followRequestCount+
|
||||||
", indexable="+indexable+
|
|
||||||
", hideCollections="+hideCollections+
|
|
||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,21 +5,8 @@ import static org.joinmastodon.android.api.MastodonAPIController.gsonWithoutDese
|
|||||||
|
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import org.joinmastodon.android.api.ObjectValidationException;
|
|
||||||
import org.joinmastodon.android.api.RequiredField;
|
|
||||||
import org.joinmastodon.android.events.StatusCountersUpdatedEvent;
|
|
||||||
import org.joinmastodon.android.ui.text.HtmlParser;
|
|
||||||
import org.parceler.Parcel;
|
|
||||||
|
|
||||||
import java.time.Instant;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import com.github.bottomSoftwareFoundation.bottom.Bottom;
|
|
||||||
import com.github.bottomSoftwareFoundation.bottom.TranslationError;
|
|
||||||
import com.google.gson.JsonDeserializationContext;
|
import com.google.gson.JsonDeserializationContext;
|
||||||
import com.google.gson.JsonDeserializer;
|
import com.google.gson.JsonDeserializer;
|
||||||
import com.google.gson.JsonElement;
|
import com.google.gson.JsonElement;
|
||||||
@@ -28,21 +15,16 @@ import com.google.gson.JsonParseException;
|
|||||||
|
|
||||||
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.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.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.joinmastodon.android.utils.StatusTextEncoder;
|
|
||||||
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.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
@Parcel
|
@Parcel
|
||||||
public class Status extends BaseModel implements DisplayItemsParent, Searchable{
|
public class Status extends BaseModel implements DisplayItemsParent, Searchable{
|
||||||
@@ -100,10 +82,10 @@ public class Status extends BaseModel implements DisplayItemsParent, Searchable{
|
|||||||
public transient boolean spoilerRevealed;
|
public transient boolean spoilerRevealed;
|
||||||
public transient boolean sensitiveRevealed;
|
public transient boolean sensitiveRevealed;
|
||||||
public transient boolean textExpanded, textExpandable;
|
public transient boolean textExpanded, textExpandable;
|
||||||
public transient String hasGapAfter;
|
public transient boolean hasGapAfter;
|
||||||
|
public transient TranslatedStatus translation;
|
||||||
|
public transient boolean translationShown;
|
||||||
private transient String strippedText;
|
private transient String strippedText;
|
||||||
public transient TranslationState translationState=TranslationState.HIDDEN;
|
|
||||||
public transient Translation translation;
|
|
||||||
|
|
||||||
public Status(){}
|
public Status(){}
|
||||||
|
|
||||||
@@ -219,38 +201,6 @@ public class Status extends BaseModel implements DisplayItemsParent, Searchable{
|
|||||||
return (Status) super.clone();
|
return (Status) super.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final Pattern BOTTOM_TEXT_PATTERN = Pattern.compile("(?:[\uD83E\uDEC2\uD83D\uDC96✨\uD83E\uDD7A,]+|❤️)(?:\uD83D\uDC49\uD83D\uDC48(?:[\uD83E\uDEC2\uD83D\uDC96✨\uD83E\uDD7A,]+|❤️))*\uD83D\uDC49\uD83D\uDC48");
|
|
||||||
public boolean isEligibleForTranslation(AccountSession session){
|
|
||||||
Instance instanceInfo = AccountSessionManager.getInstance().getInstanceInfo(session.domain);
|
|
||||||
boolean translateEnabled = instanceInfo != null &&
|
|
||||||
instanceInfo.v2 != null && instanceInfo.v2.configuration.translation != null &&
|
|
||||||
instanceInfo.v2.configuration.translation.enabled;
|
|
||||||
|
|
||||||
try {
|
|
||||||
String bottomText = BOTTOM_TEXT_PATTERN.matcher(getStrippedText()).find()
|
|
||||||
? new StatusTextEncoder(Bottom::decode).decode(getStrippedText(), BOTTOM_TEXT_PATTERN)
|
|
||||||
: null;
|
|
||||||
if(bottomText==null || bottomText.length()==0 || bottomText.equals("\u0005")) bottomText=null;
|
|
||||||
if(bottomText!=null){
|
|
||||||
translation=new Translation();
|
|
||||||
translation.content=bottomText;
|
|
||||||
translation.detectedSourceLanguage="\uD83E\uDD7A\uD83D\uDC49\uD83D\uDC48";
|
|
||||||
translation.provider="bottom-java";
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} catch (TranslationError ignored) {}
|
|
||||||
|
|
||||||
return translateEnabled && !TextUtils.isEmpty(content) && !TextUtils.isEmpty(language)
|
|
||||||
&& !Objects.equals(Locale.getDefault().getLanguage(), language)
|
|
||||||
&& (visibility==StatusPrivacy.PUBLIC || visibility==StatusPrivacy.UNLISTED);
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum TranslationState{
|
|
||||||
HIDDEN,
|
|
||||||
SHOWN,
|
|
||||||
LOADING
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isReblogPermitted(String accountID){
|
public boolean isReblogPermitted(String accountID){
|
||||||
return visibility.isReblogPermitted(account.id.equals(
|
return visibility.isReblogPermitted(account.id.equals(
|
||||||
AccountSessionManager.getInstance().getAccount(accountID).self.id
|
AccountSessionManager.getInstance().getAccount(accountID).self.id
|
||||||
|
|||||||
@@ -219,7 +219,7 @@ public class TimelineDefinition {
|
|||||||
args.putString("listID", listId);
|
args.putString("listID", listId);
|
||||||
args.putBoolean("listIsExclusive", listIsExclusive);
|
args.putBoolean("listIsExclusive", listIsExclusive);
|
||||||
} else if (type == TimelineType.HASHTAG) {
|
} else if (type == TimelineType.HASHTAG) {
|
||||||
args.putString("hashtagName", hashtagName);
|
args.putString("hashtag", hashtagName);
|
||||||
args.putBoolean("localOnly", hashtagLocalOnly);
|
args.putBoolean("localOnly", hashtagLocalOnly);
|
||||||
args.putStringArrayList("any", hashtagAny == null ? new ArrayList<>() : new ArrayList<>(hashtagAny));
|
args.putStringArrayList("any", hashtagAny == null ? new ArrayList<>() : new ArrayList<>(hashtagAny));
|
||||||
args.putStringArrayList("all", hashtagAll == null ? new ArrayList<>() : new ArrayList<>(hashtagAll));
|
args.putStringArrayList("all", hashtagAll == null ? new ArrayList<>() : new ArrayList<>(hashtagAll));
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package org.joinmastodon.android.model;
|
||||||
|
|
||||||
|
public class TranslatedStatus extends BaseModel {
|
||||||
|
public String content;
|
||||||
|
public String detectedSourceLanguage;
|
||||||
|
public String provider;
|
||||||
|
}
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
package org.joinmastodon.android.model;
|
|
||||||
|
|
||||||
import org.joinmastodon.android.api.AllFieldsAreRequired;
|
|
||||||
|
|
||||||
@AllFieldsAreRequired
|
|
||||||
public class Translation extends BaseModel{
|
|
||||||
public String content;
|
|
||||||
public String detectedSourceLanguage;
|
|
||||||
public String provider;
|
|
||||||
}
|
|
||||||
@@ -1,10 +1,8 @@
|
|||||||
package org.joinmastodon.android.model.viewmodel;
|
package org.joinmastodon.android.model.viewmodel;
|
||||||
|
|
||||||
import android.text.SpannableStringBuilder;
|
import android.text.SpannableStringBuilder;
|
||||||
import android.text.TextUtils;
|
|
||||||
|
|
||||||
import org.joinmastodon.android.GlobalUserPreferences;
|
import org.joinmastodon.android.GlobalUserPreferences;
|
||||||
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.model.Account;
|
import org.joinmastodon.android.model.Account;
|
||||||
import org.joinmastodon.android.model.AccountField;
|
import org.joinmastodon.android.model.AccountField;
|
||||||
@@ -26,13 +24,9 @@ public class AccountViewModel{
|
|||||||
|
|
||||||
public AccountViewModel(Account account, String accountID){
|
public AccountViewModel(Account account, String accountID){
|
||||||
this.account=account;
|
this.account=account;
|
||||||
AccountSession session = AccountSessionManager.get(accountID);
|
avaRequest=new UrlImageLoaderRequest(GlobalUserPreferences.playGifs ? account.avatar : account.avatarStatic, V.dp(50), V.dp(50));
|
||||||
avaRequest=new UrlImageLoaderRequest(
|
|
||||||
TextUtils.isEmpty(account.avatar) ? session.getDefaultAvatarUrl() :
|
|
||||||
GlobalUserPreferences.playGifs ? account.avatar : account.avatarStatic,
|
|
||||||
V.dp(50), V.dp(50));
|
|
||||||
emojiHelper=new CustomEmojiHelper();
|
emojiHelper=new CustomEmojiHelper();
|
||||||
if(session.getLocalPreferences().customEmojiInNames)
|
if(AccountSessionManager.get(accountID).getLocalPreferences().customEmojiInNames)
|
||||||
parsedName=HtmlParser.parseCustomEmoji(account.displayName, account.emojis);
|
parsedName=HtmlParser.parseCustomEmoji(account.displayName, account.emojis);
|
||||||
else
|
else
|
||||||
parsedName=account.displayName;
|
parsedName=account.displayName;
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import android.graphics.drawable.Animatable;
|
|||||||
import android.graphics.drawable.ColorDrawable;
|
import android.graphics.drawable.ColorDrawable;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.WindowInsets;
|
import android.view.WindowInsets;
|
||||||
@@ -40,7 +39,6 @@ import androidx.annotation.NonNull;
|
|||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.StringRes;
|
import androidx.annotation.StringRes;
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
import me.grishka.appkit.FragmentStackActivity;
|
|
||||||
import me.grishka.appkit.Nav;
|
import me.grishka.appkit.Nav;
|
||||||
import me.grishka.appkit.api.Callback;
|
import me.grishka.appkit.api.Callback;
|
||||||
import me.grishka.appkit.api.ErrorResponse;
|
import me.grishka.appkit.api.ErrorResponse;
|
||||||
@@ -146,9 +144,21 @@ public class AccountSwitcherSheet extends BottomSheet{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void logOut(String accountID){
|
private void logOut(String accountID){
|
||||||
AccountSessionManager.get(accountID).logOut(activity, ()->{
|
AccountSession session=AccountSessionManager.getInstance().getAccount(accountID);
|
||||||
((MainActivity)activity).restartActivity();
|
new RevokeOauthToken(session.app.clientId, session.app.clientSecret, session.token.accessToken)
|
||||||
});
|
.setCallback(new Callback<>(){
|
||||||
|
@Override
|
||||||
|
public void onSuccess(Object result){
|
||||||
|
onLoggedOut(accountID);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(ErrorResponse error){
|
||||||
|
onLoggedOut(accountID);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.wrapProgress(activity, R.string.loading, false)
|
||||||
|
.exec(accountID);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void logOutAll(){
|
private void logOutAll(){
|
||||||
@@ -321,10 +331,10 @@ public class AccountSwitcherSheet extends BottomSheet{
|
|||||||
onClick.accept(item.getID(), false);
|
onClick.accept(item.getID(), false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(AccountSessionManager.getInstance().tryGetAccount(item.getID())!=null){
|
if(AccountSessionManager.getInstance().tryGetAccount(item.getID())!=null)
|
||||||
AccountSessionManager.getInstance().setLastActiveAccountID(item.getID());
|
AccountSessionManager.getInstance().setLastActiveAccountID(item.getID());
|
||||||
((MainActivity)activity).restartActivity();
|
activity.finish();
|
||||||
}
|
activity.startActivity(new Intent(activity, MainActivity.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -15,7 +15,6 @@ import android.widget.TextView;
|
|||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
|
||||||
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
||||||
import org.joinmastodon.android.model.Account;
|
import org.joinmastodon.android.model.Account;
|
||||||
import org.joinmastodon.android.model.Notification;
|
import org.joinmastodon.android.model.Notification;
|
||||||
@@ -42,13 +41,12 @@ public class AccountCardStatusDisplayItem extends StatusDisplayItem{
|
|||||||
public CustomEmojiHelper emojiHelper=new CustomEmojiHelper();
|
public CustomEmojiHelper emojiHelper=new CustomEmojiHelper();
|
||||||
public CharSequence parsedName, parsedBio;
|
public CharSequence parsedName, parsedBio;
|
||||||
|
|
||||||
public AccountCardStatusDisplayItem(String parentID, BaseStatusListFragment parentFragment, String accountID, Account account, Notification notification){
|
public AccountCardStatusDisplayItem(String parentID, BaseStatusListFragment parentFragment, Account account, Notification notification){
|
||||||
super(parentID, parentFragment);
|
super(parentID, parentFragment);
|
||||||
this.account=account;
|
this.account=account;
|
||||||
this.notification=notification;
|
this.notification=notification;
|
||||||
avaRequest=new UrlImageLoaderRequest(
|
if(!TextUtils.isEmpty(account.avatar))
|
||||||
TextUtils.isEmpty(account.avatar) ? AccountSessionManager.get(accountID).getDefaultAvatarUrl() : account.avatar,
|
avaRequest=new UrlImageLoaderRequest(account.avatar, V.dp(50), V.dp(50));
|
||||||
V.dp(50), V.dp(50));
|
|
||||||
if(!TextUtils.isEmpty(account.header))
|
if(!TextUtils.isEmpty(account.header))
|
||||||
coverRequest=new UrlImageLoaderRequest(account.header, 1000, 1000);
|
coverRequest=new UrlImageLoaderRequest(account.header, 1000, 1000);
|
||||||
parsedBio=HtmlParser.parse(account.note, account.emojis, Collections.emptyList(), Collections.emptyList(), parentFragment.getAccountID());
|
parsedBio=HtmlParser.parse(account.note, account.emojis, Collections.emptyList(), Collections.emptyList(), parentFragment.getAccountID());
|
||||||
|
|||||||
@@ -170,14 +170,12 @@ public class EmojiReactionsStatusDisplayItem extends StatusDisplayItem {
|
|||||||
@Override
|
@Override
|
||||||
public void onBind(EmojiReactionsStatusDisplayItem item) {
|
public void onBind(EmojiReactionsStatusDisplayItem item) {
|
||||||
if(emojiKeyboard != null) root.removeView(emojiKeyboard.getView());
|
if(emojiKeyboard != null) root.removeView(emojiKeyboard.getView());
|
||||||
addButton.setSelected(false);
|
|
||||||
AccountSession session=item.parentFragment.getSession();
|
AccountSession session=item.parentFragment.getSession();
|
||||||
item.status.reactions.forEach(r->r.request=r.getUrl(item.playGifs)!=null
|
item.status.reactions.forEach(r->r.request=r.getUrl(item.playGifs)!=null
|
||||||
? new UrlImageLoaderRequest(r.getUrl(item.playGifs), V.sp(24), V.sp(24))
|
? new UrlImageLoaderRequest(r.getUrl(item.playGifs), V.sp(24), V.sp(24))
|
||||||
: null);
|
: null);
|
||||||
emojiKeyboard=new CustomEmojiPopupKeyboard(
|
emojiKeyboard=new CustomEmojiPopupKeyboard(
|
||||||
(Activity) item.parentFragment.getContext(),
|
(Activity) item.parentFragment.getContext(),
|
||||||
item.accountID,
|
|
||||||
AccountSessionManager.getInstance().getCustomEmojis(session.domain),
|
AccountSessionManager.getInstance().getCustomEmojis(session.domain),
|
||||||
session.domain, true);
|
session.domain, true);
|
||||||
emojiKeyboard.setListener(this);
|
emojiKeyboard.setListener(this);
|
||||||
|
|||||||
@@ -13,10 +13,7 @@ import android.widget.Button;
|
|||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
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.session.AccountSession;
|
|
||||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
|
||||||
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
||||||
import org.joinmastodon.android.fragments.StatusEditHistoryFragment;
|
import org.joinmastodon.android.fragments.StatusEditHistoryFragment;
|
||||||
import org.joinmastodon.android.fragments.account_list.StatusFavoritesListFragment;
|
import org.joinmastodon.android.fragments.account_list.StatusFavoritesListFragment;
|
||||||
@@ -77,7 +74,6 @@ public class ExtendedFooterStatusDisplayItem extends StatusDisplayItem{
|
|||||||
@Override
|
@Override
|
||||||
public void onBind(ExtendedFooterStatusDisplayItem item){
|
public void onBind(ExtendedFooterStatusDisplayItem item){
|
||||||
Status s=item.status;
|
Status s=item.status;
|
||||||
favorites.setCompoundDrawablesRelativeWithIntrinsicBounds(GlobalUserPreferences.likeIcon ? R.drawable.ic_fluent_heart_20_regular : R.drawable.ic_fluent_star_20_regular, 0, 0, 0);
|
|
||||||
favorites.setText(context.getResources().getQuantityString(R.plurals.x_favorites, (int)(s.favouritesCount%1000), s.favouritesCount));
|
favorites.setText(context.getResources().getQuantityString(R.plurals.x_favorites, (int)(s.favouritesCount%1000), s.favouritesCount));
|
||||||
reblogs.setText(context.getResources().getQuantityString(R.plurals.x_reblogs, (int) (s.reblogsCount % 1000), s.reblogsCount));
|
reblogs.setText(context.getResources().getQuantityString(R.plurals.x_reblogs, (int) (s.reblogsCount % 1000), s.reblogsCount));
|
||||||
reblogs.setVisibility(s.visibility != StatusPrivacy.DIRECT ? View.VISIBLE : View.GONE);
|
reblogs.setVisibility(s.visibility != StatusPrivacy.DIRECT ? View.VISIBLE : View.GONE);
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import android.app.Activity;
|
|||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.res.ColorStateList;
|
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
@@ -13,9 +12,10 @@ import android.view.View;
|
|||||||
import android.view.ViewConfiguration;
|
import android.view.ViewConfiguration;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.accessibility.AccessibilityNodeInfo;
|
import android.view.accessibility.AccessibilityNodeInfo;
|
||||||
|
import android.view.animation.AlphaAnimation;
|
||||||
|
import android.view.animation.Animation;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
import android.widget.ImageView;
|
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import org.joinmastodon.android.GlobalUserPreferences;
|
import org.joinmastodon.android.GlobalUserPreferences;
|
||||||
@@ -56,14 +56,14 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
|
|||||||
public static class Holder extends StatusDisplayItem.Holder<FooterStatusDisplayItem>{
|
public static class Holder extends StatusDisplayItem.Holder<FooterStatusDisplayItem>{
|
||||||
private final TextView replies, boosts, favorites;
|
private final TextView replies, boosts, favorites;
|
||||||
private final View reply, boost, favorite, share, bookmark;
|
private final View reply, boost, favorite, share, bookmark;
|
||||||
private final ImageView favIcon;
|
private static final Animation opacityOut, opacityIn;
|
||||||
|
|
||||||
private View touchingView = null;
|
private View touchingView = null;
|
||||||
private boolean longClickPerformed = false;
|
private boolean longClickPerformed = false;
|
||||||
private final Runnable longClickRunnable = () -> {
|
private final Runnable longClickRunnable = () -> {
|
||||||
longClickPerformed = touchingView != null && touchingView.performLongClick();
|
longClickPerformed = touchingView != null && touchingView.performLongClick();
|
||||||
if (longClickPerformed && touchingView != null) {
|
if (longClickPerformed && touchingView != null) {
|
||||||
UiUtils.opacityIn(touchingView);
|
touchingView.startAnimation(opacityIn);
|
||||||
touchingView.animate().scaleX(1).scaleY(1).setInterpolator(CubicBezierInterpolator.DEFAULT).setDuration(150).start();
|
touchingView.animate().scaleX(1).scaleY(1).setInterpolator(CubicBezierInterpolator.DEFAULT).setDuration(150).start();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -77,6 +77,18 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private static final float ALPHA_PRESSED=0.55f;
|
||||||
|
|
||||||
|
static {
|
||||||
|
opacityOut = new AlphaAnimation(1, ALPHA_PRESSED);
|
||||||
|
opacityOut.setDuration(300);
|
||||||
|
opacityOut.setInterpolator(CubicBezierInterpolator.DEFAULT);
|
||||||
|
opacityOut.setFillAfter(true);
|
||||||
|
opacityIn = new AlphaAnimation(ALPHA_PRESSED, 1);
|
||||||
|
opacityIn.setDuration(400);
|
||||||
|
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);
|
||||||
|
|
||||||
@@ -89,7 +101,6 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
|
|||||||
favorite=findViewById(R.id.favorite_btn);
|
favorite=findViewById(R.id.favorite_btn);
|
||||||
share=findViewById(R.id.share_btn);
|
share=findViewById(R.id.share_btn);
|
||||||
bookmark=findViewById(R.id.bookmark_btn);
|
bookmark=findViewById(R.id.bookmark_btn);
|
||||||
favIcon=findViewById(R.id.favorite_icon);
|
|
||||||
|
|
||||||
reply.setOnTouchListener(this::onButtonTouch);
|
reply.setOnTouchListener(this::onButtonTouch);
|
||||||
reply.setOnClickListener(this::onReplyClick);
|
reply.setOnClickListener(this::onReplyClick);
|
||||||
@@ -133,13 +144,6 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
|
|||||||
boolean condenseBottom = !item.isMainStatus && item.hasDescendantNeighbor &&
|
boolean condenseBottom = !item.isMainStatus && item.hasDescendantNeighbor &&
|
||||||
!nextIsWarning;
|
!nextIsWarning;
|
||||||
|
|
||||||
ColorStateList color=item.parentFragment.getResources().getColorStateList(
|
|
||||||
GlobalUserPreferences.likeIcon ? R.color.like_icon : R.color.favorite_icon, item.parentFragment.getContext().getTheme()
|
|
||||||
);
|
|
||||||
favIcon.setImageResource(GlobalUserPreferences.likeIcon ? R.drawable.ic_fluent_heart_24_selector : R.drawable.ic_fluent_star_24_selector);
|
|
||||||
favIcon.setImageTintList(color);
|
|
||||||
favorites.setTextColor(color);
|
|
||||||
|
|
||||||
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) itemView.getLayoutParams();
|
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) itemView.getLayoutParams();
|
||||||
params.setMargins(params.leftMargin, params.topMargin, params.rightMargin,
|
params.setMargins(params.leftMargin, params.topMargin, params.rightMargin,
|
||||||
condenseBottom ? V.dp(-5) : 0);
|
condenseBottom ? V.dp(-5) : 0);
|
||||||
@@ -168,7 +172,7 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
|
|||||||
if (!longClickPerformed) v.animate().scaleX(1).scaleY(1).setInterpolator(CubicBezierInterpolator.DEFAULT).setDuration(150).start();
|
if (!longClickPerformed) v.animate().scaleX(1).scaleY(1).setInterpolator(CubicBezierInterpolator.DEFAULT).setDuration(150).start();
|
||||||
if (disabled) return true;
|
if (disabled) return true;
|
||||||
if (action == MotionEvent.ACTION_UP && !longClickPerformed) v.performClick();
|
if (action == MotionEvent.ACTION_UP && !longClickPerformed) v.performClick();
|
||||||
else if (!longClickPerformed) UiUtils.opacityIn(v);
|
else if (!longClickPerformed) v.startAnimation(opacityIn);
|
||||||
} else if (action == MotionEvent.ACTION_DOWN) {
|
} else if (action == MotionEvent.ACTION_DOWN) {
|
||||||
longClickPerformed = false;
|
longClickPerformed = false;
|
||||||
touchingView = v;
|
touchingView = v;
|
||||||
@@ -176,13 +180,13 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
|
|||||||
v.animate().scaleX(0.85f).scaleY(0.85f).setInterpolator(CubicBezierInterpolator.DEFAULT).setDuration(75).start();
|
v.animate().scaleX(0.85f).scaleY(0.85f).setInterpolator(CubicBezierInterpolator.DEFAULT).setDuration(75).start();
|
||||||
if (disabled) return true;
|
if (disabled) return true;
|
||||||
v.postDelayed(longClickRunnable, ViewConfiguration.getLongPressTimeout());
|
v.postDelayed(longClickRunnable, ViewConfiguration.getLongPressTimeout());
|
||||||
UiUtils.opacityOut(v);
|
v.startAnimation(opacityOut);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onReplyClick(View v){
|
private void onReplyClick(View v){
|
||||||
UiUtils.opacityIn(v);
|
v.startAnimation(opacityIn);
|
||||||
Bundle args=new Bundle();
|
Bundle args=new Bundle();
|
||||||
args.putString("account", item.accountID);
|
args.putString("account", item.accountID);
|
||||||
args.putParcelable("replyTo", Parcels.wrap(item.status));
|
args.putParcelable("replyTo", Parcels.wrap(item.status));
|
||||||
@@ -206,7 +210,7 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
|
|||||||
|
|
||||||
private void onBoostClick(View v){
|
private void onBoostClick(View v){
|
||||||
if (GlobalUserPreferences.confirmBoost) {
|
if (GlobalUserPreferences.confirmBoost) {
|
||||||
UiUtils.opacityIn(v);
|
v.startAnimation(opacityIn);
|
||||||
onBoostLongClick(v);
|
onBoostLongClick(v);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -215,7 +219,7 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void boostConsumer(View v, Status r) {
|
private void boostConsumer(View v, Status r) {
|
||||||
UiUtils.opacityIn(v);
|
v.startAnimation(opacityIn);
|
||||||
bindText(boosts, r.reblogsCount);
|
bindText(boosts, r.reblogsCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -226,7 +230,7 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
|
|||||||
AccountSession session = AccountSessionManager.getInstance().getAccount(item.accountID);
|
AccountSession session = AccountSessionManager.getInstance().getAccount(item.accountID);
|
||||||
|
|
||||||
Consumer<StatusPrivacy> doReblog = (visibility) -> {
|
Consumer<StatusPrivacy> doReblog = (visibility) -> {
|
||||||
UiUtils.opacityOut(v);
|
v.startAnimation(opacityOut);
|
||||||
session.getStatusInteractionController()
|
session.getStatusInteractionController()
|
||||||
.setReblogged(item.status, !item.status.reblogged, visibility, r->boostConsumer(v, r));
|
.setReblogged(item.status, !item.status.reblogged, visibility, r->boostConsumer(v, r));
|
||||||
dialog.dismiss();
|
dialog.dismiss();
|
||||||
@@ -279,7 +283,7 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
|
|||||||
|
|
||||||
menu.findViewById(R.id.quote).setOnClickListener(c->{
|
menu.findViewById(R.id.quote).setOnClickListener(c->{
|
||||||
dialog.dismiss();
|
dialog.dismiss();
|
||||||
UiUtils.opacityIn(v);
|
v.startAnimation(opacityIn);
|
||||||
Bundle args=new Bundle();
|
Bundle args=new Bundle();
|
||||||
args.putString("account", item.accountID);
|
args.putString("account", item.accountID);
|
||||||
AccountSession accountSession=AccountSessionManager.getInstance().getAccount(item.accountID);
|
AccountSession accountSession=AccountSessionManager.getInstance().getAccount(item.accountID);
|
||||||
@@ -304,7 +308,7 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
|
|||||||
private void onFavoriteClick(View v){
|
private void onFavoriteClick(View v){
|
||||||
favorite.setSelected(!item.status.favourited);
|
favorite.setSelected(!item.status.favourited);
|
||||||
AccountSessionManager.getInstance().getAccount(item.accountID).getStatusInteractionController().setFavorited(item.status, !item.status.favourited, r->{
|
AccountSessionManager.getInstance().getAccount(item.accountID).getStatusInteractionController().setFavorited(item.status, !item.status.favourited, r->{
|
||||||
UiUtils.opacityIn(v);
|
v.startAnimation(opacityIn);
|
||||||
bindText(favorites, r.favouritesCount);
|
bindText(favorites, r.favouritesCount);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -318,7 +322,7 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
|
|||||||
R.string.sk_favorite_as,
|
R.string.sk_favorite_as,
|
||||||
R.string.sk_favorited_as,
|
R.string.sk_favorited_as,
|
||||||
R.string.sk_already_favorited,
|
R.string.sk_already_favorited,
|
||||||
GlobalUserPreferences.likeIcon ? R.drawable.ic_fluent_heart_28_regular : R.drawable.ic_fluent_star_28_regular
|
R.drawable.ic_fluent_star_28_regular
|
||||||
);
|
);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -326,7 +330,7 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
|
|||||||
private void onBookmarkClick(View v){
|
private void onBookmarkClick(View v){
|
||||||
bookmark.setSelected(!item.status.bookmarked);
|
bookmark.setSelected(!item.status.bookmarked);
|
||||||
AccountSessionManager.getInstance().getAccount(item.accountID).getStatusInteractionController().setBookmarked(item.status, !item.status.bookmarked, r->{
|
AccountSessionManager.getInstance().getAccount(item.accountID).getStatusInteractionController().setBookmarked(item.status, !item.status.bookmarked, r->{
|
||||||
UiUtils.opacityIn(v);
|
v.startAnimation(opacityIn);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -345,7 +349,7 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void onShareClick(View v){
|
private void onShareClick(View v){
|
||||||
UiUtils.opacityIn(v);
|
v.startAnimation(opacityIn);
|
||||||
Intent intent=new Intent(Intent.ACTION_SEND);
|
Intent intent=new Intent(Intent.ACTION_SEND);
|
||||||
intent.setType("text/plain");
|
intent.setType("text/plain");
|
||||||
intent.putExtra(Intent.EXTRA_TEXT, item.status.url);
|
intent.putExtra(Intent.EXTRA_TEXT, item.status.url);
|
||||||
|
|||||||
@@ -8,25 +8,14 @@ import android.widget.TextView;
|
|||||||
|
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
||||||
import org.joinmastodon.android.model.Status;
|
|
||||||
import org.joinmastodon.android.ui.drawables.SawtoothTearDrawable;
|
import org.joinmastodon.android.ui.drawables.SawtoothTearDrawable;
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
|
||||||
|
|
||||||
import java.time.Instant;
|
|
||||||
|
|
||||||
import me.grishka.appkit.utils.V;
|
|
||||||
|
|
||||||
|
// Mind the gap!
|
||||||
public class GapStatusDisplayItem extends StatusDisplayItem{
|
public class GapStatusDisplayItem extends StatusDisplayItem{
|
||||||
public boolean loading;
|
public boolean loading;
|
||||||
private Status status;
|
|
||||||
|
|
||||||
public GapStatusDisplayItem(String parentID, BaseStatusListFragment<?> parentFragment, Status status){
|
public GapStatusDisplayItem(String parentID, BaseStatusListFragment parentFragment){
|
||||||
super(parentID, parentFragment);
|
super(parentID, parentFragment);
|
||||||
this.status=status;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getMaxID(){
|
|
||||||
return status.hasGapAfter;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -35,55 +24,25 @@ public class GapStatusDisplayItem extends StatusDisplayItem{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static class Holder extends StatusDisplayItem.Holder<GapStatusDisplayItem>{
|
public static class Holder extends StatusDisplayItem.Holder<GapStatusDisplayItem>{
|
||||||
public final ProgressBar progressTop, progressBottom;
|
public final ProgressBar progress;
|
||||||
public final TextView textTop, gap, textBottom;
|
public final TextView text;
|
||||||
public final View top, bottom;
|
|
||||||
|
|
||||||
public Holder(Context context, ViewGroup parent){
|
public Holder(Context context, ViewGroup parent){
|
||||||
super(context, R.layout.display_item_gap, parent);
|
super(context, R.layout.display_item_gap, parent);
|
||||||
progressTop=findViewById(R.id.progress_top);
|
progress=findViewById(R.id.progress);
|
||||||
progressBottom=findViewById(R.id.progress_bottom);
|
text=findViewById(R.id.text);
|
||||||
textTop=findViewById(R.id.text_top);
|
itemView.setForeground(new SawtoothTearDrawable(context));
|
||||||
textBottom=findViewById(R.id.text_bottom);
|
|
||||||
top=findViewById(R.id.top);
|
|
||||||
top.setOnClickListener(this::onViewClick);
|
|
||||||
bottom=findViewById(R.id.bottom);
|
|
||||||
bottom.setOnClickListener(this::onViewClick);
|
|
||||||
gap=findViewById(R.id.gap);
|
|
||||||
gap.setForeground(new SawtoothTearDrawable(context));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBind(GapStatusDisplayItem item){
|
public void onBind(GapStatusDisplayItem item){
|
||||||
if(!item.loading){
|
text.setVisibility(item.loading ? View.GONE : View.VISIBLE);
|
||||||
progressBottom.setVisibility(View.GONE);
|
progress.setVisibility(item.loading ? View.VISIBLE : View.GONE);
|
||||||
progressTop.setVisibility(View.GONE);
|
|
||||||
textTop.setAlpha(1);
|
|
||||||
textBottom.setAlpha(1);
|
|
||||||
}
|
|
||||||
top.setClickable(!item.loading);
|
|
||||||
bottom.setClickable(!item.loading);
|
|
||||||
StatusDisplayItem next=getNextVisibleDisplayItem().orElse(null);
|
|
||||||
Instant dateBelow=next instanceof HeaderStatusDisplayItem h ? h.status.createdAt
|
|
||||||
: next instanceof ReblogOrReplyLineStatusDisplayItem l ? l.status.createdAt
|
|
||||||
: null;
|
|
||||||
String text=dateBelow!=null && item.status.createdAt!=null && dateBelow.isBefore(item.status.createdAt)
|
|
||||||
? UiUtils.formatPeriodBetween(item.parentFragment.getContext(), dateBelow, item.status.createdAt)
|
|
||||||
: null;
|
|
||||||
gap.setText(text);
|
|
||||||
int p=text==null ? V.dp(6) : V.dp(20);
|
|
||||||
gap.setPadding(p, p, p, p);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onViewClick(View v){
|
|
||||||
if(item.loading) return;
|
|
||||||
boolean isTop=v==top;
|
|
||||||
UiUtils.opacityOut(isTop ? textTop : textBottom);
|
|
||||||
V.setVisibilityAnimated((isTop ? progressTop : progressBottom), View.VISIBLE);
|
|
||||||
item.parentFragment.onGapClick(this, isTop);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onClick(){}
|
public void onClick(){
|
||||||
|
item.parentFragment.onGapClick(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,7 +66,6 @@ import me.grishka.appkit.api.ErrorResponse;
|
|||||||
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.CubicBezierInterpolator;
|
|
||||||
import me.grishka.appkit.utils.V;
|
import me.grishka.appkit.utils.V;
|
||||||
|
|
||||||
public class HeaderStatusDisplayItem extends StatusDisplayItem{
|
public class HeaderStatusDisplayItem extends StatusDisplayItem{
|
||||||
@@ -77,7 +76,7 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
|
|||||||
private CustomEmojiHelper emojiHelper=new CustomEmojiHelper();
|
private CustomEmojiHelper emojiHelper=new CustomEmojiHelper();
|
||||||
private SpannableStringBuilder parsedName;
|
private SpannableStringBuilder parsedName;
|
||||||
public final Status status;
|
public final Status status;
|
||||||
public boolean hasVisibilityToggle;
|
private boolean hasVisibilityToggle;
|
||||||
boolean needBottomPadding;
|
boolean needBottomPadding;
|
||||||
private CharSequence extraText;
|
private CharSequence extraText;
|
||||||
private Notification notification;
|
private Notification notification;
|
||||||
@@ -87,14 +86,10 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
|
|||||||
|
|
||||||
public HeaderStatusDisplayItem(String parentID, Account user, Instant createdAt, BaseStatusListFragment parentFragment, String accountID, Status status, CharSequence extraText, Notification notification, ScheduledStatus scheduledStatus){
|
public HeaderStatusDisplayItem(String parentID, Account user, Instant createdAt, BaseStatusListFragment parentFragment, String accountID, Status status, CharSequence extraText, Notification notification, ScheduledStatus scheduledStatus){
|
||||||
super(parentID, parentFragment);
|
super(parentID, parentFragment);
|
||||||
AccountSession session = AccountSessionManager.get(accountID);
|
user=scheduledStatus != null ? AccountSessionManager.getInstance().getAccount(accountID).self : user;
|
||||||
user=scheduledStatus != null ? session.self : user;
|
|
||||||
this.user=user;
|
this.user=user;
|
||||||
this.createdAt=createdAt;
|
this.createdAt=createdAt;
|
||||||
avaRequest=new UrlImageLoaderRequest(
|
avaRequest=new UrlImageLoaderRequest(GlobalUserPreferences.playGifs ? user.avatar : user.avatarStatic, V.dp(50), V.dp(50));
|
||||||
TextUtils.isEmpty(user.avatar) ? session.getDefaultAvatarUrl() :
|
|
||||||
GlobalUserPreferences.playGifs ? user.avatar : user.avatarStatic,
|
|
||||||
V.dp(50), V.dp(50));
|
|
||||||
this.accountID=accountID;
|
this.accountID=accountID;
|
||||||
parsedName=new SpannableStringBuilder(user.displayName);
|
parsedName=new SpannableStringBuilder(user.displayName);
|
||||||
this.status=status;
|
this.status=status;
|
||||||
@@ -295,6 +290,17 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
|
|||||||
UiUtils.enablePopupMenuIcons(activity, optionsMenu);
|
UiUtils.enablePopupMenuIcons(activity, optionsMenu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void populateAccountsMenu(Menu menu) {
|
||||||
|
List<AccountSession> sessions=AccountSessionManager.getInstance().getLoggedInAccounts();
|
||||||
|
sessions.stream().filter(s -> !s.getID().equals(item.accountID)).forEach(s -> {
|
||||||
|
String username = "@"+s.self.username+"@"+s.domain;
|
||||||
|
menu.add(username).setOnMenuItemClickListener(c->{
|
||||||
|
UiUtils.openURL(item.parentFragment.getActivity(), s.getID(), item.status.url, false);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressLint("SetTextI18n")
|
@SuppressLint("SetTextI18n")
|
||||||
@Override
|
@Override
|
||||||
public void onBind(HeaderStatusDisplayItem item){
|
public void onBind(HeaderStatusDisplayItem item){
|
||||||
@@ -321,13 +327,23 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
|
|||||||
botIcon.setColorFilter(username.getCurrentTextColor());
|
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);
|
||||||
visibility.setVisibility(item.hasVisibilityToggle ? View.VISIBLE : View.GONE);
|
|
||||||
if (item.hasVisibilityToggle){
|
if (item.hasVisibilityToggle){
|
||||||
boolean visible = item.status.sensitiveRevealed && (!item.status.hasSpoiler() || item.status.spoilerRevealed);
|
boolean hidden = !item.status.sensitiveRevealed || (item.status.hasSpoiler() && !item.status.spoilerRevealed);
|
||||||
visibility.setAlpha(visible ? 1 : 0f);
|
|
||||||
visibility.setScaleY(visible ? 1 : 0.8f);
|
// doing this because V.setVisibilityAnimated ignores changes between INVISIBLE and GONE
|
||||||
visibility.setScaleX(visible ? 1 : 0.8f);
|
int newVis=hidden ? View.INVISIBLE : View.VISIBLE;
|
||||||
visibility.setEnabled(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));
|
||||||
|
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.O){
|
||||||
|
visibility.setTooltipText(visibility.getContentDescription());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
visibility.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
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)){
|
||||||
@@ -377,42 +393,21 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
|
|||||||
markAsRead.setVisibility(View.GONE);
|
markAsRead.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
bindCollapseButton();
|
if (item.status == null || !item.status.textExpandable) {
|
||||||
|
collapseBtn.setVisibility(View.GONE);
|
||||||
|
} else {
|
||||||
|
String collapseText = item.parentFragment.getString(item.status.textExpanded ? R.string.sk_collapse : R.string.sk_expand);
|
||||||
|
collapseBtn.setVisibility(item.status.textExpandable ? View.VISIBLE : View.GONE);
|
||||||
|
collapseBtn.setContentDescription(collapseText);
|
||||||
|
if (GlobalUserPreferences.reduceMotion) collapseBtnIcon.setScaleY(item.status.textExpanded ? -1 : 1);
|
||||||
|
else collapseBtnIcon.animate().scaleY(item.status.textExpanded ? -1 : 1).start();
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) collapseBtn.setTooltipText(collapseText);
|
||||||
|
}
|
||||||
|
|
||||||
itemView.setPaddingRelative(itemView.getPaddingStart(), itemView.getPaddingTop(),
|
itemView.setPaddingRelative(itemView.getPaddingStart(), itemView.getPaddingTop(),
|
||||||
item.inset ? V.dp(10) : V.dp(4), itemView.getPaddingBottom());
|
item.inset ? V.dp(10) : V.dp(4), itemView.getPaddingBottom());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void bindCollapseButton(){
|
|
||||||
boolean expandable=item.status!=null && item.status.textExpandable;
|
|
||||||
collapseBtn.setVisibility(expandable ? View.VISIBLE : View.GONE);
|
|
||||||
if(expandable) {
|
|
||||||
bindCollapseButtonText();
|
|
||||||
collapseBtnIcon.setScaleY(item.status.textExpanded ? -1 : 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void bindCollapseButtonText(){
|
|
||||||
String collapseText = item.parentFragment.getString(item.status.textExpanded ? R.string.sk_collapse : R.string.sk_expand);
|
|
||||||
collapseBtn.setContentDescription(collapseText);
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) collapseBtn.setTooltipText(collapseText);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void animateExpandToggle(){
|
|
||||||
bindCollapseButtonText();
|
|
||||||
collapseBtnIcon.animate().scaleY(item.status.textExpanded ? -1 : 1).start();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void animateVisibilityToggle(boolean visible){
|
|
||||||
visibility.animate()
|
|
||||||
.alpha(visible ? 1 : 0)
|
|
||||||
.scaleX(visible ? 1 : 0.8f)
|
|
||||||
.scaleY(visible ? 1 : 0.8f)
|
|
||||||
.setInterpolator(CubicBezierInterpolator.DEFAULT)
|
|
||||||
.start();
|
|
||||||
visibility.setEnabled(visible);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setImage(int index, Drawable drawable){
|
public void setImage(int index, Drawable drawable){
|
||||||
if(index>0){
|
if(index>0){
|
||||||
@@ -435,8 +430,6 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void onAvaClick(View v){
|
private void onAvaClick(View v){
|
||||||
if (TextUtils.isEmpty(item.user.url))
|
|
||||||
return;
|
|
||||||
if (item.announcement != null) {
|
if (item.announcement != null) {
|
||||||
UiUtils.openURL(item.parentFragment.getActivity(), item.parentFragment.getAccountID(), item.user.url);
|
UiUtils.openURL(item.parentFragment.getActivity(), item.parentFragment.getAccountID(), item.user.url);
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.joinmastodon.android.ui.displayitems;
|
package org.joinmastodon.android.ui.displayitems;
|
||||||
|
|
||||||
import static org.joinmastodon.android.MastodonApp.context;
|
import static org.joinmastodon.android.MastodonApp.context;
|
||||||
|
import static org.joinmastodon.android.model.Notification.Type.PLEROMA_EMOJI_REACTION;
|
||||||
import static org.joinmastodon.android.ui.utils.UiUtils.generateFormattedString;
|
import static org.joinmastodon.android.ui.utils.UiUtils.generateFormattedString;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
@@ -8,6 +9,7 @@ import android.app.Activity;
|
|||||||
import android.content.res.ColorStateList;
|
import android.content.res.ColorStateList;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.text.Html;
|
||||||
import android.text.SpannableStringBuilder;
|
import android.text.SpannableStringBuilder;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
@@ -18,11 +20,7 @@ import android.widget.TextView;
|
|||||||
|
|
||||||
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.AccountSession;
|
|
||||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
|
||||||
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
||||||
import org.joinmastodon.android.fragments.NotificationsListFragment;
|
|
||||||
import org.joinmastodon.android.fragments.ProfileFragment;
|
import org.joinmastodon.android.fragments.ProfileFragment;
|
||||||
import org.joinmastodon.android.model.Emoji;
|
import org.joinmastodon.android.model.Emoji;
|
||||||
import org.joinmastodon.android.model.Notification;
|
import org.joinmastodon.android.model.Notification;
|
||||||
@@ -46,22 +44,16 @@ public class NotificationHeaderStatusDisplayItem extends StatusDisplayItem{
|
|||||||
private final String accountID;
|
private final String accountID;
|
||||||
private final CustomEmojiHelper emojiHelper=new CustomEmojiHelper();
|
private final CustomEmojiHelper emojiHelper=new CustomEmojiHelper();
|
||||||
private final CharSequence text;
|
private final CharSequence text;
|
||||||
private final CharSequence timestamp;
|
|
||||||
|
|
||||||
public NotificationHeaderStatusDisplayItem(String parentID, BaseStatusListFragment parentFragment, Notification notification, String accountID){
|
public NotificationHeaderStatusDisplayItem(String parentID, BaseStatusListFragment parentFragment, Notification notification, String accountID){
|
||||||
super(parentID, parentFragment);
|
super(parentID, parentFragment);
|
||||||
this.notification=notification;
|
this.notification=notification;
|
||||||
this.accountID=accountID;
|
this.accountID=accountID;
|
||||||
this.timestamp=notification.createdAt==null ? null : UiUtils.formatRelativeTimestamp(context, notification.createdAt);
|
|
||||||
|
|
||||||
if(notification.type==Notification.Type.POLL){
|
if(notification.type==Notification.Type.POLL){
|
||||||
text=parentFragment.getString(R.string.poll_ended);
|
text=parentFragment.getString(R.string.poll_ended);
|
||||||
}else{
|
}else{
|
||||||
AccountSession session = AccountSessionManager.get(accountID);
|
avaRequest=new UrlImageLoaderRequest(GlobalUserPreferences.playGifs ? notification.account.avatar : notification.account.avatarStatic, V.dp(50), V.dp(50));
|
||||||
avaRequest=new UrlImageLoaderRequest(
|
|
||||||
TextUtils.isEmpty(notification.account.avatar) ? session.getDefaultAvatarUrl() :
|
|
||||||
GlobalUserPreferences.playGifs ? notification.account.avatar : notification.account.avatarStatic,
|
|
||||||
V.dp(50), V.dp(50));
|
|
||||||
SpannableStringBuilder parsedName=new SpannableStringBuilder(notification.account.displayName);
|
SpannableStringBuilder parsedName=new SpannableStringBuilder(notification.account.displayName);
|
||||||
HtmlParser.parseCustomEmoji(parsedName, notification.account.emojis);
|
HtmlParser.parseCustomEmoji(parsedName, notification.account.emojis);
|
||||||
String str = parentFragment.getString(switch(notification.type){
|
String str = parentFragment.getString(switch(notification.type){
|
||||||
@@ -113,8 +105,8 @@ public class NotificationHeaderStatusDisplayItem extends StatusDisplayItem{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static class Holder extends StatusDisplayItem.Holder<NotificationHeaderStatusDisplayItem> implements ImageLoaderViewHolder{
|
public static class Holder extends StatusDisplayItem.Holder<NotificationHeaderStatusDisplayItem> implements ImageLoaderViewHolder{
|
||||||
private final ImageView icon, avatar, deleteNotification;
|
private final ImageView icon, avatar;
|
||||||
private final TextView text, timestamp;
|
private final TextView text;
|
||||||
private final int selectableItemBackground;
|
private final int selectableItemBackground;
|
||||||
|
|
||||||
public Holder(Activity activity, ViewGroup parent){
|
public Holder(Activity activity, ViewGroup parent){
|
||||||
@@ -122,16 +114,9 @@ public class NotificationHeaderStatusDisplayItem extends StatusDisplayItem{
|
|||||||
icon=findViewById(R.id.icon);
|
icon=findViewById(R.id.icon);
|
||||||
avatar=findViewById(R.id.avatar);
|
avatar=findViewById(R.id.avatar);
|
||||||
text=findViewById(R.id.text);
|
text=findViewById(R.id.text);
|
||||||
timestamp=findViewById(R.id.timestamp);
|
|
||||||
deleteNotification=findViewById(R.id.delete_notification);
|
|
||||||
|
|
||||||
avatar.setOutlineProvider(OutlineProviders.roundedRect(8));
|
avatar.setOutlineProvider(OutlineProviders.roundedRect(8));
|
||||||
avatar.setClipToOutline(true);
|
avatar.setClipToOutline(true);
|
||||||
deleteNotification.setOnClickListener(v->UiUtils.confirmDeleteNotification(activity, item.parentFragment.getAccountID(), item.notification, ()->{
|
|
||||||
if (item.parentFragment instanceof NotificationsListFragment fragment) {
|
|
||||||
fragment.removeNotification(item.notification);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
itemView.setOnClickListener(this::onItemClick);
|
itemView.setOnClickListener(this::onItemClick);
|
||||||
TypedValue outValue = new TypedValue();
|
TypedValue outValue = new TypedValue();
|
||||||
@@ -161,10 +146,9 @@ public class NotificationHeaderStatusDisplayItem extends StatusDisplayItem{
|
|||||||
@Override
|
@Override
|
||||||
public void onBind(NotificationHeaderStatusDisplayItem item){
|
public void onBind(NotificationHeaderStatusDisplayItem item){
|
||||||
text.setText(item.text);
|
text.setText(item.text);
|
||||||
timestamp.setText(item.timestamp);
|
|
||||||
avatar.setVisibility(item.notification.type==Notification.Type.POLL ? View.GONE : View.VISIBLE);
|
avatar.setVisibility(item.notification.type==Notification.Type.POLL ? View.GONE : View.VISIBLE);
|
||||||
icon.setImageResource(switch(item.notification.type){
|
icon.setImageResource(switch(item.notification.type){
|
||||||
case FAVORITE -> GlobalUserPreferences.likeIcon ? R.drawable.ic_fluent_heart_24_filled : R.drawable.ic_fluent_star_24_filled;
|
case FAVORITE -> R.drawable.ic_fluent_star_24_filled;
|
||||||
case REBLOG -> R.drawable.ic_fluent_arrow_repeat_all_24_filled;
|
case REBLOG -> R.drawable.ic_fluent_arrow_repeat_all_24_filled;
|
||||||
case FOLLOW, FOLLOW_REQUEST -> R.drawable.ic_fluent_person_add_24_filled;
|
case FOLLOW, FOLLOW_REQUEST -> R.drawable.ic_fluent_person_add_24_filled;
|
||||||
case POLL -> R.drawable.ic_fluent_poll_24_filled;
|
case POLL -> R.drawable.ic_fluent_poll_24_filled;
|
||||||
@@ -175,18 +159,15 @@ public class NotificationHeaderStatusDisplayItem extends StatusDisplayItem{
|
|||||||
default -> throw new IllegalStateException("Unexpected value: "+item.notification.type);
|
default -> throw new IllegalStateException("Unexpected value: "+item.notification.type);
|
||||||
});
|
});
|
||||||
icon.setImageTintList(ColorStateList.valueOf(UiUtils.getThemeColor(item.parentFragment.getActivity(), switch(item.notification.type){
|
icon.setImageTintList(ColorStateList.valueOf(UiUtils.getThemeColor(item.parentFragment.getActivity(), switch(item.notification.type){
|
||||||
case FAVORITE -> GlobalUserPreferences.likeIcon ? R.attr.colorLike : R.attr.colorFavorite;
|
case FAVORITE -> R.attr.colorFavorite;
|
||||||
case REBLOG -> R.attr.colorBoost;
|
case REBLOG -> R.attr.colorBoost;
|
||||||
case POLL -> R.attr.colorPoll;
|
case POLL -> R.attr.colorPoll;
|
||||||
default -> android.R.attr.colorAccent;
|
default -> android.R.attr.colorAccent;
|
||||||
})));
|
})));
|
||||||
deleteNotification.setVisibility(GlobalUserPreferences.enableDeleteNotifications && item.notification != null ? View.VISIBLE : View.GONE);
|
|
||||||
itemView.setBackgroundResource(item.notification.type != Notification.Type.POLL
|
itemView.setBackgroundResource(item.notification.type != Notification.Type.POLL
|
||||||
&& item.notification.type != Notification.Type.REPORT ?
|
&& item.notification.type != Notification.Type.REPORT ?
|
||||||
selectableItemBackground : 0);
|
selectableItemBackground : 0);
|
||||||
itemView.setClickable(item.notification.type != Notification.Type.POLL);
|
itemView.setClickable(item.notification.type != Notification.Type.POLL);
|
||||||
itemView.setPaddingRelative(itemView.getPaddingStart(), itemView.getPaddingTop(),
|
|
||||||
GlobalUserPreferences.enableDeleteNotifications ? V.dp(4) : V.dp(16), itemView.getPaddingBottom());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onItemClick(View v) {
|
public void onItemClick(View v) {
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ public class PollOptionStatusDisplayItem extends StatusDisplayItem{
|
|||||||
private final TextView text, percent;
|
private final TextView text, percent;
|
||||||
private final View button;
|
private final View button;
|
||||||
private final ImageView icon;
|
private final ImageView icon;
|
||||||
private final Drawable progressBg;
|
private final Drawable progressBg, progressBgInset;
|
||||||
|
|
||||||
public Holder(Activity activity, ViewGroup parent){
|
public Holder(Activity activity, ViewGroup parent){
|
||||||
super(activity, R.layout.display_item_poll_option, parent);
|
super(activity, R.layout.display_item_poll_option, parent);
|
||||||
@@ -77,8 +77,9 @@ public class PollOptionStatusDisplayItem extends StatusDisplayItem{
|
|||||||
icon=findViewById(R.id.icon);
|
icon=findViewById(R.id.icon);
|
||||||
button=findViewById(R.id.button);
|
button=findViewById(R.id.button);
|
||||||
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();
|
||||||
itemView.setOnClickListener(this::onButtonClick);
|
itemView.setOnClickListener(this::onButtonClick);
|
||||||
button.setOutlineProvider(OutlineProviders.roundedRect(20));
|
button.setOutlineProvider(OutlineProviders.M3_BUTTON);
|
||||||
button.setClipToOutline(true);
|
button.setClipToOutline(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,17 +93,22 @@ public class PollOptionStatusDisplayItem extends StatusDisplayItem{
|
|||||||
item.showResults ? R.drawable.ic_poll_option_button : R.drawable.ic_fluent_radio_button_24_selector
|
item.showResults ? R.drawable.ic_poll_option_button : R.drawable.ic_fluent_radio_button_24_selector
|
||||||
));
|
));
|
||||||
if(item.showResults){
|
if(item.showResults){
|
||||||
Drawable bg=progressBg;
|
Drawable bg=item.inset ? progressBgInset : progressBg;
|
||||||
bg.setLevel(Math.round(10000f*item.votesFraction));
|
bg.setLevel(Math.round(10000f*item.votesFraction));
|
||||||
button.setBackground(bg);
|
button.setBackground(bg);
|
||||||
itemView.setSelected(item.poll.ownVotes!=null && item.poll.ownVotes.contains(item.optionIndex));
|
itemView.setSelected(item.poll.ownVotes!=null && item.poll.ownVotes.contains(item.optionIndex));
|
||||||
percent.setText(String.format(Locale.getDefault(), "%d%%", Math.round(item.votesFraction*100f)));
|
percent.setText(String.format(Locale.getDefault(), "%d%%", Math.round(item.votesFraction*100f)));
|
||||||
}else{
|
}else{
|
||||||
itemView.setSelected(item.poll.selectedOptions!=null && item.poll.selectedOptions.contains(item.option));
|
itemView.setSelected(item.poll.selectedOptions!=null && item.poll.selectedOptions.contains(item.option));
|
||||||
button.setBackgroundResource(R.drawable.bg_poll_option_clickable);
|
button.setBackgroundResource(item.inset ? R.drawable.bg_poll_option_clickable_inset : R.drawable.bg_poll_option_clickable);
|
||||||
|
}
|
||||||
|
if(item.inset){
|
||||||
|
text.setTextColor(itemView.getContext().getColorStateList(R.color.poll_option_text_inset));
|
||||||
|
percent.setTextColor(itemView.getContext().getColorStateList(R.color.poll_option_text_inset));
|
||||||
|
}else{
|
||||||
|
text.setTextColor(UiUtils.getThemeColor(itemView.getContext(), android.R.attr.textColorPrimary));
|
||||||
|
percent.setTextColor(UiUtils.getThemeColor(itemView.getContext(), R.attr.colorM3OnSecondaryContainer));
|
||||||
}
|
}
|
||||||
text.setTextColor(UiUtils.getThemeColor(itemView.getContext(), android.R.attr.textColorPrimary));
|
|
||||||
percent.setTextColor(UiUtils.getThemeColor(itemView.getContext(), R.attr.colorM3OnSecondaryContainer));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -15,10 +15,8 @@ import android.widget.TextView;
|
|||||||
|
|
||||||
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.AccountSessionManager;
|
|
||||||
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
||||||
import org.joinmastodon.android.model.Emoji;
|
import org.joinmastodon.android.model.Emoji;
|
||||||
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.text.HtmlParser;
|
||||||
import org.joinmastodon.android.ui.utils.CustomEmojiHelper;
|
import org.joinmastodon.android.ui.utils.CustomEmojiHelper;
|
||||||
@@ -45,21 +43,18 @@ public class ReblogOrReplyLineStatusDisplayItem extends StatusDisplayItem{
|
|||||||
public boolean needBottomPadding;
|
public boolean needBottomPadding;
|
||||||
ReblogOrReplyLineStatusDisplayItem extra;
|
ReblogOrReplyLineStatusDisplayItem extra;
|
||||||
CharSequence fullText;
|
CharSequence fullText;
|
||||||
Status status;
|
|
||||||
|
|
||||||
public ReblogOrReplyLineStatusDisplayItem(String parentID, BaseStatusListFragment parentFragment, CharSequence text, List<Emoji> emojis, @DrawableRes int icon, StatusPrivacy visibility, @Nullable View.OnClickListener handleClick, Status status) {
|
public ReblogOrReplyLineStatusDisplayItem(String parentID, BaseStatusListFragment parentFragment, CharSequence text, List<Emoji> emojis, @DrawableRes int icon, StatusPrivacy visibility, @Nullable View.OnClickListener handleClick) {
|
||||||
this(parentID, parentFragment, text, emojis, icon, visibility, handleClick, text, status);
|
this(parentID, parentFragment, text, emojis, icon, visibility, handleClick, text);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReblogOrReplyLineStatusDisplayItem(String parentID, BaseStatusListFragment parentFragment, CharSequence text, List<Emoji> emojis, @DrawableRes int icon, StatusPrivacy visibility, @Nullable View.OnClickListener handleClick, CharSequence fullText, Status status) {
|
public ReblogOrReplyLineStatusDisplayItem(String parentID, BaseStatusListFragment parentFragment, CharSequence text, List<Emoji> emojis, @DrawableRes int icon, StatusPrivacy visibility, @Nullable View.OnClickListener handleClick, CharSequence fullText) {
|
||||||
super(parentID, parentFragment);
|
super(parentID, parentFragment);
|
||||||
SpannableStringBuilder ssb=new SpannableStringBuilder(text);
|
SpannableStringBuilder ssb=new SpannableStringBuilder(text);
|
||||||
if(AccountSessionManager.get(parentFragment.getAccountID()).getLocalPreferences().customEmojiInNames)
|
HtmlParser.parseCustomEmoji(ssb, emojis);
|
||||||
HtmlParser.parseCustomEmoji(ssb, emojis);
|
|
||||||
this.text=ssb;
|
this.text=ssb;
|
||||||
emojiHelper.setText(ssb);
|
emojiHelper.setText(ssb);
|
||||||
this.icon=icon;
|
this.icon=icon;
|
||||||
this.status=status;
|
|
||||||
this.handleClick=handleClick;
|
this.handleClick=handleClick;
|
||||||
TypedValue outValue = new TypedValue();
|
TypedValue outValue = new TypedValue();
|
||||||
context.getTheme().resolveAttribute(android.R.attr.selectableItemBackground, outValue, true);
|
context.getTheme().resolveAttribute(android.R.attr.selectableItemBackground, outValue, true);
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ import android.graphics.drawable.LayerDrawable;
|
|||||||
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.ImageView;
|
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.GlobalUserPreferences;
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
@@ -29,13 +29,11 @@ public class SpoilerStatusDisplayItem extends StatusDisplayItem{
|
|||||||
private final CharSequence parsedTitle;
|
private final CharSequence parsedTitle;
|
||||||
private final CustomEmojiHelper emojiHelper;
|
private final CustomEmojiHelper emojiHelper;
|
||||||
private final Type type;
|
private final Type type;
|
||||||
private final int attachmentCount;
|
|
||||||
|
|
||||||
public SpoilerStatusDisplayItem(String parentID, BaseStatusListFragment<?> parentFragment, String title, Status statusForContent, Type type){
|
public SpoilerStatusDisplayItem(String parentID, BaseStatusListFragment<?> parentFragment, String title, Status statusForContent, Type type){
|
||||||
super(parentID, parentFragment);
|
super(parentID, parentFragment);
|
||||||
this.status=statusForContent;
|
this.status=statusForContent;
|
||||||
this.type=type;
|
this.type=type;
|
||||||
this.attachmentCount=statusForContent.mediaAttachments.size();
|
|
||||||
if(TextUtils.isEmpty(title)){
|
if(TextUtils.isEmpty(title)){
|
||||||
parsedTitle=HtmlParser.parseCustomEmoji(statusForContent.spoilerText, statusForContent.emojis);
|
parsedTitle=HtmlParser.parseCustomEmoji(statusForContent.spoilerText, statusForContent.emojis);
|
||||||
emojiHelper=new CustomEmojiHelper();
|
emojiHelper=new CustomEmojiHelper();
|
||||||
@@ -64,14 +62,12 @@ public class SpoilerStatusDisplayItem extends StatusDisplayItem{
|
|||||||
public static class Holder extends StatusDisplayItem.Holder<SpoilerStatusDisplayItem> implements ImageLoaderViewHolder{
|
public static class Holder extends StatusDisplayItem.Holder<SpoilerStatusDisplayItem> implements ImageLoaderViewHolder{
|
||||||
private final TextView title, action;
|
private final TextView title, action;
|
||||||
private final View button;
|
private final View button;
|
||||||
private final ImageView mediaIcon;
|
|
||||||
|
|
||||||
public Holder(Context context, ViewGroup parent, Type type){
|
public Holder(Context context, ViewGroup parent, Type type){
|
||||||
super(context, R.layout.display_item_spoiler, parent);
|
super(context, R.layout.display_item_spoiler, parent);
|
||||||
title=findViewById(R.id.spoiler_title);
|
title=findViewById(R.id.spoiler_title);
|
||||||
action=findViewById(R.id.spoiler_action);
|
action=findViewById(R.id.spoiler_action);
|
||||||
button=findViewById(R.id.spoiler_button);
|
button=findViewById(R.id.spoiler_button);
|
||||||
mediaIcon=findViewById(R.id.media_icon);
|
|
||||||
|
|
||||||
button.setOutlineProvider(OutlineProviders.roundedRect(8));
|
button.setOutlineProvider(OutlineProviders.roundedRect(8));
|
||||||
button.setClipToOutline(true);
|
button.setClipToOutline(true);
|
||||||
@@ -98,10 +94,6 @@ public class SpoilerStatusDisplayItem extends StatusDisplayItem{
|
|||||||
itemView.getPaddingRight(),
|
itemView.getPaddingRight(),
|
||||||
item.inset ? itemView.getPaddingTop() : 0
|
item.inset ? itemView.getPaddingTop() : 0
|
||||||
);
|
);
|
||||||
mediaIcon.setVisibility(item.attachmentCount > 0 ? View.VISIBLE : View.GONE);
|
|
||||||
mediaIcon.setImageResource(item.attachmentCount > 1
|
|
||||||
? R.drawable.ic_fluent_image_multiple_24_regular
|
|
||||||
: R.drawable.ic_fluent_image_24_regular);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ 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;
|
||||||
@@ -55,7 +54,7 @@ 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,
|
||||||
@@ -136,7 +135,7 @@ public abstract class StatusDisplayItem{
|
|||||||
: fragment.getString(R.string.in_reply_to, account.displayName);
|
: fragment.getString(R.string.in_reply_to, account.displayName);
|
||||||
return new ReblogOrReplyLineStatusDisplayItem(
|
return new ReblogOrReplyLineStatusDisplayItem(
|
||||||
parentID, fragment, text, account == null ? List.of() : account.emojis,
|
parentID, fragment, text, account == null ? List.of() : account.emojis,
|
||||||
R.drawable.ic_fluent_arrow_reply_20sp_filled, null, null, fullText, status
|
R.drawable.ic_fluent_arrow_reply_20sp_filled, null, null, fullText
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,7 +145,7 @@ public abstract class StatusDisplayItem{
|
|||||||
Status statusForContent=status.getContentStatus();
|
Status statusForContent=status.getContentStatus();
|
||||||
Bundle args=new Bundle();
|
Bundle args=new Bundle();
|
||||||
args.putString("account", accountID);
|
args.putString("account", accountID);
|
||||||
ScheduledStatus scheduledStatus = parentObject instanceof ScheduledStatus s ? s : null;
|
ScheduledStatus scheduledStatus = parentObject instanceof ScheduledStatus ? (ScheduledStatus) parentObject : null;
|
||||||
|
|
||||||
HeaderStatusDisplayItem header=null;
|
HeaderStatusDisplayItem header=null;
|
||||||
boolean hideCounts=!AccountSessionManager.get(accountID).getLocalPreferences().showInteractionCounts;
|
boolean hideCounts=!AccountSessionManager.get(accountID).getLocalPreferences().showInteractionCounts;
|
||||||
@@ -168,7 +167,7 @@ public abstract class StatusDisplayItem{
|
|||||||
items.add(new ReblogOrReplyLineStatusDisplayItem(parentID, fragment, text, status.account.emojis, R.drawable.ic_fluent_arrow_repeat_all_20sp_filled, isOwnPost ? status.visibility : null, i->{
|
items.add(new ReblogOrReplyLineStatusDisplayItem(parentID, fragment, text, status.account.emojis, R.drawable.ic_fluent_arrow_repeat_all_20sp_filled, isOwnPost ? status.visibility : null, i->{
|
||||||
args.putParcelable("profileAccount", Parcels.wrap(status.account));
|
args.putParcelable("profileAccount", Parcels.wrap(status.account));
|
||||||
Nav.go(fragment.getActivity(), ProfileFragment.class, args);
|
Nav.go(fragment.getActivity(), ProfileFragment.class, args);
|
||||||
}, fullText, status));
|
}, fullText));
|
||||||
} else if (!(status.tags.isEmpty() ||
|
} else if (!(status.tags.isEmpty() ||
|
||||||
fragment instanceof HashtagTimelineFragment ||
|
fragment instanceof HashtagTimelineFragment ||
|
||||||
fragment instanceof ListTimelineFragment
|
fragment instanceof ListTimelineFragment
|
||||||
@@ -181,8 +180,10 @@ public abstract class StatusDisplayItem{
|
|||||||
.ifPresent(hashtag -> items.add(new ReblogOrReplyLineStatusDisplayItem(
|
.ifPresent(hashtag -> items.add(new ReblogOrReplyLineStatusDisplayItem(
|
||||||
parentID, fragment, hashtag.name, List.of(),
|
parentID, fragment, hashtag.name, List.of(),
|
||||||
R.drawable.ic_fluent_number_symbol_20sp_filled, null,
|
R.drawable.ic_fluent_number_symbol_20sp_filled, null,
|
||||||
i->UiUtils.openHashtagTimeline(fragment.getActivity(), accountID, hashtag),
|
i -> {
|
||||||
status
|
args.putString("hashtag", hashtag.name);
|
||||||
|
Nav.go(fragment.getActivity(), HashtagTimelineFragment.class, args);
|
||||||
|
}
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -202,15 +203,14 @@ public abstract class StatusDisplayItem{
|
|||||||
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
|
||||||
items.add(header=new HeaderStatusDisplayItem(parentID, statusForContent.account, statusForContent.createdAt, fragment, accountID, statusForContent, null, parentObject instanceof Notification n ? n : null, scheduledStatus));
|
items.add(header=new HeaderStatusDisplayItem(parentID, statusForContent.account, statusForContent.createdAt, fragment, accountID, statusForContent, null, null, scheduledStatus));
|
||||||
}
|
}
|
||||||
|
|
||||||
LegacyFilter applyingFilter=null;
|
boolean filtered=false;
|
||||||
if(status.filtered!=null){
|
if(status.filtered!=null){
|
||||||
for(FilterResult filter:status.filtered){
|
for(FilterResult filter:status.filtered){
|
||||||
LegacyFilter f=filter.filter;
|
if(filter.filter.isActive()){
|
||||||
if(f.isActive() && filterContext != null && f.context.contains(filterContext)){
|
filtered=true;
|
||||||
applyingFilter=f;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -293,8 +293,8 @@ public abstract class StatusDisplayItem{
|
|||||||
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!=null && !(fragment instanceof ThreadFragment))
|
if(status.hasGapAfter && !(fragment instanceof ThreadFragment))
|
||||||
items.add(new GapStatusDisplayItem(parentID, fragment, status));
|
items.add(new GapStatusDisplayItem(parentID, fragment));
|
||||||
}
|
}
|
||||||
int i=1;
|
int i=1;
|
||||||
boolean inset=(flags & FLAG_INSET)!=0;
|
boolean inset=(flags & FLAG_INSET)!=0;
|
||||||
@@ -317,7 +317,14 @@ public abstract class StatusDisplayItem{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return applyingFilter==null ? items :
|
LegacyFilter applyingFilter = null;
|
||||||
|
if (!statusForContent.filterRevealed) {
|
||||||
|
StatusFilterPredicate predicate = new StatusFilterPredicate(accountID, filterContext, FilterAction.WARN);
|
||||||
|
statusForContent.filterRevealed = predicate.test(status);
|
||||||
|
applyingFilter = predicate.getApplyingFilter();
|
||||||
|
}
|
||||||
|
|
||||||
|
return statusForContent.filterRevealed ? items :
|
||||||
new ArrayList<>(List.of(new WarningFilteredStatusDisplayItem(parentID, fragment, statusForContent, items, applyingFilter)));
|
new ArrayList<>(List.of(new WarningFilteredStatusDisplayItem(parentID, fragment, statusForContent, items, applyingFilter)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -390,11 +397,10 @@ public abstract class StatusDisplayItem{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Optional<StatusDisplayItem> getDisplayItemOffset(int offset){
|
public Optional<StatusDisplayItem> getDisplayItemOffset(int offset){
|
||||||
|
int nextPos=getAbsoluteAdapterPosition() + offset;
|
||||||
List<StatusDisplayItem> displayItems=item.parentFragment.getDisplayItems();
|
List<StatusDisplayItem> displayItems=item.parentFragment.getDisplayItems();
|
||||||
int thisPos=displayItems.indexOf(item);
|
return displayItems.size() > nextPos
|
||||||
int offsetPos=thisPos + offset;
|
? Optional.of(displayItems.get(nextPos))
|
||||||
return displayItems.size() > offsetPos && thisPos >= 0 && offsetPos >= 0
|
|
||||||
? Optional.of(displayItems.get(offsetPos))
|
|
||||||
: Optional.empty();
|
: Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,28 +3,40 @@ package org.joinmastodon.android.ui.displayitems;
|
|||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.graphics.drawable.Animatable;
|
import android.graphics.drawable.Animatable;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.text.TextUtils;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.ViewStub;
|
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
import android.widget.ProgressBar;
|
|
||||||
import android.widget.ScrollView;
|
import android.widget.ScrollView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.github.bottomSoftwareFoundation.bottom.Bottom;
|
||||||
|
import com.github.bottomSoftwareFoundation.bottom.TranslationError;
|
||||||
|
|
||||||
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.requests.statuses.TranslateStatus;
|
||||||
|
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.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
import org.joinmastodon.android.model.Translation;
|
import org.joinmastodon.android.model.StatusPrivacy;
|
||||||
|
import org.joinmastodon.android.model.TranslatedStatus;
|
||||||
import org.joinmastodon.android.ui.text.HtmlParser;
|
import org.joinmastodon.android.ui.text.HtmlParser;
|
||||||
import org.joinmastodon.android.ui.utils.CustomEmojiHelper;
|
import org.joinmastodon.android.ui.utils.CustomEmojiHelper;
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
import org.joinmastodon.android.ui.views.LinkedTextView;
|
import org.joinmastodon.android.ui.views.LinkedTextView;
|
||||||
|
import org.joinmastodon.android.utils.StatusTextEncoder;
|
||||||
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import me.grishka.appkit.api.Callback;
|
||||||
|
import me.grishka.appkit.api.ErrorResponse;
|
||||||
import me.grishka.appkit.imageloader.ImageLoaderViewHolder;
|
import me.grishka.appkit.imageloader.ImageLoaderViewHolder;
|
||||||
import me.grishka.appkit.imageloader.MovieDrawable;
|
import me.grishka.appkit.imageloader.MovieDrawable;
|
||||||
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
|
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
|
||||||
@@ -34,19 +46,26 @@ import me.grishka.appkit.utils.V;
|
|||||||
public class TextStatusDisplayItem extends StatusDisplayItem{
|
public class TextStatusDisplayItem extends StatusDisplayItem{
|
||||||
private CharSequence text;
|
private CharSequence text;
|
||||||
private CustomEmojiHelper emojiHelper=new CustomEmojiHelper();
|
private CustomEmojiHelper emojiHelper=new CustomEmojiHelper();
|
||||||
private CharSequence translatedText;
|
|
||||||
private CustomEmojiHelper translationEmojiHelper=new CustomEmojiHelper();
|
|
||||||
public boolean textSelectable;
|
public boolean textSelectable;
|
||||||
public boolean reduceTopPadding;
|
public boolean reduceTopPadding;
|
||||||
public boolean disableTranslate;
|
|
||||||
public final Status status;
|
public final Status status;
|
||||||
|
public boolean disableTranslate, translationShown;
|
||||||
|
private AccountSession session;
|
||||||
|
public static final Pattern BOTTOM_TEXT_PATTERN = Pattern.compile("(?:[\uD83E\uDEC2\uD83D\uDC96✨\uD83E\uDD7A,]+|❤️)(?:\uD83D\uDC49\uD83D\uDC48(?:[\uD83E\uDEC2\uD83D\uDC96✨\uD83E\uDD7A,]+|❤️))*\uD83D\uDC49\uD83D\uDC48");
|
||||||
|
|
||||||
public TextStatusDisplayItem(String parentID, CharSequence text, BaseStatusListFragment parentFragment, Status status, boolean disableTranslate){
|
public TextStatusDisplayItem(String parentID, CharSequence text, BaseStatusListFragment parentFragment, Status status, boolean disableTranslate){
|
||||||
super(parentID, parentFragment);
|
super(parentID, parentFragment);
|
||||||
this.text=text;
|
this.text=text;
|
||||||
this.status=status;
|
this.status=status;
|
||||||
this.disableTranslate=disableTranslate;
|
this.disableTranslate=disableTranslate;
|
||||||
|
this.translationShown=status.translationShown;
|
||||||
emojiHelper.setText(text);
|
emojiHelper.setText(text);
|
||||||
|
session = AccountSessionManager.getInstance().getAccount(parentFragment.getAccountID());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTranslationShown(boolean translationShown) {
|
||||||
|
this.translationShown = translationShown;
|
||||||
|
status.translationShown = translationShown;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -56,47 +75,38 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getImageCount(){
|
public int getImageCount(){
|
||||||
return getCurrentEmojiHelper().getImageCount();
|
return emojiHelper.getImageCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ImageLoaderRequest getImageRequest(int index){
|
public ImageLoaderRequest getImageRequest(int index){
|
||||||
return getCurrentEmojiHelper().getImageRequest(index);
|
return emojiHelper.getImageRequest(index);
|
||||||
}
|
|
||||||
|
|
||||||
public void setTranslatedText(String text){
|
|
||||||
Status statusForContent=status.getContentStatus();
|
|
||||||
translatedText=HtmlParser.parse(text, statusForContent.emojis, statusForContent.mentions, statusForContent.tags, parentFragment.getAccountID());
|
|
||||||
translationEmojiHelper.setText(translatedText);
|
|
||||||
}
|
|
||||||
|
|
||||||
private CustomEmojiHelper getCurrentEmojiHelper(){
|
|
||||||
return status.translationState==Status.TranslationState.SHOWN ? translationEmojiHelper : emojiHelper;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Holder extends StatusDisplayItem.Holder<TextStatusDisplayItem> implements ImageLoaderViewHolder{
|
public static class Holder extends StatusDisplayItem.Holder<TextStatusDisplayItem> implements ImageLoaderViewHolder{
|
||||||
private final LinkedTextView text;
|
private final LinkedTextView text;
|
||||||
private final ViewStub translationFooterStub;
|
private final TextView translateInfo, readMore;
|
||||||
private View translationFooter, translationButtonWrap;
|
private final View textWrap, translateWrap, translateProgress;
|
||||||
private TextView translationInfo;
|
private final Button translateButton;
|
||||||
private Button translationButton;
|
private final ScrollView textScrollView;
|
||||||
private ProgressBar translationProgress;
|
|
||||||
|
|
||||||
private final float textMaxHeight;
|
private final float textMaxHeight, textCollapsedHeight;
|
||||||
private final LinearLayout.LayoutParams collapseParams, wrapParams;
|
private final LinearLayout.LayoutParams collapseParams, wrapParams;
|
||||||
private final ViewGroup parent;
|
private final ViewGroup parent;
|
||||||
private final TextView readMore;
|
|
||||||
private final ScrollView textScrollView;
|
|
||||||
|
|
||||||
public Holder(Activity activity, ViewGroup parent){
|
public Holder(Activity activity, ViewGroup parent){
|
||||||
super(activity, R.layout.display_item_text, parent);
|
super(activity, R.layout.display_item_text, parent);
|
||||||
this.parent=parent;
|
this.parent=parent;
|
||||||
text=findViewById(R.id.text);
|
text=findViewById(R.id.text);
|
||||||
translationFooterStub=findViewById(R.id.translation_info);
|
textWrap = (LinearLayout) itemView;
|
||||||
|
translateWrap=findViewById(R.id.translate_wrap);
|
||||||
|
translateButton=findViewById(R.id.translate_btn);
|
||||||
|
translateInfo=findViewById(R.id.translate_info);
|
||||||
|
translateProgress=findViewById(R.id.translate_progress);
|
||||||
textScrollView=findViewById(R.id.text_scroll_view);
|
textScrollView=findViewById(R.id.text_scroll_view);
|
||||||
readMore=findViewById(R.id.read_more);
|
readMore=findViewById(R.id.read_more);
|
||||||
textMaxHeight=activity.getResources().getDimension(R.dimen.text_max_height);
|
textMaxHeight=activity.getResources().getDimension(R.dimen.text_max_height);
|
||||||
float textCollapsedHeight=activity.getResources().getDimension(R.dimen.text_collapsed_height);
|
textCollapsedHeight=activity.getResources().getDimension(R.dimen.text_collapsed_height);
|
||||||
collapseParams=new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, (int) textCollapsedHeight);
|
collapseParams=new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, (int) textCollapsedHeight);
|
||||||
wrapParams=new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
wrapParams=new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||||
readMore.setOnClickListener(v -> item.parentFragment.onToggleExpanded(item.status, getItemID()));
|
readMore.setOnClickListener(v -> item.parentFragment.onToggleExpanded(item.status, getItemID()));
|
||||||
@@ -104,21 +114,83 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBind(TextStatusDisplayItem item){
|
public void onBind(TextStatusDisplayItem item){
|
||||||
if(item.status.translationState==Status.TranslationState.SHOWN){
|
boolean hasSpoiler = !TextUtils.isEmpty(item.status.spoilerText);
|
||||||
if(item.translatedText==null){
|
text.setText(item.translationShown
|
||||||
item.setTranslatedText(item.status.translation.content);
|
? HtmlParser.parse(item.status.translation.content, item.status.emojis, item.status.mentions, item.status.tags, item.parentFragment.getAccountID())
|
||||||
}
|
: item.text);
|
||||||
text.setText(item.translatedText);
|
text.setTextIsSelectable(item.textSelectable);
|
||||||
}else{
|
if (item.textSelectable) {
|
||||||
text.setText(item.text);
|
textScrollView.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT));
|
||||||
}
|
}
|
||||||
text.setTextIsSelectable(false);
|
|
||||||
if(item.textSelectable) itemView.post(() -> text.setTextIsSelectable(true));
|
|
||||||
text.setInvalidateOnEveryFrame(false);
|
text.setInvalidateOnEveryFrame(false);
|
||||||
itemView.setClickable(false);
|
itemView.setClickable(false);
|
||||||
itemView.setPadding(itemView.getPaddingLeft(), item.reduceTopPadding ? V.dp(6) : V.dp(12), itemView.getPaddingRight(), itemView.getPaddingBottom());
|
itemView.setPadding(itemView.getPaddingLeft(), item.reduceTopPadding ? V.dp(6) : V.dp(12), itemView.getPaddingRight(), itemView.getPaddingBottom());
|
||||||
text.setTextColor(UiUtils.getThemeColor(text.getContext(), item.inset ? R.attr.colorM3OnSurfaceVariant : R.attr.colorM3OnSurface));
|
text.setTextColor(UiUtils.getThemeColor(text.getContext(), item.inset ? R.attr.colorM3OnSurfaceVariant : R.attr.colorM3OnSurface));
|
||||||
updateTranslation(false);
|
|
||||||
|
Instance instanceInfo = AccountSessionManager.getInstance().getInstanceInfo(item.session.domain);
|
||||||
|
boolean translateEnabled = !item.disableTranslate && instanceInfo != null &&
|
||||||
|
instanceInfo.v2 != null && instanceInfo.v2.configuration.translation != null &&
|
||||||
|
instanceInfo.v2.configuration.translation.enabled;
|
||||||
|
String bottomText = null;
|
||||||
|
try {
|
||||||
|
bottomText = BOTTOM_TEXT_PATTERN.matcher(item.status.getStrippedText()).find()
|
||||||
|
? new StatusTextEncoder(Bottom::decode).decode(item.status.getStrippedText(), BOTTOM_TEXT_PATTERN)
|
||||||
|
: null;
|
||||||
|
} catch (TranslationError ignored) {}
|
||||||
|
|
||||||
|
boolean translateVisible = (bottomText != null || (
|
||||||
|
translateEnabled &&
|
||||||
|
!item.status.visibility.isLessVisibleThan(StatusPrivacy.UNLISTED) &&
|
||||||
|
item.status.language != null &&
|
||||||
|
// todo: compare to mastodon locale instead (how do i query that?!)
|
||||||
|
!item.status.language.equalsIgnoreCase(Locale.getDefault().getLanguage())))
|
||||||
|
&& (!GlobalUserPreferences.translateButtonOpenedOnly || item.textSelectable);
|
||||||
|
translateWrap.setVisibility(translateVisible ? View.VISIBLE : View.GONE);
|
||||||
|
translateButton.setText(item.translationShown ? R.string.sk_translate_show_original : R.string.sk_translate_post);
|
||||||
|
translateInfo.setText(item.translationShown ? itemView.getResources().getString(R.string.sk_translated_using, bottomText != null ? "bottom-java" : item.status.translation.provider) : "");
|
||||||
|
String finalBottomText = bottomText;
|
||||||
|
translateButton.setOnClickListener(v->{
|
||||||
|
if (item.status.translation == null) {
|
||||||
|
if (finalBottomText != null) {
|
||||||
|
try {
|
||||||
|
item.status.translation = new TranslatedStatus();
|
||||||
|
item.status.translation.content = finalBottomText;
|
||||||
|
item.setTranslationShown(true);
|
||||||
|
} catch (TranslationError err) {
|
||||||
|
item.status.translation = null;
|
||||||
|
Toast.makeText(itemView.getContext(), err.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
rebind();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
translateProgress.setVisibility(View.VISIBLE);
|
||||||
|
translateButton.setClickable(false);
|
||||||
|
translateButton.animate().alpha(0.5f).setInterpolator(CubicBezierInterpolator.DEFAULT).setDuration(150).start();
|
||||||
|
new TranslateStatus(item.status.id).setCallback(new Callback<>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(TranslatedStatus translatedStatus) {
|
||||||
|
item.status.translation = translatedStatus;
|
||||||
|
item.setTranslationShown(true);
|
||||||
|
if (item.parentFragment.getActivity() == null) return;
|
||||||
|
translateProgress.setVisibility(View.GONE);
|
||||||
|
translateButton.setClickable(true);
|
||||||
|
translateButton.animate().alpha(1).setInterpolator(CubicBezierInterpolator.DEFAULT).setDuration(50).start();
|
||||||
|
rebind();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(ErrorResponse error) {
|
||||||
|
translateProgress.setVisibility(View.GONE);
|
||||||
|
translateButton.setClickable(true);
|
||||||
|
translateButton.animate().alpha(1).setInterpolator(CubicBezierInterpolator.DEFAULT).setDuration(50).start();
|
||||||
|
error.showToast(itemView.getContext());
|
||||||
|
}
|
||||||
|
}).exec(item.parentFragment.getAccountID());
|
||||||
|
} else {
|
||||||
|
item.setTranslationShown(!item.translationShown);
|
||||||
|
rebind();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
@@ -152,19 +224,18 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
|
|||||||
|
|
||||||
if (GlobalUserPreferences.collapseLongPosts && !item.status.textExpandable) {
|
if (GlobalUserPreferences.collapseLongPosts && !item.status.textExpandable) {
|
||||||
boolean tooBig = text.getMeasuredHeight() > textMaxHeight;
|
boolean tooBig = text.getMeasuredHeight() > textMaxHeight;
|
||||||
boolean expandable = tooBig && !item.status.hasSpoiler();
|
boolean expandable = tooBig && !hasSpoiler;
|
||||||
item.parentFragment.onEnableExpandable(Holder.this, expandable);
|
item.parentFragment.onEnableExpandable(Holder.this, expandable);
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean expandButtonShown=item.status.textExpandable && !item.status.textExpanded;
|
boolean expandButtonShown=item.status.textExpandable && !item.status.textExpanded;
|
||||||
if(translationFooter!=null)
|
translateWrap.setPadding(0, V.dp(expandButtonShown ? 0 : 4), 0, 0);
|
||||||
translationFooter.setPadding(0, V.dp(expandButtonShown ? 0 : 4), 0, 0);
|
|
||||||
readMore.setVisibility(expandButtonShown ? View.VISIBLE : View.GONE);
|
readMore.setVisibility(expandButtonShown ? View.VISIBLE : View.GONE);
|
||||||
textScrollView.setLayoutParams(item.status.textExpandable && !item.status.textExpanded ? collapseParams : wrapParams);
|
textScrollView.setLayoutParams(item.status.textExpandable && !item.status.textExpanded ? collapseParams : wrapParams);
|
||||||
|
|
||||||
// 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 && item.status.hasSpoiler() ? V.dp(-16) : 0,
|
params.setMargins(params.leftMargin, item.inset && hasSpoiler ? V.dp(-16) : 0,
|
||||||
params.rightMargin, params.bottomMargin);
|
params.rightMargin, params.bottomMargin);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,61 +259,5 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
|
|||||||
private CustomEmojiHelper getEmojiHelper(){
|
private CustomEmojiHelper getEmojiHelper(){
|
||||||
return item.emojiHelper;
|
return item.emojiHelper;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateTranslation(boolean updateText){
|
|
||||||
if(item.status==null)
|
|
||||||
return;
|
|
||||||
boolean translateEnabled=!item.disableTranslate && item.status.isEligibleForTranslation(item.parentFragment.getSession());
|
|
||||||
if(translationFooter==null && translateEnabled){
|
|
||||||
translationFooter=translationFooterStub.inflate();
|
|
||||||
translationInfo=findViewById(R.id.translation_info_text);
|
|
||||||
translationButton=findViewById(R.id.translation_btn);
|
|
||||||
translationButtonWrap=findViewById(R.id.translation_btn_wrap);
|
|
||||||
translationProgress=findViewById(R.id.translation_progress);
|
|
||||||
translationButton.setOnClickListener(v->item.parentFragment.togglePostTranslation(item.status, item.parentID));
|
|
||||||
}
|
|
||||||
if(translationButton!=null) translationButton.animate().cancel();
|
|
||||||
if(item.status.translationState==Status.TranslationState.HIDDEN){
|
|
||||||
if(updateText) text.setText(item.text);
|
|
||||||
if(translationFooter==null) return;
|
|
||||||
translationFooter.setVisibility(translateEnabled ? View.VISIBLE : View.GONE);
|
|
||||||
translationProgress.setVisibility(View.GONE);
|
|
||||||
Translation existingTrans=item.status.getContentStatus().translation;
|
|
||||||
String existingTransLang=existingTrans!=null ? existingTrans.detectedSourceLanguage : null;
|
|
||||||
String lang=existingTransLang!=null ? existingTransLang : item.status.getContentStatus().language;
|
|
||||||
String displayLang=Locale.forLanguageTag(lang != null ? lang
|
|
||||||
: AccountSessionManager.get(item.parentFragment.getAccountID()).preferences.postingDefaultLanguage).getDisplayLanguage();
|
|
||||||
translationButton.setText(item.parentFragment.getString(R.string.translate_post, !displayLang.isBlank() ? displayLang : lang));
|
|
||||||
translationButton.setClickable(true);
|
|
||||||
translationButton.animate().alpha(1).setDuration(100).start();
|
|
||||||
translationInfo.setVisibility(View.GONE);
|
|
||||||
UiUtils.beginLayoutTransition((ViewGroup) translationButtonWrap);
|
|
||||||
}else{
|
|
||||||
translationFooter.setVisibility(View.VISIBLE);
|
|
||||||
if(item.status.translationState==Status.TranslationState.SHOWN){
|
|
||||||
translationProgress.setVisibility(View.GONE);
|
|
||||||
translationButton.setText(R.string.translation_show_original);
|
|
||||||
translationButton.setClickable(true);
|
|
||||||
translationButton.animate().alpha(1).setDuration(200).start();
|
|
||||||
translationInfo.setVisibility(View.VISIBLE);
|
|
||||||
translationButton.setVisibility(View.VISIBLE);
|
|
||||||
String displayLang=Locale.forLanguageTag(item.status.translation.detectedSourceLanguage).getDisplayLanguage();
|
|
||||||
translationInfo.setText(translationInfo.getContext().getString(R.string.post_translated, !displayLang.isBlank() ? displayLang : item.status.translation.detectedSourceLanguage, item.status.translation.provider));
|
|
||||||
UiUtils.beginLayoutTransition((ViewGroup) translationButtonWrap);
|
|
||||||
if(updateText){
|
|
||||||
if(item.translatedText==null){
|
|
||||||
item.setTranslatedText(item.status.translation.content);
|
|
||||||
}
|
|
||||||
text.setText(item.translatedText);
|
|
||||||
}
|
|
||||||
}else{ // LOADING
|
|
||||||
translationProgress.setVisibility(View.VISIBLE);
|
|
||||||
translationButton.setClickable(false);
|
|
||||||
translationButton.animate().alpha(UiUtils.ALPHA_PRESSED).setStartDelay(50).setDuration(300).setInterpolator(CubicBezierInterpolator.DEFAULT).start();
|
|
||||||
translationInfo.setVisibility(View.INVISIBLE);
|
|
||||||
UiUtils.beginLayoutTransition((ViewGroup) translationButton.getParent());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,11 +31,13 @@ public class WarningFilteredStatusDisplayItem extends StatusDisplayItem{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static class Holder extends StatusDisplayItem.Holder<WarningFilteredStatusDisplayItem>{
|
public static class Holder extends StatusDisplayItem.Holder<WarningFilteredStatusDisplayItem>{
|
||||||
|
public final View warningWrap;
|
||||||
public final TextView text;
|
public final TextView text;
|
||||||
public List<StatusDisplayItem> filteredItems;
|
public List<StatusDisplayItem> filteredItems;
|
||||||
|
|
||||||
public Holder(Context context, ViewGroup parent) {
|
public Holder(Context context, ViewGroup parent) {
|
||||||
super(context, R.layout.display_item_filter_warning, parent);
|
super(context, R.layout.display_item_filter_warning, parent);
|
||||||
|
warningWrap=findViewById(R.id.warning_wrap);
|
||||||
text=findViewById(R.id.text);
|
text=findViewById(R.id.text);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,7 +45,11 @@ public class WarningFilteredStatusDisplayItem extends StatusDisplayItem{
|
|||||||
public void onBind(WarningFilteredStatusDisplayItem item) {
|
public void onBind(WarningFilteredStatusDisplayItem item) {
|
||||||
filteredItems = item.filteredItems;
|
filteredItems = item.filteredItems;
|
||||||
text.setText(item.parentFragment.getString(R.string.sk_filtered, item.applyingFilter.title));
|
text.setText(item.parentFragment.getString(R.string.sk_filtered, item.applyingFilter.title));
|
||||||
itemView.setOnClickListener(v->item.parentFragment.onWarningClick(this));
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClick() {
|
||||||
|
item.parentFragment.onWarningClick(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user