Merge remote-tracking branch 'megalodon_main/main'
# Conflicts: # mastodon/src/main/java/org/joinmastodon/android/fragments/SettingsFragment.java # mastodon/src/main/res/layout/fragment_compose.xml
This commit is contained in:
@@ -40,6 +40,22 @@
|
|||||||
<category android:name="android.intent.category.LAUNCHER"/>
|
<category android:name="android.intent.category.LAUNCHER"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
<activity
|
||||||
|
android:name=".PanicResponderActivity"
|
||||||
|
android:exported="true"
|
||||||
|
android:launchMode="singleInstance"
|
||||||
|
android:noHistory="true"
|
||||||
|
android:theme="@android:style/Theme.NoDisplay">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="info.guardianproject.panic.action.TRIGGER" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
<activity
|
||||||
|
android:name=".ExitActivity"
|
||||||
|
android:exported="false"
|
||||||
|
android:theme="@android:style/Theme.NoDisplay" />
|
||||||
<activity android:name=".OAuthActivity" android:exported="true" android:configChanges="orientation|screenSize" android:launchMode="singleTask">
|
<activity android:name=".OAuthActivity" android:exported="true" android:configChanges="orientation|screenSize" android:launchMode="singleTask">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.VIEW"/>
|
<action android:name="android.intent.action.VIEW"/>
|
||||||
|
|||||||
@@ -1,89 +0,0 @@
|
|||||||
# lists.d Mastodon Blocklist (c) 2022 Greyhat Academy LICENSED UNDER: CC-BY-NC-SA 4.0
|
|
||||||
# https://raw.githubusercontent.com/greyhat-academy/lists.d/main/mastodon.domains.block.list.tsv
|
|
||||||
# This list contains domains of toxic mastodon instances
|
|
||||||
# Last-Modified: 1672044500
|
|
||||||
|
|
||||||
# gab - a neonazi social network
|
|
||||||
gab.ai
|
|
||||||
gab.com
|
|
||||||
gab.protohype.net
|
|
||||||
|
|
||||||
# consequence-free speech
|
|
||||||
social.unzensiert.to
|
|
||||||
freeatlantis.com
|
|
||||||
|
|
||||||
# reactionary bigotry and hatespeech against marginalized groups
|
|
||||||
poa.st
|
|
||||||
freespeechextremist.com
|
|
||||||
rdrama.cc
|
|
||||||
outpoa.st
|
|
||||||
anime.website
|
|
||||||
gameliberty.club
|
|
||||||
social.byoblu.com
|
|
||||||
yggdrasil.social
|
|
||||||
smuglo.li
|
|
||||||
dogeposting.social
|
|
||||||
unsafe.space
|
|
||||||
freezepeach.xyz
|
|
||||||
|
|
||||||
# + CSAM
|
|
||||||
rojogato.com
|
|
||||||
|
|
||||||
# antivaxxer shitposting & fearmongering
|
|
||||||
shadowsocial.org
|
|
||||||
|
|
||||||
# Kiwifarms
|
|
||||||
kiwifarms.net
|
|
||||||
kiwifarms.cc
|
|
||||||
kiwifarms.is
|
|
||||||
kiwifarms.pleroma.net
|
|
||||||
|
|
||||||
|
|
||||||
# https://mastodon.art/@Curator/109649354849593592
|
|
||||||
|
|
||||||
poa.st antisemitic racist homophobic
|
|
||||||
nicecrew.digital antisemitic
|
|
||||||
beefyboys.win antisemitic racist homophobic harassment
|
|
||||||
cawfee.club antisemitic racist homophobic
|
|
||||||
comfyboy.club antisemitic racist homophobic
|
|
||||||
freespeechextremist.com racist homophobic
|
|
||||||
cum.salon racist misogynist
|
|
||||||
bae.st racist
|
|
||||||
natehiggers.online racist
|
|
||||||
rapemeat.solutions misogynist
|
|
||||||
rapist.town misogynist
|
|
||||||
rapefeminists.network misogynist
|
|
||||||
kiwifarms.cc harassment
|
|
||||||
noagendasocial.com noagenda
|
|
||||||
posting.lolicon.rocks underage
|
|
||||||
urchan.org harassment homophobic racist
|
|
||||||
ryona.agency harassment
|
|
||||||
yggdrasil.social antisemitic homophobic racist
|
|
||||||
genderheretics.xyz transphobic
|
|
||||||
baraag.net underage
|
|
||||||
lolison.top underage
|
|
||||||
shota.house underage
|
|
||||||
shota.social underage
|
|
||||||
aethy.com underage
|
|
||||||
taullo.social underage
|
|
||||||
childpawn.shop underage
|
|
||||||
posting.lolicon.rocks underage
|
|
||||||
loli.best underage
|
|
||||||
gothloli.club underage
|
|
||||||
smuglo.li underage
|
|
||||||
youjo.love underage
|
|
||||||
pedo.school underage
|
|
||||||
lolison.network underage
|
|
||||||
freak.university underage
|
|
||||||
mirr0r.city underage
|
|
||||||
xhais.love underage
|
|
||||||
refusal.biz underage
|
|
||||||
refusal.llc underage
|
|
||||||
mirr0r.city underage
|
|
||||||
nnia.space underage
|
|
||||||
ignorelist.com malicious
|
|
||||||
repl.co malicious
|
|
||||||
|
|
||||||
# custom
|
|
||||||
|
|
||||||
pawoo.net csam
|
|
||||||
|
171
mastodon/src/main/assets/blocks.txt
Normal file
171
mastodon/src/main/assets/blocks.txt
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
13bells.com
|
||||||
|
4aem.com
|
||||||
|
aethy.com
|
||||||
|
anime.website
|
||||||
|
annihilation.social
|
||||||
|
anon-kenkai.com
|
||||||
|
asbestos.cafe
|
||||||
|
bae.st
|
||||||
|
bajax.us
|
||||||
|
banepo.st
|
||||||
|
baraag.net
|
||||||
|
beefyboys.win
|
||||||
|
beepboop.ga
|
||||||
|
berserker.town
|
||||||
|
bikeshed.party
|
||||||
|
boks.moe
|
||||||
|
brainsoap.net
|
||||||
|
breastmilk.club
|
||||||
|
brighteon.social
|
||||||
|
cawfee.club
|
||||||
|
clew.lol
|
||||||
|
clubcyberia.co
|
||||||
|
collapsitarian.io
|
||||||
|
comfyboy.club
|
||||||
|
contrapointsfan.club
|
||||||
|
cum.camp
|
||||||
|
cum.salon
|
||||||
|
cybercriminal.eu
|
||||||
|
darknight-coffee.org
|
||||||
|
dembased.xyz
|
||||||
|
desupost.soy
|
||||||
|
detroitriotcity.com
|
||||||
|
eatthebugs.social
|
||||||
|
eientei.org
|
||||||
|
elementality.org
|
||||||
|
eveningzoo.club
|
||||||
|
firedragonstudios.com
|
||||||
|
firefaithfellowship.com
|
||||||
|
fluf.club
|
||||||
|
foxfam.club
|
||||||
|
freak.university
|
||||||
|
freeatlantis.com
|
||||||
|
freecumextremist.com
|
||||||
|
freedomstrike.org
|
||||||
|
freesoftwareextremist.com
|
||||||
|
freespeech.group
|
||||||
|
freespeechextremist.com
|
||||||
|
freetalklive.com
|
||||||
|
froth.zone
|
||||||
|
fulltermprivacy.com
|
||||||
|
gameliberty.club
|
||||||
|
gearlandia.haus
|
||||||
|
genderheretics.xyz
|
||||||
|
geofront.rocks
|
||||||
|
gleasonator.com
|
||||||
|
glee.li
|
||||||
|
glindr.org
|
||||||
|
goyim.app
|
||||||
|
goyslop.cafe
|
||||||
|
haeder.net
|
||||||
|
handholding.io
|
||||||
|
hidamari.apartments
|
||||||
|
hitchhiker.social
|
||||||
|
hunk.city
|
||||||
|
iddqd.social
|
||||||
|
intkos.link
|
||||||
|
justicewarrior.social
|
||||||
|
kawa-kun.com
|
||||||
|
kitsunemimi.club
|
||||||
|
kiwifarms.cc
|
||||||
|
kompost.cz
|
||||||
|
kurosawa.moe
|
||||||
|
leafposter.club
|
||||||
|
leftychan.net
|
||||||
|
lewdieheaven.com
|
||||||
|
liberdon.com
|
||||||
|
ligma.pro
|
||||||
|
lizards.live
|
||||||
|
lolicon.rocks
|
||||||
|
lolison.top
|
||||||
|
lovingexpressions.net
|
||||||
|
lucasvl.nl
|
||||||
|
mahodou.moe
|
||||||
|
makemysarcophagus.com
|
||||||
|
maladaptive.art
|
||||||
|
masochi.st
|
||||||
|
mastinator.com
|
||||||
|
merovingian.club
|
||||||
|
midwaytrades.com
|
||||||
|
mirr0r.city
|
||||||
|
moa.st
|
||||||
|
mouse.services
|
||||||
|
mugicha.club
|
||||||
|
narrativerry.xyz
|
||||||
|
natehiggers.online
|
||||||
|
neckbeard.xyz
|
||||||
|
needs.vodka
|
||||||
|
neenster.org
|
||||||
|
nicecrew.digital
|
||||||
|
nnia.space
|
||||||
|
noagendasocial.com
|
||||||
|
noagendasocial.nl
|
||||||
|
noagendatube.com
|
||||||
|
nobodyhasthe.biz
|
||||||
|
nukem.biz
|
||||||
|
obo.sh
|
||||||
|
onionfarms.org
|
||||||
|
outpoa.st
|
||||||
|
pawlicker.com
|
||||||
|
pawoo.net
|
||||||
|
pedo.school
|
||||||
|
piazza.today
|
||||||
|
pibvt.net
|
||||||
|
pieville.net
|
||||||
|
pisskey.io
|
||||||
|
plagu.ee
|
||||||
|
pmth.us
|
||||||
|
poa.st
|
||||||
|
poast.org
|
||||||
|
poast.tv
|
||||||
|
poster.place
|
||||||
|
prospeech.space
|
||||||
|
quodverum.com
|
||||||
|
rakket.app
|
||||||
|
rapemeat.solutions
|
||||||
|
rdrama.cc
|
||||||
|
rebelbase.site
|
||||||
|
retardedniggers.forsale
|
||||||
|
rojogato.com
|
||||||
|
ryona.agency
|
||||||
|
schwartzwelt.xyz
|
||||||
|
seal.cafe
|
||||||
|
shigusegubu.club
|
||||||
|
shitpost.cloud
|
||||||
|
shitposter.club
|
||||||
|
shota.house
|
||||||
|
silliness.observer
|
||||||
|
skinheads.eu
|
||||||
|
skinheads.io
|
||||||
|
skinheads.social
|
||||||
|
skinheads.uk
|
||||||
|
skippers-bin.com
|
||||||
|
skyshanty.xyz
|
||||||
|
slash.cl
|
||||||
|
sleepy.cafe
|
||||||
|
smuglo.li
|
||||||
|
sneed.social
|
||||||
|
sonichu.com
|
||||||
|
spinster.xyz
|
||||||
|
springbo.cc
|
||||||
|
starnix.network
|
||||||
|
stereophonic.space
|
||||||
|
strelizia.net
|
||||||
|
syspxl.xyz
|
||||||
|
tastingtraffic.net
|
||||||
|
teci.world
|
||||||
|
theapex.social
|
||||||
|
thepostearthdestination.com
|
||||||
|
tkammer.de
|
||||||
|
trumpislovetrumpis.life
|
||||||
|
truthsocial.co.in
|
||||||
|
urchan.org
|
||||||
|
varishangout.net
|
||||||
|
whinge.house
|
||||||
|
whinge.town
|
||||||
|
wideboys.org
|
||||||
|
wolfgirl.bar
|
||||||
|
xn--p1abe3d.xn--80asehdb
|
||||||
|
yggdrasil.social
|
||||||
|
youjo.love
|
||||||
|
zztails.gay
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package org.joinmastodon.android;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
public class ExitActivity extends Activity {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
finishAndRemoveTask();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void exit(Context context) {
|
||||||
|
Intent intent = new Intent(context, ExitActivity.class);
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||||
|
context.startActivity(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@ import android.os.Build;
|
|||||||
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.model.ContentType;
|
||||||
import org.joinmastodon.android.model.TimelineDefinition;
|
import org.joinmastodon.android.model.TimelineDefinition;
|
||||||
|
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
@@ -61,10 +62,13 @@ public class GlobalUserPreferences{
|
|||||||
|
|
||||||
private final static Type recentLanguagesType = new TypeToken<Map<String, List<String>>>() {}.getType();
|
private final static Type recentLanguagesType = new TypeToken<Map<String, List<String>>>() {}.getType();
|
||||||
private final static Type pinnedTimelinesType = new TypeToken<Map<String, List<TimelineDefinition>>>() {}.getType();
|
private final static Type pinnedTimelinesType = new TypeToken<Map<String, List<TimelineDefinition>>>() {}.getType();
|
||||||
|
private final static Type accountsDefaultContentTypesType = new TypeToken<Map<String, ContentType>>() {}.getType();
|
||||||
public static Map<String, List<String>> recentLanguages;
|
public static Map<String, List<String>> recentLanguages;
|
||||||
public static Map<String, List<TimelineDefinition>> pinnedTimelines;
|
public static Map<String, List<TimelineDefinition>> pinnedTimelines;
|
||||||
public static Set<String> accountsWithLocalOnlySupport;
|
public static Set<String> accountsWithLocalOnlySupport;
|
||||||
public static Set<String> accountsInGlitchMode;
|
public static Set<String> accountsInGlitchMode;
|
||||||
|
public static Set<String> accountsWithContentTypesEnabled;
|
||||||
|
public static Map<String, ContentType> accountsDefaultContentTypes;
|
||||||
|
|
||||||
private final static Type recentEmojisType = new TypeToken<Map<String, Integer>>() {}.getType();
|
private final static Type recentEmojisType = new TypeToken<Map<String, Integer>>() {}.getType();
|
||||||
public static Map<String, Integer> recentEmojis;
|
public static Map<String, Integer> recentEmojis;
|
||||||
@@ -133,6 +137,8 @@ public class GlobalUserPreferences{
|
|||||||
accountsWithLocalOnlySupport=prefs.getStringSet("accountsWithLocalOnlySupport", new HashSet<>());
|
accountsWithLocalOnlySupport=prefs.getStringSet("accountsWithLocalOnlySupport", new HashSet<>());
|
||||||
accountsInGlitchMode=prefs.getStringSet("accountsInGlitchMode", new HashSet<>());
|
accountsInGlitchMode=prefs.getStringSet("accountsInGlitchMode", new HashSet<>());
|
||||||
replyVisibility=prefs.getString("replyVisibility", null);
|
replyVisibility=prefs.getString("replyVisibility", null);
|
||||||
|
accountsWithContentTypesEnabled=prefs.getStringSet("accountsWithContentTypesEnabled", new HashSet<>());
|
||||||
|
accountsDefaultContentTypes=fromJson(prefs.getString("accountsDefaultContentTypes", null), accountsDefaultContentTypesType, new HashMap<>());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if(android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.S){
|
if(android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.S){
|
||||||
@@ -192,6 +198,8 @@ public class GlobalUserPreferences{
|
|||||||
.putStringSet("accountsWithLocalOnlySupport", accountsWithLocalOnlySupport)
|
.putStringSet("accountsWithLocalOnlySupport", accountsWithLocalOnlySupport)
|
||||||
.putStringSet("accountsInGlitchMode", accountsInGlitchMode)
|
.putStringSet("accountsInGlitchMode", accountsInGlitchMode)
|
||||||
.putString("replyVisibility", replyVisibility)
|
.putString("replyVisibility", replyVisibility)
|
||||||
|
.putStringSet("accountsWithContentTypesEnabled", accountsWithContentTypesEnabled)
|
||||||
|
.putString("accountsDefaultContentTypes", gson.toJson(accountsDefaultContentTypes))
|
||||||
.apply();
|
.apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package org.joinmastodon.android;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.api.requests.oauth.RevokeOauthToken;
|
||||||
|
import org.joinmastodon.android.api.session.AccountSession;
|
||||||
|
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||||
|
|
||||||
|
import me.grishka.appkit.api.Callback;
|
||||||
|
import me.grishka.appkit.api.ErrorResponse;
|
||||||
|
|
||||||
|
|
||||||
|
public class PanicResponderActivity extends Activity {
|
||||||
|
public static final String PANIC_TRIGGER_ACTION = "info.guardianproject.panic.action.TRIGGER";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(final Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
final Intent intent = getIntent();
|
||||||
|
if (intent != null && PANIC_TRIGGER_ACTION.equals(intent.getAction())) {
|
||||||
|
AccountSessionManager.getInstance().getLoggedInAccounts().forEach(accountSession -> logOut(accountSession.getID()));
|
||||||
|
ExitActivity.exit(this);
|
||||||
|
}
|
||||||
|
finishAndRemoveTask();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void logOut(String accountID){
|
||||||
|
AccountSession session=AccountSessionManager.getInstance().getAccount(accountID);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.exec(accountID);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onLoggedOut(String accountID){
|
||||||
|
AccountSessionManager.getInstance().removeAccount(accountID);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -60,7 +60,7 @@ public class MastodonAPIController{
|
|||||||
thread.start();
|
thread.start();
|
||||||
try {
|
try {
|
||||||
final BufferedReader reader = new BufferedReader(new InputStreamReader(
|
final BufferedReader reader = new BufferedReader(new InputStreamReader(
|
||||||
MastodonApp.context.getAssets().open("blocks.tsv")
|
MastodonApp.context.getAssets().open("blocks.txt")
|
||||||
));
|
));
|
||||||
String line;
|
String line;
|
||||||
while ((line = reader.readLine()) != null) {
|
while ((line = reader.readLine()) != null) {
|
||||||
@@ -91,7 +91,7 @@ public class MastodonAPIController{
|
|||||||
Request.Builder builder=new Request.Builder()
|
Request.Builder builder=new Request.Builder()
|
||||||
.url(req.getURL().toString())
|
.url(req.getURL().toString())
|
||||||
.method(req.getMethod(), req.getRequestBody())
|
.method(req.getMethod(), req.getRequestBody())
|
||||||
.header("User-Agent", "MastodonAndroid/"+BuildConfig.VERSION_NAME);
|
.header("User-Agent", "MegalodonAndroid/"+BuildConfig.VERSION_NAME);
|
||||||
|
|
||||||
String token=null;
|
String token=null;
|
||||||
if(session!=null)
|
if(session!=null)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
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.ContentType;
|
||||||
import org.joinmastodon.android.model.ScheduledStatus;
|
import org.joinmastodon.android.model.ScheduledStatus;
|
||||||
import org.joinmastodon.android.model.Status;
|
import org.joinmastodon.android.model.Status;
|
||||||
import org.joinmastodon.android.model.StatusPrivacy;
|
import org.joinmastodon.android.model.StatusPrivacy;
|
||||||
@@ -46,6 +47,7 @@ public class CreateStatus extends MastodonAPIRequest<Status>{
|
|||||||
public String language;
|
public String language;
|
||||||
|
|
||||||
public String quoteId;
|
public String quoteId;
|
||||||
|
public ContentType contentType;
|
||||||
|
|
||||||
public static class Poll{
|
public static class Poll{
|
||||||
public ArrayList<String> options=new ArrayList<>();
|
public ArrayList<String> options=new ArrayList<>();
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package org.joinmastodon.android.api.requests.statuses;
|
|||||||
import org.joinmastodon.android.api.AllFieldsAreRequired;
|
import org.joinmastodon.android.api.AllFieldsAreRequired;
|
||||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||||
import org.joinmastodon.android.model.BaseModel;
|
import org.joinmastodon.android.model.BaseModel;
|
||||||
|
import org.joinmastodon.android.model.ContentType;
|
||||||
|
|
||||||
public class GetStatusSourceText extends MastodonAPIRequest<GetStatusSourceText.Response>{
|
public class GetStatusSourceText extends MastodonAPIRequest<GetStatusSourceText.Response>{
|
||||||
public GetStatusSourceText(String id){
|
public GetStatusSourceText(String id){
|
||||||
@@ -14,5 +15,6 @@ public class GetStatusSourceText extends MastodonAPIRequest<GetStatusSourceText.
|
|||||||
public String id;
|
public String id;
|
||||||
public String text;
|
public String text;
|
||||||
public String spoilerText;
|
public String spoilerText;
|
||||||
|
public ContentType contentType;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -94,6 +94,7 @@ 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.Attachment;
|
import org.joinmastodon.android.model.Attachment;
|
||||||
|
import org.joinmastodon.android.model.ContentType;
|
||||||
import org.joinmastodon.android.model.Emoji;
|
import org.joinmastodon.android.model.Emoji;
|
||||||
import org.joinmastodon.android.model.EmojiCategory;
|
import org.joinmastodon.android.model.EmojiCategory;
|
||||||
import org.joinmastodon.android.model.Instance;
|
import org.joinmastodon.android.model.Instance;
|
||||||
@@ -182,8 +183,8 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
private int charCount, charLimit, trimmedCharCount;
|
private int charCount, charLimit, trimmedCharCount;
|
||||||
|
|
||||||
private Button publishButton, languageButton, scheduleTimeBtn, draftsBtn;
|
private Button publishButton, languageButton, scheduleTimeBtn, draftsBtn;
|
||||||
private PopupMenu languagePopup, visibilityPopup, draftOptionsPopup;
|
private PopupMenu languagePopup, contentTypePopup, visibilityPopup, draftOptionsPopup;
|
||||||
private ImageButton mediaBtn, pollBtn, emojiBtn, spoilerBtn, visibilityBtn, scheduleDraftDismiss;
|
private ImageButton mediaBtn, pollBtn, emojiBtn, spoilerBtn, visibilityBtn, scheduleDraftDismiss, contentTypeBtn;
|
||||||
private ImageView sensitiveIcon;
|
private ImageView sensitiveIcon;
|
||||||
private ComposeMediaLayout attachmentsView;
|
private ComposeMediaLayout attachmentsView;
|
||||||
private TextView replyText;
|
private TextView replyText;
|
||||||
@@ -237,6 +238,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
private Runnable updateUploadEtaRunnable;
|
private Runnable updateUploadEtaRunnable;
|
||||||
|
|
||||||
private String language, encoding;
|
private String language, encoding;
|
||||||
|
private ContentType contentType;
|
||||||
private MastodonLanguage.LanguageResolver languageResolver;
|
private MastodonLanguage.LanguageResolver languageResolver;
|
||||||
|
|
||||||
private int navigationBarColorBefore;
|
private int navigationBarColorBefore;
|
||||||
@@ -249,6 +251,12 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
getActivity().getWindow().setNavigationBarColor(UiUtils.getThemeColor(getActivity(), R.attr.colorBackgroundLightest));
|
getActivity().getWindow().setNavigationBarColor(UiUtils.getThemeColor(getActivity(), R.attr.colorBackgroundLightest));
|
||||||
|
|
||||||
accountID=getArguments().getString("account");
|
accountID=getArguments().getString("account");
|
||||||
|
contentType = GlobalUserPreferences.accountsDefaultContentTypes.get(accountID);
|
||||||
|
if (contentType == null && GlobalUserPreferences.accountsWithContentTypesEnabled.contains(accountID)) {
|
||||||
|
// if formatting is enabled, use plain to avoid confusing unspecified default setting
|
||||||
|
contentType = ContentType.PLAIN;
|
||||||
|
}
|
||||||
|
|
||||||
AccountSession session=AccountSessionManager.getInstance().getAccount(accountID);
|
AccountSession session=AccountSessionManager.getInstance().getAccount(accountID);
|
||||||
self=session.self;
|
self=session.self;
|
||||||
instanceDomain=session.domain;
|
instanceDomain=session.domain;
|
||||||
@@ -353,6 +361,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
emojiBtn=view.findViewById(R.id.btn_emoji);
|
emojiBtn=view.findViewById(R.id.btn_emoji);
|
||||||
spoilerBtn=view.findViewById(R.id.btn_spoiler);
|
spoilerBtn=view.findViewById(R.id.btn_spoiler);
|
||||||
visibilityBtn=view.findViewById(R.id.btn_visibility);
|
visibilityBtn=view.findViewById(R.id.btn_visibility);
|
||||||
|
contentTypeBtn=view.findViewById(R.id.btn_content_type);
|
||||||
scheduleDraftView=view.findViewById(R.id.schedule_draft_view);
|
scheduleDraftView=view.findViewById(R.id.schedule_draft_view);
|
||||||
scheduleDraftText=view.findViewById(R.id.schedule_draft_text);
|
scheduleDraftText=view.findViewById(R.id.schedule_draft_text);
|
||||||
scheduleDraftDismiss=view.findViewById(R.id.schedule_draft_dismiss);
|
scheduleDraftDismiss=view.findViewById(R.id.schedule_draft_dismiss);
|
||||||
@@ -387,6 +396,10 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
visibilityBtn.setOnClickListener(v->visibilityPopup.show());
|
visibilityBtn.setOnClickListener(v->visibilityPopup.show());
|
||||||
visibilityBtn.setOnTouchListener(visibilityPopup.getDragToOpenListener());
|
visibilityBtn.setOnTouchListener(visibilityPopup.getDragToOpenListener());
|
||||||
|
|
||||||
|
buildContentTypePopup(contentTypeBtn);
|
||||||
|
contentTypeBtn.setOnClickListener(v->contentTypePopup.show());
|
||||||
|
contentTypeBtn.setOnTouchListener(contentTypePopup.getDragToOpenListener());
|
||||||
|
|
||||||
scheduleDraftDismiss.setOnClickListener(v->updateScheduledAt(null));
|
scheduleDraftDismiss.setOnClickListener(v->updateScheduledAt(null));
|
||||||
scheduleTimeBtn.setOnClickListener(v->pickScheduledDateTime());
|
scheduleTimeBtn.setOnClickListener(v->pickScheduledDateTime());
|
||||||
|
|
||||||
@@ -489,8 +502,10 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(editingStatus!=null && editingStatus.visibility!=null) {
|
if (savedInstanceState != null) {
|
||||||
statusVisibility=editingStatus.visibility;
|
statusVisibility = (StatusPrivacy) savedInstanceState.getSerializable("visibility");
|
||||||
|
} else if (editingStatus != null && editingStatus.visibility != null) {
|
||||||
|
statusVisibility = editingStatus.visibility;
|
||||||
} else {
|
} else {
|
||||||
loadDefaultStatusVisibility(savedInstanceState);
|
loadDefaultStatusVisibility(savedInstanceState);
|
||||||
}
|
}
|
||||||
@@ -505,6 +520,20 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
}).setChecked(true);
|
}).setChecked(true);
|
||||||
visibilityPopup.getMenu().findItem(R.id.local_only).setChecked(localOnly);
|
visibilityPopup.getMenu().findItem(R.id.local_only).setChecked(localOnly);
|
||||||
|
|
||||||
|
|
||||||
|
if (savedInstanceState != null && savedInstanceState.containsKey("contentType")) {
|
||||||
|
contentType = (ContentType) savedInstanceState.getSerializable("contentType");
|
||||||
|
} else if (getArguments().containsKey("sourceContentType")) {
|
||||||
|
try {
|
||||||
|
String val = getArguments().getString("sourceContentType");
|
||||||
|
contentType = val == null ? null : ContentType.valueOf(val);
|
||||||
|
} catch (IllegalArgumentException ignored) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
int contentTypeId = ContentType.getContentTypeRes(contentType);
|
||||||
|
contentTypePopup.getMenu().findItem(contentTypeId).setChecked(true);
|
||||||
|
contentTypeBtn.setSelected(contentTypeId != R.id.content_type_null && contentTypeId != R.id.content_type_plain);
|
||||||
|
|
||||||
autocompleteViewController=new ComposeAutocompleteViewController(getActivity(), accountID);
|
autocompleteViewController=new ComposeAutocompleteViewController(getActivity(), accountID);
|
||||||
autocompleteViewController.setCompletionSelectedListener(this::onAutocompleteOptionSelected);
|
autocompleteViewController.setCompletionSelectedListener(this::onAutocompleteOptionSelected);
|
||||||
View autocompleteView=autocompleteViewController.getView();
|
View autocompleteView=autocompleteViewController.getView();
|
||||||
@@ -541,6 +570,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
outState.putParcelableArrayList("attachments", serializedAttachments);
|
outState.putParcelableArrayList("attachments", serializedAttachments);
|
||||||
}
|
}
|
||||||
outState.putSerializable("visibility", statusVisibility);
|
outState.putSerializable("visibility", statusVisibility);
|
||||||
|
outState.putSerializable("contentType", contentType);
|
||||||
if (scheduledAt != null) outState.putSerializable("scheduledAt", scheduledAt);
|
if (scheduledAt != null) outState.putSerializable("scheduledAt", scheduledAt);
|
||||||
if (scheduledStatus != null) outState.putParcelable("scheduledStatus", Parcels.wrap(scheduledStatus));
|
if (scheduledStatus != null) outState.putParcelable("scheduledStatus", Parcels.wrap(scheduledStatus));
|
||||||
}
|
}
|
||||||
@@ -948,6 +978,17 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int getContentTypeName(String id) {
|
||||||
|
return switch (id) {
|
||||||
|
case "text/plain" -> R.string.sk_content_type_plain;
|
||||||
|
case "text/html" -> R.string.sk_content_type_html;
|
||||||
|
case "text/markdown" -> R.string.sk_content_type_markdown;
|
||||||
|
case "text/bbcode" -> R.string.sk_content_type_bbcode;
|
||||||
|
case "text/x.misskeymarkdown" -> R.string.sk_content_type_mfm;
|
||||||
|
default -> throw new IllegalArgumentException("Invalid content type");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private void addBottomLanguage(Menu menu) {
|
private void addBottomLanguage(Menu menu) {
|
||||||
if (menu.findItem(allLanguages.size()) == null) {
|
if (menu.findItem(allLanguages.size()) == null) {
|
||||||
menu.add(0, allLanguages.size(), Menu.NONE, "bottom (\uD83E\uDD7A\uD83D\uDC49\uD83D\uDC48)");
|
menu.add(0, allLanguages.size(), Menu.NONE, "bottom (\uD83E\uDD7A\uD83D\uDC49\uD83D\uDC48)");
|
||||||
@@ -1108,6 +1149,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
req.visibility=localOnly && instance.pleroma != null ? StatusPrivacy.LOCAL : statusVisibility;
|
req.visibility=localOnly && instance.pleroma != null ? StatusPrivacy.LOCAL : statusVisibility;
|
||||||
req.sensitive=sensitive;
|
req.sensitive=sensitive;
|
||||||
req.language=language;
|
req.language=language;
|
||||||
|
req.contentType=contentType;
|
||||||
req.scheduledAt = scheduledAt;
|
req.scheduledAt = scheduledAt;
|
||||||
if(!attachments.isEmpty()){
|
if(!attachments.isEmpty()){
|
||||||
req.mediaIds=attachments.stream().map(a->a.serverAttachment.id).collect(Collectors.toList());
|
req.mediaIds=attachments.stream().map(a->a.serverAttachment.id).collect(Collectors.toList());
|
||||||
@@ -1963,16 +2005,38 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
|
private void buildContentTypePopup(View btn) {
|
||||||
|
contentTypePopup=new PopupMenu(getActivity(), btn);
|
||||||
|
contentTypePopup.inflate(R.menu.compose_content_type);
|
||||||
|
Menu m = contentTypePopup.getMenu();
|
||||||
|
ContentType.adaptMenuToInstance(m, instance);
|
||||||
|
if (contentType != null) m.findItem(R.id.content_type_null).setVisible(false);
|
||||||
|
|
||||||
|
contentTypePopup.setOnMenuItemClickListener(i->{
|
||||||
|
int id=i.getItemId();
|
||||||
|
if (id == R.id.content_type_null) contentType = null;
|
||||||
|
else if (id == R.id.content_type_plain) contentType = ContentType.PLAIN;
|
||||||
|
else if (id == R.id.content_type_html) contentType = ContentType.HTML;
|
||||||
|
else if (id == R.id.content_type_markdown) contentType = ContentType.MARKDOWN;
|
||||||
|
else if (id == R.id.content_type_bbcode) contentType = ContentType.BBCODE;
|
||||||
|
else if (id == R.id.content_type_misskey_markdown) contentType = ContentType.MISSKEY_MARKDOWN;
|
||||||
|
else return false;
|
||||||
|
btn.setSelected(id != R.id.content_type_null && id != R.id.content_type_plain);
|
||||||
|
i.setChecked(true);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!GlobalUserPreferences.accountsWithContentTypesEnabled.contains(accountID)) {
|
||||||
|
btn.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void loadDefaultStatusVisibility(Bundle savedInstanceState) {
|
private void loadDefaultStatusVisibility(Bundle savedInstanceState) {
|
||||||
if(replyTo != null) {
|
if(replyTo != null) {
|
||||||
statusVisibility = (replyTo.visibility == StatusPrivacy.PUBLIC && GlobalUserPreferences.defaultToUnlistedReplies ? StatusPrivacy.UNLISTED : replyTo.visibility);
|
statusVisibility = (replyTo.visibility == StatusPrivacy.PUBLIC && GlobalUserPreferences.defaultToUnlistedReplies ? StatusPrivacy.UNLISTED : replyTo.visibility);
|
||||||
}
|
}
|
||||||
|
|
||||||
// A saved privacy setting from a previous compose session wins over the reply visibility
|
|
||||||
if(savedInstanceState !=null){
|
|
||||||
statusVisibility = (StatusPrivacy) savedInstanceState.getSerializable("visibility");
|
|
||||||
}
|
|
||||||
|
|
||||||
AccountSessionManager asm = AccountSessionManager.getInstance();
|
AccountSessionManager asm = AccountSessionManager.getInstance();
|
||||||
Preferences prefs = asm.getAccount(accountID).preferences;
|
Preferences prefs = asm.getAccount(accountID).preferences;
|
||||||
if (prefs != null) {
|
if (prefs != null) {
|
||||||
|
|||||||
@@ -97,6 +97,8 @@ public class ScheduledStatusListFragment extends BaseStatusListFragment<Schedule
|
|||||||
args.putString("sourceText", status.text);
|
args.putString("sourceText", status.text);
|
||||||
args.putString("sourceSpoiler", status.spoilerText);
|
args.putString("sourceSpoiler", status.spoilerText);
|
||||||
args.putBoolean("redraftStatus", true);
|
args.putBoolean("redraftStatus", true);
|
||||||
|
args.putString("sourceContentType", scheduledStatus.params.contentType != null ?
|
||||||
|
scheduledStatus.params.contentType.name() : null);
|
||||||
setResult(true, null);
|
setResult(true, null);
|
||||||
|
|
||||||
// closing this scheduled status list if another status list is opened from compose fragment
|
// closing this scheduled status list if another status list is opened from compose fragment
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import android.util.LruCache;
|
|||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
import android.view.Gravity;
|
import android.view.Gravity;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
@@ -55,6 +56,7 @@ import org.joinmastodon.android.api.session.AccountSession;
|
|||||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||||
import org.joinmastodon.android.events.SelfUpdateStateChangedEvent;
|
import org.joinmastodon.android.events.SelfUpdateStateChangedEvent;
|
||||||
import org.joinmastodon.android.fragments.onboarding.InstanceRulesFragment;
|
import org.joinmastodon.android.fragments.onboarding.InstanceRulesFragment;
|
||||||
|
import org.joinmastodon.android.model.ContentType;
|
||||||
import org.joinmastodon.android.model.Instance;
|
import org.joinmastodon.android.model.Instance;
|
||||||
import org.joinmastodon.android.fragments.onboarding.AccountActivationFragment;
|
import org.joinmastodon.android.fragments.onboarding.AccountActivationFragment;
|
||||||
import org.joinmastodon.android.model.PushNotification;
|
import org.joinmastodon.android.model.PushNotification;
|
||||||
@@ -71,6 +73,7 @@ import java.util.function.Consumer;
|
|||||||
|
|
||||||
import androidx.annotation.DrawableRes;
|
import androidx.annotation.DrawableRes;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.StringRes;
|
import androidx.annotation.StringRes;
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
@@ -88,7 +91,8 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
|||||||
private ArrayList<Item> items=new ArrayList<>();
|
private ArrayList<Item> items=new ArrayList<>();
|
||||||
private ThemeItem themeItem;
|
private ThemeItem themeItem;
|
||||||
private NotificationPolicyItem notificationPolicyItem;
|
private NotificationPolicyItem notificationPolicyItem;
|
||||||
private SwitchItem showNewPostsButtonItem, glitchModeItem, compactReblogReplyLineItem;
|
private SwitchItem showNewPostsItem, glitchModeItem, compactReblogReplyLineItem;
|
||||||
|
private ButtonItem defaultContentTypeButtonItem;
|
||||||
private String accountID;
|
private String accountID;
|
||||||
private boolean needUpdateNotificationSettings;
|
private boolean needUpdateNotificationSettings;
|
||||||
private boolean needAppRestart;
|
private boolean needAppRestart;
|
||||||
@@ -97,7 +101,9 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
|||||||
private ImageView themeTransitionWindowView;
|
private ImageView themeTransitionWindowView;
|
||||||
private TextItem checkForUpdateItem, clearImageCacheItem;
|
private TextItem checkForUpdateItem, clearImageCacheItem;
|
||||||
private ImageCache imageCache;
|
private ImageCache imageCache;
|
||||||
|
private Menu contentTypeMenu;
|
||||||
|
|
||||||
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState){
|
public void onCreate(Bundle savedInstanceState){
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
@@ -290,15 +296,15 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
|||||||
}));
|
}));
|
||||||
items.add(new SwitchItem(R.string.sk_settings_load_new_posts, R.drawable.ic_fluent_arrow_sync_24_regular, GlobalUserPreferences.loadNewPosts, i->{
|
items.add(new SwitchItem(R.string.sk_settings_load_new_posts, R.drawable.ic_fluent_arrow_sync_24_regular, GlobalUserPreferences.loadNewPosts, i->{
|
||||||
GlobalUserPreferences.loadNewPosts=i.checked;
|
GlobalUserPreferences.loadNewPosts=i.checked;
|
||||||
showNewPostsButtonItem.enabled = i.checked;
|
showNewPostsItem.enabled = i.checked;
|
||||||
if (!i.checked) {
|
if (!i.checked) {
|
||||||
GlobalUserPreferences.showNewPostsButton = false;
|
GlobalUserPreferences.showNewPostsButton = false;
|
||||||
showNewPostsButtonItem.checked = false;
|
showNewPostsItem.checked = false;
|
||||||
}
|
}
|
||||||
if (list.findViewHolderForAdapterPosition(items.indexOf(showNewPostsButtonItem)) instanceof SwitchViewHolder svh) svh.rebind();
|
if (list.findViewHolderForAdapterPosition(items.indexOf(showNewPostsItem)) instanceof SwitchViewHolder svh) svh.rebind();
|
||||||
GlobalUserPreferences.save();
|
GlobalUserPreferences.save();
|
||||||
}));
|
}));
|
||||||
items.add(showNewPostsButtonItem = new SwitchItem(R.string.sk_settings_show_new_posts_button, R.drawable.ic_fluent_arrow_up_24_regular, GlobalUserPreferences.showNewPostsButton, i->{
|
items.add(showNewPostsItem = new SwitchItem(R.string.sk_settings_see_new_posts_button, R.drawable.ic_fluent_arrow_up_24_regular, GlobalUserPreferences.showNewPostsButton, i->{
|
||||||
GlobalUserPreferences.showNewPostsButton=i.checked;
|
GlobalUserPreferences.showNewPostsButton=i.checked;
|
||||||
GlobalUserPreferences.save();
|
GlobalUserPreferences.save();
|
||||||
}));
|
}));
|
||||||
@@ -398,6 +404,36 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
|||||||
if (!TextUtils.isEmpty(instance.version)) items.add(new SmallTextItem(getString(R.string.sk_settings_server_version, instance.version)));
|
if (!TextUtils.isEmpty(instance.version)) items.add(new SmallTextItem(getString(R.string.sk_settings_server_version, instance.version)));
|
||||||
|
|
||||||
items.add(new HeaderItem(R.string.sk_instance_features));
|
items.add(new HeaderItem(R.string.sk_instance_features));
|
||||||
|
items.add(new SwitchItem(R.string.sk_settings_content_types, 0, GlobalUserPreferences.accountsWithContentTypesEnabled.contains(accountID), (i)->{
|
||||||
|
if (i.checked) {
|
||||||
|
GlobalUserPreferences.accountsWithContentTypesEnabled.add(accountID);
|
||||||
|
if (GlobalUserPreferences.accountsDefaultContentTypes.get(accountID) == null) {
|
||||||
|
GlobalUserPreferences.accountsDefaultContentTypes.put(accountID, ContentType.PLAIN);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
GlobalUserPreferences.accountsWithContentTypesEnabled.remove(accountID);
|
||||||
|
GlobalUserPreferences.accountsDefaultContentTypes.remove(accountID);
|
||||||
|
}
|
||||||
|
if (list.findViewHolderForAdapterPosition(items.indexOf(defaultContentTypeButtonItem))
|
||||||
|
instanceof ButtonViewHolder bvh) bvh.rebind();
|
||||||
|
GlobalUserPreferences.save();
|
||||||
|
}));
|
||||||
|
items.add(new SmallTextItem(getString(R.string.sk_settings_content_types_explanation)));
|
||||||
|
items.add(defaultContentTypeButtonItem = new ButtonItem(R.string.sk_settings_default_content_type, 0, b->{
|
||||||
|
PopupMenu popupMenu=new PopupMenu(getActivity(), b, Gravity.CENTER_HORIZONTAL);
|
||||||
|
popupMenu.inflate(R.menu.compose_content_type);
|
||||||
|
popupMenu.setOnMenuItemClickListener(item -> this.onContentTypeChanged(item, b));
|
||||||
|
b.setOnTouchListener(popupMenu.getDragToOpenListener());
|
||||||
|
b.setOnClickListener(v->popupMenu.show());
|
||||||
|
ContentType contentType = GlobalUserPreferences.accountsDefaultContentTypes.get(accountID);
|
||||||
|
b.setText(getContentTypeString(contentType));
|
||||||
|
contentTypeMenu = popupMenu.getMenu();
|
||||||
|
contentTypeMenu.findItem(ContentType.getContentTypeRes(contentType)).setChecked(true);
|
||||||
|
ContentType.adaptMenuToInstance(contentTypeMenu, instance);
|
||||||
|
contentTypeMenu.findItem(R.id.content_type_null).setVisible(
|
||||||
|
!GlobalUserPreferences.accountsWithContentTypesEnabled.contains(accountID));
|
||||||
|
}));
|
||||||
|
items.add(new SmallTextItem(getString(R.string.sk_settings_default_content_type_explanation)));
|
||||||
items.add(new SwitchItem(R.string.sk_settings_support_local_only, 0, GlobalUserPreferences.accountsWithLocalOnlySupport.contains(accountID), i->{
|
items.add(new SwitchItem(R.string.sk_settings_support_local_only, 0, GlobalUserPreferences.accountsWithLocalOnlySupport.contains(accountID), i->{
|
||||||
glitchModeItem.enabled = i.checked;
|
glitchModeItem.enabled = i.checked;
|
||||||
if (i.checked) {
|
if (i.checked) {
|
||||||
@@ -617,6 +653,34 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private @StringRes int getContentTypeString(@Nullable ContentType contentType) {
|
||||||
|
if (contentType == null) return R.string.sk_content_type_unspecified;
|
||||||
|
return switch (contentType) {
|
||||||
|
case PLAIN -> R.string.sk_content_type_plain;
|
||||||
|
case HTML -> R.string.sk_content_type_html;
|
||||||
|
case MARKDOWN -> R.string.sk_content_type_markdown;
|
||||||
|
case BBCODE -> R.string.sk_content_type_bbcode;
|
||||||
|
case MISSKEY_MARKDOWN -> R.string.sk_content_type_mfm;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean onContentTypeChanged(MenuItem item, Button btn){
|
||||||
|
int id = item.getItemId();
|
||||||
|
ContentType contentType = switch (id) {
|
||||||
|
case R.id.content_type_plain -> ContentType.PLAIN;
|
||||||
|
case R.id.content_type_html -> ContentType.HTML;
|
||||||
|
case R.id.content_type_markdown -> ContentType.MARKDOWN;
|
||||||
|
case R.id.content_type_bbcode -> ContentType.BBCODE;
|
||||||
|
case R.id.content_type_misskey_markdown -> ContentType.MISSKEY_MARKDOWN;
|
||||||
|
default -> null;
|
||||||
|
};
|
||||||
|
GlobalUserPreferences.accountsDefaultContentTypes.put(accountID, contentType);
|
||||||
|
GlobalUserPreferences.save();
|
||||||
|
btn.setText(getContentTypeString(contentType));
|
||||||
|
item.setChecked(true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean onReplyVisibilityChanged(MenuItem item, Button btn){
|
private boolean onReplyVisibilityChanged(MenuItem item, Button btn){
|
||||||
String pref = null;
|
String pref = null;
|
||||||
int id = item.getItemId();
|
int id = item.getItemId();
|
||||||
@@ -1146,7 +1210,11 @@ public class SettingsFragment extends MastodonToolbarFragment{
|
|||||||
@Override
|
@Override
|
||||||
public void onBind(ButtonItem item){
|
public void onBind(ButtonItem item){
|
||||||
text.setText(item.text);
|
text.setText(item.text);
|
||||||
icon.setImageResource(item.icon);
|
if (item.icon == 0) {
|
||||||
|
icon.setVisibility(View.GONE);
|
||||||
|
} else {
|
||||||
|
icon.setImageResource(item.icon);
|
||||||
|
}
|
||||||
item.buttonConsumer.accept(button);
|
item.buttonConsumer.accept(button);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package org.joinmastodon.android.model;
|
||||||
|
|
||||||
|
import android.view.Menu;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
import org.joinmastodon.android.R;
|
||||||
|
|
||||||
|
public enum ContentType {
|
||||||
|
@SerializedName("text/plain")
|
||||||
|
PLAIN,
|
||||||
|
@SerializedName("text/html")
|
||||||
|
HTML,
|
||||||
|
@SerializedName("text/markdown")
|
||||||
|
MARKDOWN,
|
||||||
|
@SerializedName("text/bbcode")
|
||||||
|
BBCODE, // akkoma
|
||||||
|
@SerializedName("text/x.misskeymarkdown")
|
||||||
|
MISSKEY_MARKDOWN; // akkoma/*key
|
||||||
|
|
||||||
|
public static int getContentTypeRes(@Nullable ContentType contentType) {
|
||||||
|
return contentType == null ? R.id.content_type_null : switch(contentType) {
|
||||||
|
case PLAIN -> R.id.content_type_plain;
|
||||||
|
case HTML -> R.id.content_type_html;
|
||||||
|
case MARKDOWN -> R.id.content_type_markdown;
|
||||||
|
case BBCODE -> R.id.content_type_bbcode;
|
||||||
|
case MISSKEY_MARKDOWN -> R.id.content_type_misskey_markdown;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void adaptMenuToInstance(Menu m, Instance i) {
|
||||||
|
if (i.pleroma == null) {
|
||||||
|
// memo: change this if glitch or another mastodon fork supports bbcode or mfm
|
||||||
|
m.findItem(R.id.content_type_bbcode).setVisible(false);
|
||||||
|
m.findItem(R.id.content_type_misskey_markdown).setVisible(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -39,6 +39,7 @@ public class ScheduledStatus extends BaseModel implements DisplayItemsParent{
|
|||||||
public String idempotency;
|
public String idempotency;
|
||||||
public String applicationId;
|
public String applicationId;
|
||||||
public List<String> mediaIds;
|
public List<String> mediaIds;
|
||||||
|
public ContentType contentType;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Parcel
|
@Parcel
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ import org.joinmastodon.android.fragments.report.ReportReasonChoiceFragment;
|
|||||||
import org.joinmastodon.android.model.Account;
|
import org.joinmastodon.android.model.Account;
|
||||||
import org.joinmastodon.android.model.Announcement;
|
import org.joinmastodon.android.model.Announcement;
|
||||||
import org.joinmastodon.android.model.Attachment;
|
import org.joinmastodon.android.model.Attachment;
|
||||||
|
import org.joinmastodon.android.model.ContentType;
|
||||||
import org.joinmastodon.android.model.Notification;
|
import org.joinmastodon.android.model.Notification;
|
||||||
import org.joinmastodon.android.model.Relationship;
|
import org.joinmastodon.android.model.Relationship;
|
||||||
import org.joinmastodon.android.model.ScheduledStatus;
|
import org.joinmastodon.android.model.ScheduledStatus;
|
||||||
@@ -223,6 +224,9 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
|
|||||||
public void onSuccess(GetStatusSourceText.Response result){
|
public void onSuccess(GetStatusSourceText.Response result){
|
||||||
args.putString("sourceText", result.text);
|
args.putString("sourceText", result.text);
|
||||||
args.putString("sourceSpoiler", result.spoilerText);
|
args.putString("sourceSpoiler", result.spoilerText);
|
||||||
|
if (result.contentType != null) {
|
||||||
|
args.putString("sourceContentType", result.contentType.name());
|
||||||
|
}
|
||||||
if (redraft) {
|
if (redraft) {
|
||||||
UiUtils.confirmDeletePost(item.parentFragment.getActivity(), item.parentFragment.getAccountID(), item.status, s->{
|
UiUtils.confirmDeletePost(item.parentFragment.getActivity(), item.parentFragment.getAccountID(), item.status, s->{
|
||||||
Nav.go(item.parentFragment.getActivity(), ComposeFragment.class, args);
|
Nav.go(item.parentFragment.getActivity(), ComposeFragment.class, args);
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
||||||
|
<path android:pathData="M20.063 8.445c1.256 1.258 1.255 3.295-0.002 4.551l-7.114 7.102c-0.271 0.272-0.608 0.469-0.978 0.573l-4.613 1.303c-0.57 0.162-1.094-0.373-0.92-0.94l1.387-4.543c0.107-0.354 0.3-0.675 0.562-0.936l7.124-7.112c1.258-1.256 3.297-1.255 4.554 0.002zM8.15 2.37L8.2 2.475l3.253 8.249-1.157 1.155L9.556 10H5.443l-0.995 2.52c-0.14 0.354-0.518 0.542-0.876 0.454l-0.098-0.031c-0.353-0.14-0.54-0.518-0.452-0.876l0.03-0.098 3.754-9.495C7.042 1.88 7.85 1.844 8.151 2.37zM7.503 4.792L6.036 8.5h2.928L7.503 4.792z" android:fillColor="@color/fluent_default_icon_tint"/>
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
|
||||||
|
<path android:pathData="M20.063 8.445c1.256 1.258 1.255 3.295-0.002 4.551l-7.123 7.112c-0.263 0.262-0.587 0.455-0.943 0.562l-4.293 1.29c-0.529 0.159-1.086-0.141-1.245-0.67-0.058-0.194-0.056-0.402 0.006-0.594l1.361-4.228c0.11-0.34 0.3-0.65 0.552-0.903l7.133-7.121c1.258-1.256 3.297-1.255 4.554 0.002zm-3.494 1.06l-7.133 7.12c-0.084 0.085-0.147 0.188-0.184 0.301l-1.07 3.323 3.382-1.015c0.119-0.036 0.227-0.1 0.314-0.188L19 11.936c0.672-0.671 0.672-1.76 0.002-2.43-0.672-0.672-1.76-0.673-2.433-0.002zM8.15 2.37L8.2 2.475l3.253 8.249-1.157 1.155L9.556 10H5.443l-0.995 2.52c-0.14 0.354-0.518 0.542-0.876 0.454l-0.098-0.031c-0.353-0.14-0.54-0.518-0.452-0.876l0.03-0.098 3.754-9.495C7.042 1.88 7.85 1.844 8.151 2.37zM7.503 4.792L6.036 8.5h2.928L7.503 4.792z" android:fillColor="@color/fluent_default_icon_tint"/>
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--~ Copyright (c) 2023. ~ Microsoft Corporation. All rights reserved.-->
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:drawable="@drawable/ic_fluent_text_edit_style_24_filled" android:state_activated="true"/>
|
||||||
|
<item android:drawable="@drawable/ic_fluent_text_edit_style_24_filled" android:state_checked="true"/>
|
||||||
|
<item android:drawable="@drawable/ic_fluent_text_edit_style_24_filled" android:state_selected="true"/>
|
||||||
|
<item android:drawable="@drawable/ic_fluent_text_edit_style_24_regular"/>
|
||||||
|
</selector>
|
||||||
@@ -414,6 +414,19 @@
|
|||||||
android:tooltipText="@string/post_visibility"
|
android:tooltipText="@string/post_visibility"
|
||||||
android:src="@drawable/ic_fluent_earth_24_regular"/>
|
android:src="@drawable/ic_fluent_earth_24_regular"/>
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/btn_content_type"
|
||||||
|
android:layout_width="36dp"
|
||||||
|
android:layout_height="36dp"
|
||||||
|
android:layout_marginEnd="12dp"
|
||||||
|
android:background="?android:attr/actionBarItemBackground"
|
||||||
|
android:padding="0px"
|
||||||
|
android:tint="@color/compose_button"
|
||||||
|
android:tintMode="src_in"
|
||||||
|
android:contentDescription="@string/sk_content_type"
|
||||||
|
android:tooltipText="@string/sk_content_type"
|
||||||
|
android:src="@drawable/ic_fluent_text_edit_style_24_selector"/>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/drafts_btn"
|
android:id="@+id/drafts_btn"
|
||||||
android:layout_width="32dp"
|
android:layout_width="32dp"
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
android:layout_width="24dp"
|
android:layout_width="24dp"
|
||||||
android:layout_height="24dp"
|
android:layout_height="24dp"
|
||||||
android:layout_marginStart="16dp"
|
android:layout_marginStart="16dp"
|
||||||
android:layout_marginEnd="32dp"
|
android:layout_marginEnd="16dp"
|
||||||
android:importantForAccessibility="no"
|
android:importantForAccessibility="no"
|
||||||
android:tint="?android:textColorPrimary"
|
android:tint="?android:textColorPrimary"
|
||||||
tools:src="@drawable/ic_fluent_color_24_regular"/>
|
tools:src="@drawable/ic_fluent_color_24_regular"/>
|
||||||
@@ -24,6 +24,7 @@
|
|||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
android:layout_marginEnd="8dp"
|
android:layout_marginEnd="8dp"
|
||||||
android:paddingVertical="8dp"
|
android:paddingVertical="8dp"
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
|
|||||||
11
mastodon/src/main/res/menu/compose_content_type.xml
Normal file
11
mastodon/src/main/res/menu/compose_content_type.xml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<group android:id="@+id/content_type_group" android:checkableBehavior="single">
|
||||||
|
<item android:id="@+id/content_type_null" android:title="@string/sk_content_type_unspecified" />
|
||||||
|
<item android:id="@+id/content_type_plain" android:title="@string/sk_content_type_plain" />
|
||||||
|
<item android:id="@+id/content_type_markdown" android:title="@string/sk_content_type_markdown" />
|
||||||
|
<item android:id="@+id/content_type_html" android:title="@string/sk_content_type_html" />
|
||||||
|
<item android:id="@+id/content_type_bbcode" android:title="@string/sk_content_type_bbcode" />
|
||||||
|
<item android:id="@+id/content_type_misskey_markdown" android:title="@string/sk_content_type_mfm" />
|
||||||
|
</group>
|
||||||
|
</menu>
|
||||||
@@ -275,4 +275,15 @@
|
|||||||
<string name="sk_show_thread">Show thread</string>
|
<string name="sk_show_thread">Show thread</string>
|
||||||
<string name="sk_compact_reblog_reply_line">Compact boost/reply line</string>
|
<string name="sk_compact_reblog_reply_line">Compact boost/reply line</string>
|
||||||
<string name="sk_settings_confirm_before_reblog">Confirm before boosting</string>
|
<string name="sk_settings_confirm_before_reblog">Confirm before boosting</string>
|
||||||
|
<string name="sk_content_type">Content type</string>
|
||||||
|
<string name="sk_content_type_unspecified">Not specified</string>
|
||||||
|
<string name="sk_content_type_plain">Plain text</string>
|
||||||
|
<string name="sk_content_type_html">HTML</string>
|
||||||
|
<string name="sk_content_type_markdown">Markdown</string>
|
||||||
|
<string name="sk_content_type_bbcode">BBCode</string>
|
||||||
|
<string name="sk_content_type_mfm">MFM</string>
|
||||||
|
<string name="sk_settings_content_types">Enable post formatting</string>
|
||||||
|
<string name="sk_settings_content_types_explanation">Allows setting a content type like Markdown when creating a post. Keep in mind that not all instances support this.</string>
|
||||||
|
<string name="sk_settings_default_content_type">Default content type</string>
|
||||||
|
<string name="sk_settings_default_content_type_explanation">This lets you have a content type be pre-selected when creating new posts, overriding the value set in “Posting preferences”.</string>
|
||||||
</resources>
|
</resources>
|
||||||
Reference in New Issue
Block a user