Merge remote-tracking branch 'megalodon_main/main'

# Conflicts:
#	mastodon/build.gradle
#	mastodon/src/main/java/org/joinmastodon/android/GlobalUserPreferences.java
#	mastodon/src/main/java/org/joinmastodon/android/api/session/AccountLocalPreferences.java
#	mastodon/src/main/java/org/joinmastodon/android/fragments/ComposeFragment.java
#	mastodon/src/main/java/org/joinmastodon/android/fragments/HashtagTimelineFragment.java
#	mastodon/src/main/java/org/joinmastodon/android/fragments/StatusListFragment.java
#	mastodon/src/main/java/org/joinmastodon/android/fragments/ThreadFragment.java
#	mastodon/src/main/java/org/joinmastodon/android/fragments/settings/SettingsDisplayFragment.java
#	mastodon/src/main/java/org/joinmastodon/android/ui/CustomEmojiPopupKeyboard.java
#	mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/ExtendedFooterStatusDisplayItem.java
#	mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/FooterStatusDisplayItem.java
#	mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/NotificationHeaderStatusDisplayItem.java
#	mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/StatusDisplayItem.java
#	mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/TextStatusDisplayItem.java
#	mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/WarningFilteredStatusDisplayItem.java
#	mastodon/src/main/java/org/joinmastodon/android/ui/utils/ColorPalette.java
#	mastodon/src/main/res/color/bookmark_icon.xml
#	mastodon/src/main/res/color/boost_icon.xml
#	mastodon/src/main/res/color/favorite_icon.xml
#	mastodon/src/main/res/layout/display_item_footer.xml
#	mastodon/src/main/res/layout/tab_bar.xml
#	mastodon/src/main/res/values/palettes.xml
#	mastodon/src/main/res/values/styles.xml
#	metadata/uk/changelogs/51.txt
#	metadata/uk/changelogs/56.txt
#	metadata/uk/changelogs/61.txt
#	metadata/uk/full_description.txt
This commit is contained in:
LucasGGamerM
2023-10-12 14:58:23 -03:00
241 changed files with 1110 additions and 1636 deletions

View File

@@ -1,56 +1,43 @@
13bells.com
1611.social
4aem.com
5dollah.click
adachi.party
anime.website
adtension.com
annihilation.social
anon-kenkai.com
asbestos.cafe
bae.st
bajax.us
banepo.st
baraag.net
bassam.social
battlepenguin.video
beefyboys.win
beepboop.ga
berserker.town
bikeshed.party
boks.moe
boymoder.biz
brainsoap.net
breastmilk.club
brighteon.social
bungle.online
cachapa.xyz
canary.fedinuke.example.com
catgirl.life
cawfee.club
childlove.space
clew.lol
clubcyberia.co
collapsitarian.io
comfyboy.club
contrapointsfan.club
crucible.world
cum.camp
cum.salon
darknight-coffee.org
decayable.ink
dembased.xyz
desupost.soy
detroitriotcity.com
eatthebugs.social
djsumdog.com
eientei.org
elementality.org
eveningzoo.club
firedragonstudios.com
firefaithfellowship.com
fluf.club
foxfam.club
freak.university
freeatlantis.com
freedomstrike.org
freesoftwareextremist.com
freespeech.group
freespeechextremist.com
freetalklive.com
froth.zone
fulltermprivacy.com
gameliberty.club
gearlandia.haus
genderheretics.xyz
@@ -59,42 +46,34 @@ gleasonator.com
glee.li
glindr.org
goyim.app
goyslop.cafe
h5q.net
haeder.net
handholding.io
hitchhiker.social
hunk.city
iddqd.social
intkos.link
justicewarrior.social
kawa-kun.com
kitsunemimi.club
kiwifarms.cc
kompost.cz
kurosawa.moe
kyaruc.moe
leafposter.club
leftychan.net
lewdieheaven.com
liberdon.com
ligma.pro
lolicon.rocks
lolison.network
lolison.top
lovingexpressions.net
mahodou.moe
makemysarcophagus.com
maladaptive.art
marsey.moe
masochi.st
mastinator.com
merovingian.club
midwaytrades.com
mirr0r.city
moa.st
morale.ch
mouse.services
mugicha.club
narrativerry.xyz
natehiggers.online
neckbeard.xyz
needs.vodka
neenster.org
nicecrew.digital
@@ -103,18 +82,18 @@ noagendasocial.com
noagendasocial.nl
noagendatube.com
nobodyhasthe.biz
nukem.biz
obo.sh
norwoodzero.net
nyanide.com
onionfarms.org
pawlicker.com
pawoo.net
pedo.school
peervideo.club
piazza.today
pibvt.net
pieville.net
pisskey.io
plagu.ee
pmth.us
poa.st
poast.org
poast.tv
@@ -123,17 +102,18 @@ prospeech.space
quodverum.com
r18.social
rakket.app
rapemeat.express
rapemeat.solutions
rdrama.cc
rayci.st
rebelbase.site
retardedniggers.forsale
rojogato.com
ryona.agency
sad.cab
schwartzwelt.xyz
seal.cafe
shaw.app
shigusegubu.club
shitpost.cloud
shota.house
shortstacksran.ch
silliness.observer
skinheads.eu
skinheads.io
@@ -148,23 +128,20 @@ sneed.social
sonichu.com
spinster.xyz
springbo.cc
starnix.network
strelizia.net
syspxl.xyz
tastingtraffic.net
teci.world
theapex.social
thechimp.zone
thenobody.club
thepostearthdestination.com
tkammer.de
trumpislovetrumpis.life
truthsocial.co.in
urchan.org
usualsuspects.lol
varishangout.net
whinge.house
whinge.town
wideboys.org
vtuberfan.social
wolfgirl.bar
xn--p1abe3d.xn--80asehdb
yggdrasil.social
youjo.love
zztails.gay

View File

@@ -281,7 +281,7 @@ public class AudioPlayerService extends Service{
if(playerReady){
boolean isPlaying=player.isPlaying();
bldr.addAction(new Notification.Action.Builder(Icon.createWithResource(this, isPlaying ? R.drawable.ic_pause_24 : R.drawable.ic_play_24),
bldr.addAction(new Notification.Action.Builder(Icon.createWithResource(this, isPlaying ? R.drawable.ic_fluent_pause_24_filled : R.drawable.ic_fluent_play_24_filled),
getString(isPlaying ? R.string.pause : R.string.play),
PendingIntent.getBroadcast(this, 2, new Intent(ACTION_PLAY_PAUSE), PendingIntent.FLAG_IMMUTABLE))
.build());

View File

@@ -32,7 +32,6 @@ public class ExternalShareActivity extends FragmentStackActivity{
UiUtils.setUserPreferredTheme(this);
super.onCreate(savedInstanceState);
if(savedInstanceState==null){
Optional<String> text = Optional.ofNullable(getIntent().getStringExtra(Intent.EXTRA_TEXT));
Optional<Pair<String, Optional<String>>> fediHandle = text.flatMap(UiUtils::parseFediverseHandle);
boolean isFediUrl = text.map(UiUtils::looksLikeFediverseUrl).orElse(false);

View File

@@ -26,8 +26,6 @@ import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import androidx.annotation.StringRes;
public class GlobalUserPreferences{
private static final String TAG="GlobalUserPreferences";
@@ -58,12 +56,12 @@ public class GlobalUserPreferences{
public static boolean allowRemoteLoading;
public static boolean forwardReportDefault;
public static AutoRevealMode autoRevealEqualSpoilers;
public static ColorPreference color;
public static boolean disableM3PillActiveIndicator;
public static boolean showNavigationLabels;
public static boolean displayPronounsInTimelines, displayPronounsInThreads, displayPronounsInUserListings;
public static boolean overlayMedia;
public static boolean showSuicideHelp;
public static boolean underlinedLinks;
// MOSHIDON
public static boolean showDividers;
@@ -133,12 +131,13 @@ public class GlobalUserPreferences{
autoRevealEqualSpoilers=AutoRevealMode.valueOf(prefs.getString("autoRevealEqualSpoilers", AutoRevealMode.THREADS.name()));
forwardReportDefault=prefs.getBoolean("forwardReportDefault", true);
disableM3PillActiveIndicator=prefs.getBoolean("disableM3PillActiveIndicator", false);
showNavigationLabels=prefs.getBoolean("showNavigationLabels", true);
showNavigationLabels=prefs.getBoolean("showNavigationLabels", false);
displayPronounsInTimelines=prefs.getBoolean("displayPronounsInTimelines", true);
displayPronounsInThreads=prefs.getBoolean("displayPronounsInThreads", true);
displayPronounsInUserListings=prefs.getBoolean("displayPronounsInUserListings", true);
overlayMedia=prefs.getBoolean("overlayMedia", false);
showSuicideHelp=prefs.getBoolean("showSuicideHelp", true);
underlinedLinks=prefs.getBoolean("underlinedLinks", true);
// MOSHIDON
uniformNotificationIcon=prefs.getBoolean("uniformNotificationIcon", false);
@@ -169,18 +168,13 @@ public class GlobalUserPreferences{
.apply();
}
try {
if(android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.S){
color=ColorPreference.valueOf(prefs.getString("color", ColorPreference.MATERIAL3.name()));
}else{
color=ColorPreference.valueOf(prefs.getString("color", ColorPreference.PURPLE.name()));
}
} catch (IllegalArgumentException|ClassCastException ignored) {
// invalid color name or color was previously saved as integer
color=ColorPreference.PURPLE;
}
if(prefs.getInt("migrationLevel", 0) < 61) migrateToUpstreamVersion61();
int migrationLevel=prefs.getInt("migrationLevel", BuildConfig.VERSION_CODE);
if(migrationLevel < 61)
migrateToUpstreamVersion61();
if(migrationLevel < 102)
migrateToVersion102();
if(migrationLevel < BuildConfig.VERSION_CODE)
prefs.edit().putInt("migrationLevel", BuildConfig.VERSION_CODE).apply();
}
public static void save(){
@@ -211,7 +205,6 @@ public class GlobalUserPreferences{
.putBoolean("spectatorMode", spectatorMode)
.putBoolean("autoHideFab", autoHideFab)
.putBoolean("compactReblogReplyLine", compactReblogReplyLine)
.putString("color", color.name())
.putBoolean("allowRemoteLoading", allowRemoteLoading)
.putString("autoRevealEqualSpoilers", autoRevealEqualSpoilers.name())
.putBoolean("forwardReportDefault", forwardReportDefault)
@@ -222,6 +215,7 @@ public class GlobalUserPreferences{
.putBoolean("displayPronounsInUserListings", displayPronounsInUserListings)
.putBoolean("overlayMedia", overlayMedia)
.putBoolean("showSuicideHelp", showSuicideHelp)
.putBoolean("underlinedLinks", underlinedLinks)
// MOSHIDON
.putBoolean("defaultToUnlistedReplies", defaultToUnlistedReplies)
@@ -240,11 +234,49 @@ public class GlobalUserPreferences{
.putBoolean("showPostsWithoutAlt", showPostsWithoutAlt)
.putBoolean("showMediaPreview", showMediaPreview)
.putInt("theme", theme.ordinal())
.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 migrateToVersion102(){
Log.d(TAG, "Migrating preferences to version 102!! (copy current theme to local preferences)");
SharedPreferences prefs=getPrefs();
// only migrate if global prefs even contains a color (but like.. it should)
if(prefs.contains("color")){
AccountSessionManager asm=AccountSessionManager.getInstance();
for(AccountSession session : asm.getLoggedInAccounts()){
if(session.getRawLocalPreferences().contains("color")) continue;
AccountLocalPreferences localPrefs=session.getLocalPreferences();
localPrefs.color=AccountLocalPreferences.ColorPreference.valueOf(prefs.getString(
"color", AccountLocalPreferences.ColorPreference.MATERIAL3.name()
));
localPrefs.save();
}
prefs.edit().remove("color").apply();
}
}
private static void migrateToUpstreamVersion61(){
Log.d(TAG, "Migrating preferences to upstream version 61!!");
@@ -291,53 +323,8 @@ public class GlobalUserPreferences{
localPrefs.save();
}
prefs.edit().putInt("migrationLevel", 61).apply();
}
public enum ColorPreference{
MATERIAL3,
PURPLE,
PINK,
GREEN,
BLUE,
BROWN,
RED,
YELLOW,
NORD,
WHITE;
//endregion
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;
case NORD -> R.string.mo_color_palette_nord;
case WHITE -> R.string.mo_color_palette_black_and_white;
};
}
}
public enum ThemePreference{
AUTO,
LIGHT,
DARK
}
public enum AutoRevealMode {
NEVER,
THREADS,
DISCUSSIONS
}
public enum PrefixRepliesMode {
NEVER,
ALWAYS,
TO_OTHERS
}
}

View File

@@ -47,7 +47,8 @@ import me.grishka.appkit.api.ErrorResponse;
public class MainActivity extends FragmentStackActivity implements ProvidesAssistContent {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState){
UiUtils.setUserPreferredTheme(this);
AccountSession session=getCurrentSession();
UiUtils.setUserPreferredTheme(this, session);
super.onCreate(savedInstanceState);
if(savedInstanceState==null){
@@ -243,6 +244,36 @@ public class MainActivity extends FragmentStackActivity implements ProvidesAssis
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());

View File

@@ -28,6 +28,7 @@ import org.joinmastodon.android.api.requests.statuses.CreateStatus;
import org.joinmastodon.android.api.requests.statuses.SetStatusBookmarked;
import org.joinmastodon.android.api.requests.statuses.SetStatusFavorited;
import org.joinmastodon.android.api.requests.statuses.SetStatusReblogged;
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.model.Account;
@@ -164,6 +165,7 @@ public class PushNotificationReceiver extends BroadcastReceiver{
private void notify(Context context, PushNotification pn, String accountID, org.joinmastodon.android.model.Notification notification){
NotificationManager nm=context.getSystemService(NotificationManager.class);
AccountSession session=AccountSessionManager.get(accountID);
AccountLocalPreferences lp=session.getLocalPreferences();
Account self=session.self;
String accountName="@"+self.username+"@"+AccountSessionManager.getInstance().getAccount(accountID).domain;
Notification.Builder builder;
@@ -218,7 +220,7 @@ public class PushNotificationReceiver extends BroadcastReceiver{
if (!GlobalUserPreferences.uniformNotificationIcon) {
builder.setSmallIcon(switch (pn.notificationType) {
case FAVORITE -> R.drawable.ic_fluent_star_24_filled;
case FAVORITE -> lp.likeIcon ? R.drawable.ic_fluent_heart_24_filled : R.drawable.ic_fluent_star_24_filled;
case REBLOG -> R.drawable.ic_fluent_arrow_repeat_all_24_filled;
case FOLLOW -> R.drawable.ic_fluent_person_add_24_filled;
case MENTION -> R.drawable.ic_fluent_mention_24_filled;

View File

@@ -74,7 +74,6 @@ public class CacheController{
result.add(status);
}while(cursor.moveToNext());
String _newMaxID=newMaxID;
AccountSessionManager.get(accountID).filterStatuses(result, FilterContext.HOME);
uiHandler.post(()->callback.onSuccess(new CacheablePaginatedResponse<>(result, _newMaxID, true)));
return;
}
@@ -86,9 +85,7 @@ public class CacheController{
.setCallback(new Callback<>(){
@Override
public void onSuccess(List<Status> result){
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));
callback.onSuccess(new CacheablePaginatedResponse<>(result, result.isEmpty() ? null : result.get(result.size()-1).id, false));
putHomeTimeline(result, maxID==null);
}

View File

@@ -97,7 +97,7 @@ public class PushSubscriptionManager{
deviceToken=getPrefs().getString("deviceToken", null);
int tokenVersion=getPrefs().getInt("version", 0);
if(!TextUtils.isEmpty(deviceToken) && tokenVersion==BuildConfig.VERSION_CODE){
registerAllAccountsForPush(true); // TODO: revert this before release
registerAllAccountsForPush(false);
return;
}
Log.i(TAG, "tryRegisterFCM: no token found or app was updated. Trying to get push token...");

View File

@@ -13,7 +13,7 @@ import okhttp3.MultipartBody;
import okhttp3.RequestBody;
public class PleromaMarkNotificationsRead extends MastodonAPIRequest<List<Notification>> {
private String maxID;
private final String maxID;
public PleromaMarkNotificationsRead(String maxID) {
super(HttpMethod.POST, "/pleroma/notifications/read", new TypeToken<>(){});
this.maxID = maxID;

View File

@@ -6,10 +6,14 @@ import static org.joinmastodon.android.api.MastodonAPIController.gson;
import android.content.SharedPreferences;
import androidx.annotation.StringRes;
import com.google.gson.reflect.TypeToken;
import org.joinmastodon.android.R;
import org.joinmastodon.android.model.ContentType;
import org.joinmastodon.android.model.Emoji;
import org.joinmastodon.android.model.Emoji;
import org.joinmastodon.android.model.PushSubscription;
import org.joinmastodon.android.model.TimelineDefinition;
@@ -41,16 +45,19 @@ public class AccountLocalPreferences{
public String publishButtonText;
public String timelineReplyVisibility; // akkoma-only
public boolean keepOnlyLatestNotification;
public boolean emojiReactionsEnabled;
public ShowEmojiReactions showEmojiReactions;
public ColorPreference color;
public boolean likeIcon;
public ArrayList<Emoji> recentCustomEmoji;
private final static Type recentLanguagesType = new TypeToken<ArrayList<String>>() {}.getType();
private final static Type timelinesType = new TypeToken<ArrayList<TimelineDefinition>>() {}.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 recentCustomEmojiType=new TypeToken<ArrayList<Emoji>>() {}.getType();
// MOSHIDON
private final static Type recentEmojisType = new TypeToken<Map<String, Integer>>() {}.getType();
public Map<String, Integer> recentEmojis;
// private final static Type recentEmojisType = new TypeToken<Map<String, Integer>>() {}.getType();
// public Map<String, Integer> recentEmojis;
private final static Type notificationFiltersType = new TypeToken<PushSubscription.Alerts>() {}.getType();
public PushSubscription.Alerts notificationFilters;
@@ -77,9 +84,12 @@ public class AccountLocalPreferences{
keepOnlyLatestNotification=prefs.getBoolean("keepOnlyLatestNotification", false);
emojiReactionsEnabled=prefs.getBoolean("emojiReactionsEnabled", session.getInstance().isPresent() && session.getInstance().get().isAkkoma());
showEmojiReactions=ShowEmojiReactions.valueOf(prefs.getString("showEmojiReactions", ShowEmojiReactions.HIDE_EMPTY.name()));
color=ColorPreference.valueOf(prefs.getString("color", ColorPreference.MATERIAL3.name()));
likeIcon=prefs.getBoolean("likeIcon", false);
recentCustomEmoji=fromJson(prefs.getString("recentCustomEmoji", null), recentCustomEmojiType, new ArrayList<>());
// MOSHIDON
recentEmojis=fromJson(prefs.getString("recentEmojis", "{}"), recentEmojisType, new HashMap<>());
// recentEmojis=fromJson(prefs.getString("recentEmojis", "{}"), recentEmojisType, new HashMap<>());
notificationFilters=fromJson(prefs.getString("notificationFilters", gson.toJson(PushSubscription.Alerts.ofAll())), notificationFiltersType, PushSubscription.Alerts.ofAll());
}
@@ -114,13 +124,40 @@ public class AccountLocalPreferences{
.putBoolean("keepOnlyLatestNotification", keepOnlyLatestNotification)
.putBoolean("emojiReactionsEnabled", emojiReactionsEnabled)
.putString("showEmojiReactions", showEmojiReactions.name())
.putString("color", color.name())
.putBoolean("likeIcon", likeIcon)
.putString("recentCustomEmoji", gson.toJson(recentCustomEmoji))
// MOSHIDON
.putString("recentEmojis", gson.toJson(recentEmojis))
// .putString("recentEmojis", gson.toJson(recentEmojis))
.putString("notificationFilters", gson.toJson(notificationFilters))
.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{
HIDE_EMPTY,
ONLY_OPENED,

View File

@@ -40,7 +40,6 @@ import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import me.grishka.appkit.api.Callback;
import me.grishka.appkit.api.ErrorResponse;
@@ -255,52 +254,62 @@ public class AccountSession{
filterStatusContainingObjects(objects, extractor, context, null);
}
public <T> void filterStatusContainingObjects(List<T> objects, Function<T, Status> extractor, FilterContext context, Account profile){
Predicate<Status> statusIsOnOwnProfile = (s) -> self != null && profile != null && s.account != 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);
}
if(getLocalPreferences().serverSideFiltersSupported){
// Even with server-side filters, clients are expected to remove statuses that match a filter that hides them
objects.removeIf(o->{
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){
if(!localPreferences.serverSideFiltersSupported) for(T obj:objects){
Status s=extractor.apply(obj);
if(s!=null && s.filtered!=null){
localPreferences.serverSideFiltersSupported=true;
localPreferences.save();
}
}
List<T> removeUs=new ArrayList<>();
for(int i=0; i<objects.size(); i++){
T o=objects.get(i);
if(filterStatusContainingObject(o, extractor, context, profile)){
Status s=extractor.apply(o);
removeUs.add(o);
if(s!=null && s.hasGapAfter && i > 0){
Status prev=extractor.apply(objects.get(i - 1));
if(prev!=null) prev.hasGapAfter=true;
}
}
}
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;
if(s.filtered==null)
return false;
// don't hide own posts in own profile
if (statusIsOnOwnProfile.test(s))
if(statusIsOnOwnProfile(s, profile))
return false;
for(FilterResult filter:s.filtered){
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;
}
return false;
});
return;
}
if(wordFilters==null)
return;
for(T obj:objects){
Status s=extractor.apply(obj);
if(s!=null && s.filtered!=null){
getLocalPreferences().serverSideFiltersSupported=true;
getLocalPreferences().save();
return;
}
}
objects.removeIf(o->{
Status s=extractor.apply(o);
if(s==null)
return false;
// don't hide own posts in own profile
if (statusIsOnOwnProfile.test(s))
return false;
for(LegacyFilter filter:wordFilters){
}else if(wordFilters!=null){
for(LegacyFilter filter : wordFilters){
if(filter.context.contains(context) && filter.matches(s) && filter.isActive())
return true;
}
}
return false;
});
}
public void updateAccountInfo(){

View File

@@ -9,19 +9,16 @@ import org.joinmastodon.android.R;
import org.joinmastodon.android.api.requests.accounts.GetAccountStatuses;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.events.RemoveAccountPostsEvent;
import org.joinmastodon.android.events.StatusCreatedEvent;
import org.joinmastodon.android.events.StatusUnpinnedEvent;
import org.joinmastodon.android.model.Account;
import org.joinmastodon.android.model.FilterContext;
import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.ui.displayitems.HeaderStatusDisplayItem;
import org.joinmastodon.android.utils.StatusFilterPredicate;
import org.parceler.Parcels;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import me.grishka.appkit.api.SimpleCallback;
@@ -55,15 +52,14 @@ public class AccountTimelineFragment extends StatusListFragment{
@Override
protected void doLoadData(int offset, int count){
currentRequest=new GetAccountStatuses(user.id, offset>0 ? getMaxID() : null, null, count, filter)
currentRequest=new GetAccountStatuses(user.id, getMaxID(), null, count, filter)
.setCallback(new SimpleCallback<>(this){
@Override
public void onSuccess(List<Status> result){
if(getActivity()==null) return;
AccountSessionManager asm = AccountSessionManager.getInstance();
boolean empty=result.isEmpty();
boolean more=applyMaxID(result);
AccountSessionManager.get(accountID).filterStatuses(result, getFilterContext(), user);
onDataLoaded(result, !empty);
onDataLoaded(result, more);
}
})
.exec(accountID);

View File

@@ -97,7 +97,7 @@ public class AnnouncementsFragment extends BaseStatusListFragment<Announcement>
.setCallback(new SimpleCallback<>(this){
@Override
public void onSuccess(List<Announcement> result){
if (getActivity() == null) return;
if(getActivity()==null) return;
// get unread items first
List<Announcement> data = result.stream().filter(a -> !a.read).collect(toList());

View File

@@ -9,7 +9,6 @@ import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowInsets;
@@ -100,6 +99,7 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
protected TypedObjectPool<MediaGridStatusDisplayItem.GridItemType, PreviewlessMediaAttachmentViewController> previewlessAttachmentViewsPool=new TypedObjectPool<>(this::makeNewPreviewlessMediaAttachmentView);
protected boolean currentlyScrolling;
protected String maxID;
public BaseStatusListFragment(){
super(20);
@@ -166,6 +166,8 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
}
protected String getMaxID(){
if(refreshing) return null;
if(maxID!=null) return maxID;
if(!preloadedData.isEmpty())
return preloadedData.get(preloadedData.size()-1).getID();
else if(!data.isEmpty())
@@ -174,6 +176,12 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
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 void addAccountToKnown(T s);
@@ -642,12 +650,12 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
// to do this if the media grid is not bound, tho - so, doing it ourselves here
status.sensitiveRevealed = !status.sensitiveRevealed;
}
holder.rebind();
if(holder.getItem().hasVisibilityToggle) holder.animateVisibilityToggle(false);
}
public void onSensitiveRevealed(MediaGridStatusDisplayItem.Holder holder) {
HeaderStatusDisplayItem.Holder header = findHolderOfType(holder.getItemID(), HeaderStatusDisplayItem.Holder.class);
if(header != null) header.rebind();
if(header != null && header.getItem().hasVisibilityToggle) header.animateVisibilityToggle(true);
}
protected void toggleSpoiler(Status status, String itemID){
@@ -695,15 +703,6 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
if (header != null) header.rebind();
}
public void updateEmojiReactions(Status status, String itemID){
EmojiReactionsStatusDisplayItem.Holder reactions=findHolderOfType(itemID, EmojiReactionsStatusDisplayItem.Holder.class);
if(reactions != null){
reactions.getItem().status.reactions.clear();
reactions.getItem().status.reactions.addAll(status.reactions);
reactions.rebind();
}
}
public void onGapClick(GapStatusDisplayItem.Holder item, boolean downwards){}
public void onWarningClick(WarningFilteredStatusDisplayItem.Holder warning){
@@ -937,7 +936,7 @@ public abstract class BaseStatusListFragment<T extends DisplayItemsParent> exten
if(getContext()==null) return;
super.onDataLoaded(d, more);
// more available, but the page isn't even full yet? seems wrong, let's load some more
if(more && d.size() < itemsPerPage){
if(more && data.size() < itemsPerPage){
preloader.onScrolledToLastItem();
}
}

View File

@@ -28,7 +28,7 @@ public class BookmarkedStatusListFragment extends StatusListFragment{
.setCallback(new SimpleCallback<>(this){
@Override
public void onSuccess(HeaderPaginationList<Status> result){
if (getActivity() == null) return;
if(getActivity()==null) return;
if(result.nextPageUri!=null)
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
else

View File

@@ -177,7 +177,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
private int charCount, charLimit, trimmedCharCount;
private Button publishButton, languageButton, scheduleTimeBtn;
private PopupMenu languagePopup, contentTypePopup, visibilityPopup, draftOptionsPopup;
private PopupMenu contentTypePopup, visibilityPopup, draftOptionsPopup;
private ImageButton publishButtonRelocated, mediaBtn, pollBtn, emojiBtn, spoilerBtn, draftsBtn, scheduleDraftDismiss, contentTypeBtn;
private View sensitiveBtn;
private TextView replyText;
@@ -313,7 +313,7 @@ public class ComposeFragment extends MastodonToolbarFragment implements OnBackPr
@Override
public View onCreateContentView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
creatingView=true;
emojiKeyboard=new CustomEmojiPopupKeyboard(getActivity(), customEmojis, instanceDomain, getAccountID());
emojiKeyboard=new CustomEmojiPopupKeyboard(getActivity(), accountID, customEmojis, instanceDomain);
emojiKeyboard.setListener(new CustomEmojiPopupKeyboard.Listener(){
@Override
public void onEmojiSelected(Emoji emoji){

View File

@@ -26,12 +26,12 @@ import android.widget.ImageView;
import org.joinmastodon.android.GlobalUserPreferences;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.MastodonAPIController;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.model.Attachment;
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
import org.joinmastodon.android.ui.photoviewer.PhotoViewer;
import org.joinmastodon.android.ui.utils.ColorPalette;
import org.joinmastodon.android.ui.utils.UiUtils;
import org.joinmastodon.android.ui.views.FixedAspectRatioImageView;
import java.util.Collections;
@@ -54,16 +54,16 @@ public class ComposeImageDescriptionFragment extends MastodonToolbarFragment imp
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
accountID=getArguments().getString("account");
attachmentID=getArguments().getString("attachment");
setHasOptionsMenu(true);
}
@Override
public void onAttach(Activity activity){
super.onAttach(activity);
accountID=getArguments().getString("account");
attachmentID=getArguments().getString("attachment");
themeWrapper=new ContextThemeWrapper(activity, R.style.Theme_Mastodon_Dark);
ColorPalette.palettes.get(GlobalUserPreferences.color).apply(themeWrapper, GlobalUserPreferences.ThemePreference.DARK);
ColorPalette.palettes.get(AccountSessionManager.get(accountID).getLocalPreferences().color).apply(themeWrapper, GlobalUserPreferences.ThemePreference.DARK);
setTitle(R.string.add_alt_text);
}

View File

@@ -201,7 +201,7 @@ public class EditTimelinesFragment extends MastodonRecyclerFragment<TimelineDefi
}
private void updateOptionsMenu() {
if (getActivity() == null) return;
if(getActivity()==null) return;
optionsMenu.clear();
timelineByMenuItem.clear();

View File

@@ -27,7 +27,7 @@ public class FavoritedStatusListFragment extends StatusListFragment{
.setCallback(new SimpleCallback<>(this){
@Override
public void onSuccess(HeaderPaginationList<Status> result){
if (getActivity() == null) return;
if(getActivity()==null) return;
if(result.nextPageUri!=null)
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
else

View File

@@ -83,7 +83,7 @@ public class FollowRequestsListFragment extends MastodonRecyclerFragment<FollowR
.setCallback(new SimpleCallback<>(this){
@Override
public void onSuccess(HeaderPaginationList<Account> result){
if (getActivity() == null) return;
if(getActivity()==null) return;
if(result.nextPageUri!=null)
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
else

View File

@@ -56,7 +56,7 @@ public class FollowedHashtagsFragment extends MastodonRecyclerFragment<Hashtag>
.setCallback(new SimpleCallback<>(this){
@Override
public void onSuccess(HeaderPaginationList<Hashtag> result){
if (getActivity() == null) return;
if(getActivity()==null) return;
if(result.nextPageUri!=null)
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
else

View File

@@ -29,6 +29,7 @@ import org.joinmastodon.android.api.requests.tags.SetTagFollowed;
import org.joinmastodon.android.api.requests.timelines.GetHashtagTimeline;
import org.joinmastodon.android.model.Filter;
import org.joinmastodon.android.model.FilterAction;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.model.FilterContext;
import org.joinmastodon.android.model.FilterKeyword;
import org.joinmastodon.android.model.Hashtag;
@@ -153,11 +154,14 @@ public class HashtagTimelineFragment extends PinnableStatusListFragment{
@Override
protected void doLoadData(int offset, int count){
currentRequest=new GetHashtagTimeline(hashtagName, offset==0 ? null : getMaxID(), null, count, any, all, none, localOnly, getLocalPrefs().timelineReplyVisibility)
currentRequest=new GetHashtagTimeline(hashtagName, getMaxID(), null, count, any, all, none, localOnly, getLocalPrefs().timelineReplyVisibility)
.setCallback(new SimpleCallback<>(this){
@Override
public void onSuccess(List<Status> result){
onDataLoaded(result, !result.isEmpty());
if(getActivity()==null) return;
boolean more=applyMaxID(result);
AccountSessionManager.get(accountID).filterStatuses(result, getFilterContext());
onDataLoaded(result, more);
}
})
.exec(accountID);
@@ -285,6 +289,7 @@ public class HashtagTimelineFragment extends PinnableStatusListFragment{
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);
muteMenuItem = optionsMenu.findItem(R.id.mute_hashtag);
@@ -339,7 +344,7 @@ public class HashtagTimelineFragment extends PinnableStatusListFragment{
}
private void updateHeader(){
if(hashtag==null)
if(hashtag==null || getActivity()==null)
return;
if(hashtag.history!=null && !hashtag.history.isEmpty()){

View File

@@ -291,7 +291,7 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
new GetAnnouncements(false).setCallback(new Callback<>() {
@Override
public void onSuccess(List<Announcement> result) {
if (getActivity() == null) return;
if(getActivity()==null) return;
if (result.stream().anyMatch(a -> !a.read)) {
announcementsBadged = true;
announcements.setVisible(false);
@@ -385,7 +385,7 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab
}
private void updateOverflowMenu() {
if (getActivity() == null) return;
if(getActivity()==null) return;
Menu m = overflowPopup.getMenu();
m.clear();
overflowPopup.inflate(R.menu.home_overflow);

View File

@@ -11,7 +11,6 @@ import androidx.recyclerview.widget.RecyclerView;
import org.joinmastodon.android.GlobalUserPreferences;
import org.joinmastodon.android.api.requests.markers.SaveMarkers;
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.model.CacheablePaginatedResponse;
import org.joinmastodon.android.model.FilterContext;
@@ -20,6 +19,7 @@ import org.joinmastodon.android.model.TimelineMarkers;
import org.joinmastodon.android.ui.displayitems.GapStatusDisplayItem;
import org.joinmastodon.android.ui.displayitems.StatusDisplayItem;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
@@ -49,16 +49,6 @@ public class HomeTimelineFragment extends StatusListFragment {
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
protected void doLoadData(int offset, int count){
AccountSessionManager.getInstance()
@@ -66,10 +56,11 @@ public class HomeTimelineFragment extends StatusListFragment {
.getHomeTimeline(offset>0 ? maxID : null, count, refreshing, new SimpleCallback<>(this){
@Override
public void onSuccess(CacheablePaginatedResponse<List<Status>> result){
if (getActivity() == null) return;
List<Status> filteredItems = filterPosts(result.items);
if(getActivity()==null) return;
boolean empty=result.items.isEmpty();
maxID=result.maxID;
onDataLoaded(filteredItems, !result.items.isEmpty());
AccountSessionManager.get(accountID).filterStatuses(result.items, getFilterContext());
onDataLoaded(result.items, !empty);
if(result.isFromCache())
loadNewPosts();
}
@@ -142,23 +133,26 @@ public class HomeTimelineFragment extends StatusListFragment {
public void onSuccess(List<Status> result){
currentRequest=null;
dataLoading=false;
result = filterPosts(result);
if(result.isEmpty() || getActivity()==null)
return;
Status last=result.get(result.size()-1);
List<Status> toAdd;
if(!data.isEmpty() && last.id.equals(data.get(0).id)){ // This part intersects with the existing one
toAdd=result.subList(0, result.size()-1); // Remove the already known last post
toAdd=new ArrayList<>(result.subList(0, result.size()-1)); // Remove the already known last post
}else{
result.get(result.size()-1).hasGapAfter=true;
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());
if(!toAdd.isEmpty()){
prependItems(toAdd, true);
if (parent != null && GlobalUserPreferences.showNewPostsButton) parent.showNewPostsButton();
AccountSessionManager.getInstance().getAccount(accountID).getCacheController().putHomeTimeline(toAdd, false);
if(parent != null && GlobalUserPreferences.showNewPostsButton) parent.showNewPostsButton();
}
if(toAddUnfiltered.isEmpty())
AccountSessionManager.getInstance().getAccount(accountID).getCacheController().putHomeTimeline(toAddUnfiltered, false);
}
@Override

View File

@@ -16,6 +16,7 @@ import org.joinmastodon.android.R;
import org.joinmastodon.android.api.requests.lists.GetList;
import org.joinmastodon.android.api.requests.lists.UpdateList;
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.ListUpdatedCreatedEvent;
import org.joinmastodon.android.model.FilterContext;
@@ -25,10 +26,8 @@ import org.joinmastodon.android.model.TimelineDefinition;
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
import org.joinmastodon.android.ui.utils.UiUtils;
import org.joinmastodon.android.ui.views.ListEditor;
import org.joinmastodon.android.utils.StatusFilterPredicate;
import java.util.List;
import java.util.stream.Collectors;
import me.grishka.appkit.Nav;
import me.grishka.appkit.api.Callback;
@@ -63,7 +62,7 @@ public class ListTimelineFragment extends PinnableStatusListFragment {
new GetList(listID).setCallback(new Callback<>() {
@Override
public void onSuccess(ListTimeline listTimeline) {
if (getActivity() == null) return;
if(getActivity()==null) return;
// TODO: save updated info
if (!listTimeline.title.equals(listTitle)) setTitle(listTimeline.title);
if (listTimeline.repliesPolicy != null && !listTimeline.repliesPolicy.equals(repliesPolicy)) {
@@ -101,7 +100,7 @@ public class ListTimelineFragment extends PinnableStatusListFragment {
new UpdateList(listID, newTitle, editor.isExclusive(), editor.getRepliesPolicy()).setCallback(new Callback<>() {
@Override
public void onSuccess(ListTimeline list) {
if (getActivity() == null) return;
if(getActivity()==null) return;
setTitle(list.title);
listTitle = list.title;
repliesPolicy = list.repliesPolicy;
@@ -134,13 +133,14 @@ public class ListTimelineFragment extends PinnableStatusListFragment {
@Override
protected void doLoadData(int offset, int count) {
currentRequest=new GetListTimeline(listID, offset==0 ? null : getMaxID(), null, count, null, getLocalPrefs().timelineReplyVisibility)
currentRequest=new GetListTimeline(listID, getMaxID(), null, count, null, getLocalPrefs().timelineReplyVisibility)
.setCallback(new SimpleCallback<>(this) {
@Override
public void onSuccess(List<Status> result) {
if (getActivity() == null) return;
result=result.stream().filter(new StatusFilterPredicate(accountID, getFilterContext())).collect(Collectors.toList());
onDataLoaded(result, !result.isEmpty());
if(getActivity()==null) return;
boolean more=applyMaxID(result);
AccountSessionManager.get(accountID).filterStatuses(result, getFilterContext());
onDataLoaded(result, more);
}
})
.exec(accountID);

View File

@@ -140,7 +140,7 @@ public class ListsFragment extends MastodonRecyclerFragment<ListTimeline> implem
.setCallback(new SimpleCallback<>(this) {
@Override
public void onSuccess(List<ListTimeline> lists) {
if (getActivity() == null) return;
if(getActivity()==null) return;
for (ListTimeline l : lists) userInListBefore.put(l.id, true);
userInList.putAll(userInListBefore);
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) {
@Override
public void onSuccess(List<ListTimeline> allLists) {
if (getActivity() == null) return;
if(getActivity()==null) return;
List<ListTimeline> newLists = new ArrayList<>();
for (ListTimeline l : allLists) {
if (lists.stream().noneMatch(e -> e.id.equals(l.id))) newLists.add(l);

View File

@@ -327,7 +327,7 @@ public class NotificationsFragment extends MastodonToolbarFragment implements Sc
new GetFollowRequests(null, 1).setCallback(new Callback<>() {
@Override
public void onSuccess(HeaderPaginationList<Account> accounts) {
if (getActivity() == null) return;
if(getActivity()==null) return;
getToolbar().getMenu().findItem(R.id.follow_requests).setVisible(!accounts.isEmpty());
}

View File

@@ -178,6 +178,7 @@ public class NotificationsListFragment extends BaseStatusListFragment<Notificati
maxID=result.maxID;
onDataLoaded(result.items.stream().filter(n->n.type!=null).collect(Collectors.toList()), !result.items.isEmpty());
if(bannerHelper!=null) bannerHelper.onBannerBecameVisible();
reloadingFromCache=false;
if (getParentFragment() instanceof NotificationsFragment nf) {
nf.updateMarkAllReadButton();

View File

@@ -5,6 +5,7 @@ import android.os.Bundle;
import org.joinmastodon.android.R;
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.FilterContext;
import org.joinmastodon.android.model.Status;
@@ -35,6 +36,8 @@ public class PinnedPostsListFragment extends StatusListFragment{
.setCallback(new SimpleCallback<>(this){
@Override
public void onSuccess(List<Status> result){
if(getActivity()==null) return;
AccountSessionManager.get(accountID).filterStatuses(result, getFilterContext());
onDataLoaded(result, false);
}
}).exec(accountID);

View File

@@ -138,6 +138,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
private ImageView avatar;
private CoverImageView cover;
private View avatarBorder;
private View usernameWrap;
private TextView name, username, bio, followersCount, followersLabel, followingCount, followingLabel;
private ImageView lockIcon, botIcon;
private ProgressBarButton actionButton, notifyButton;
@@ -241,6 +242,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
cover=content.findViewById(R.id.cover);
avatarBorder=content.findViewById(R.id.avatar_border);
name=content.findViewById(R.id.name);
usernameWrap=content.findViewById(R.id.username_wrap);
username=content.findViewById(R.id.username);
lockIcon=content.findViewById(R.id.lock_icon);
botIcon=content.findViewById(R.id.bot_icon);
@@ -563,7 +565,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
.setCallback(new SimpleCallback<>(this){
@Override
public void onSuccess(Account result){
if (getActivity() == null) return;
if(getActivity()==null) return;
onAccountLoaded(result);
}
})
@@ -984,7 +986,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
}
private void updateRelationship(){
if (getActivity() == null) return;
if(getActivity()==null) return;
invalidateOptionsMenu();
actionButton.setVisibility(View.VISIBLE);
notifyButton.setVisibility(relationship.following ? View.VISIBLE : View.GONE);
@@ -1272,6 +1274,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
imm.hideSoftInputFromWindow(content.getWindowToken(), 0);
V.setVisibilityAnimated(fab, View.VISIBLE);
bindHeaderView();
V.setVisibilityAnimated(fab, View.VISIBLE);
}
private void saveAndExitEditMode(){
@@ -1286,7 +1289,7 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList
savingEdits=false;
account=result;
AccountSessionManager.getInstance().updateAccountInfo(accountID, account);
if (getActivity() == null) return;
if(getActivity()==null) return;
exitEditMode();
setActionProgressVisible(false);
}

View File

@@ -130,7 +130,7 @@ public class ScheduledStatusListFragment extends BaseStatusListFragment<Schedule
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
else
nextMaxID=null;
if (getActivity() == null) return;
if(getActivity()==null) return;
onDataLoaded(result, nextMaxID!=null);
}
})

View File

@@ -47,13 +47,12 @@ public class SplashFragment extends AppKitFragment{
private ProgressBarButton defaultServerButton;
private ProgressBar defaultServerProgress;
private String chosenDefaultServer=DEFAULT_SERVER;
private boolean loadingDefaultServer;
private boolean loadingDefaultServer, loadedDefaultServer;
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
motionEffect=new InterpolatingMotionEffect(MastodonApp.context);
loadAndChooseDefaultServer();
}
@Nullable
@@ -101,6 +100,8 @@ public class SplashFragment extends AppKitFragment{
});
}
});
if(!loadedDefaultServer && !loadingDefaultServer)
loadAndChooseDefaultServer();
return contentView;
}
@@ -239,6 +240,7 @@ public class SplashFragment extends AppKitFragment{
private void setChosenDefaultServer(String domain){
chosenDefaultServer=domain;
loadingDefaultServer=false;
loadedDefaultServer=true;
if(defaultServerButton!=null && getActivity()!=null){
defaultServerButton.setTextVisible(true);
defaultServerProgress.setVisibility(View.GONE);

View File

@@ -48,8 +48,8 @@ public class StatusEditHistoryFragment extends StatusListFragment{
.setCallback(new SimpleCallback<>(this){
@Override
public void onSuccess(List<Status> result){
if(getActivity()==null) return;
Collections.sort(result, Comparator.comparing((Status s)->s.createdAt).reversed());
if (getActivity() == null) return;
onDataLoaded(result, false);
}
})

View File

@@ -45,10 +45,12 @@ public abstract class StatusListFragment extends BaseStatusListFragment<Status>
boolean isMainThreadStatus = this instanceof ThreadFragment t && s.id.equals(t.mainStatus.id);
int flags = 0;
AccountLocalPreferences lp=getLocalPrefs();
if (GlobalUserPreferences.spectatorMode)
if(GlobalUserPreferences.spectatorMode)
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;
if(GlobalUserPreferences.translateButtonOpenedOnly)
flags |= StatusDisplayItem.FLAG_NO_TRANSLATE;
if(!GlobalUserPreferences.showMediaPreview)
flags |= StatusDisplayItem.FLAG_NO_MEDIA_PREVIEW;
return StatusDisplayItem.buildItems(this, s, accountID, s, knownAccounts, getFilterContext(), isMainThreadStatus ? 0 : flags);

View File

@@ -35,7 +35,6 @@ import org.joinmastodon.android.ui.displayitems.WarningFilteredStatusDisplayItem
import org.joinmastodon.android.ui.text.HtmlParser;
import org.joinmastodon.android.ui.utils.UiUtils;
import org.joinmastodon.android.utils.ProvidesAssistContent;
import org.joinmastodon.android.utils.StatusFilterPredicate;
import org.parceler.Parcels;
import java.util.ArrayDeque;
@@ -221,8 +220,8 @@ public class ThreadFragment extends StatusListFragment implements ProvidesAssist
// TODO: figure out how this code works
if (isInstanceAkkoma()) sortStatusContext(mainStatus, result);
result.descendants=filterStatuses(result.descendants);
result.ancestors=filterStatuses(result.ancestors);
filterStatuses(result.descendants);
filterStatuses(result.ancestors);
restoreStatusStates(result.descendants, oldData);
restoreStatusStates(result.ancestors, oldData);
@@ -358,11 +357,8 @@ public class ThreadFragment extends StatusListFragment implements ProvidesAssist
.collect(Collectors.toList());
}
private List<Status> filterStatuses(List<Status> statuses){
StatusFilterPredicate statusFilterPredicate=new StatusFilterPredicate(accountID,getFilterContext());
return statuses.stream()
.filter(statusFilterPredicate)
.collect(Collectors.toList());
private void filterStatuses(List<Status> statuses){
AccountSessionManager.get(accountID).filterStatuses(statuses, getFilterContext());
}
@Override

View File

@@ -83,7 +83,7 @@ public abstract class BaseAccountListFragment extends MastodonRecyclerFragment<A
for(Relationship rel:result){
relationships.put(rel.id, rel);
}
if (getActivity() == null) return;
if(getActivity()==null) return;
if(list==null)
return;
for(int i=0;i<list.getChildCount();i++){

View File

@@ -133,7 +133,7 @@ public abstract class PaginatedAccountListFragment<T> extends BaseAccountListFra
nextMaxID=result.nextPageUri.getQueryParameter("max_id");
else
nextMaxID=null;
if (getActivity() == null) return;
if(getActivity()==null) return;
List<AccountViewModel> items = result.stream()
.filter(a -> d.size() > 1000 || d.stream()
.noneMatch(i -> i.account.url.equals(a.url)))

View File

@@ -6,21 +6,19 @@ import android.os.Bundle;
import androidx.recyclerview.widget.RecyclerView;
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.model.FilterContext;
import org.joinmastodon.android.model.Status;
import org.joinmastodon.android.ui.utils.DiscoverInfoBannerHelper;
import org.joinmastodon.android.utils.StatusFilterPredicate;
import java.util.List;
import java.util.stream.Collectors;
import me.grishka.appkit.api.SimpleCallback;
import me.grishka.appkit.utils.MergeRecyclerAdapter;
public class BubbleTimelineFragment extends StatusListFragment {
private DiscoverInfoBannerHelper bannerHelper;
private String maxID;
@Override
public void onCreate(Bundle savedInstanceState){
@@ -36,15 +34,15 @@ public class BubbleTimelineFragment extends StatusListFragment {
@Override
protected void doLoadData(int offset, int count){
currentRequest=new GetBubbleTimeline(refreshing ? null : maxID, count, getLocalPrefs().timelineReplyVisibility)
currentRequest=new GetBubbleTimeline(getMaxID(), count, getLocalPrefs().timelineReplyVisibility)
.setCallback(new SimpleCallback<>(this){
@Override
public void onSuccess(List<Status> result){
if(!result.isEmpty())
maxID=result.get(result.size()-1).id;
if (getActivity() == null) return;
result=result.stream().filter(new StatusFilterPredicate(accountID, getFilterContext())).collect(Collectors.toList());
onDataLoaded(result, !result.isEmpty());
if(getActivity()==null) return;
boolean more=applyMaxID(result);
AccountSessionManager.get(accountID).filterStatuses(result, getFilterContext());
onDataLoaded(result, more);
bannerHelper.onBannerBecameVisible();
}
})
.exec(accountID);

View File

@@ -77,7 +77,7 @@ public class DiscoverAccountsFragment extends MastodonRecyclerFragment<DiscoverA
.setCallback(new SimpleCallback<>(this){
@Override
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);
loadRelationships();
}
@@ -112,7 +112,7 @@ public class DiscoverAccountsFragment extends MastodonRecyclerFragment<DiscoverA
public void onSuccess(List<Relationship> result){
relationshipsRequest=null;
relationships=result.stream().collect(Collectors.toMap(rel->rel.id, Function.identity()));
if (getActivity() == null) return;
if(getActivity()==null) return;
if(list==null)
return;
for(int i=0;i<list.getChildCount();i++){

View File

@@ -97,8 +97,6 @@ public class DiscoverFragment extends AppKitFragment implements ScrollableToTop,
pager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback(){
@Override
public void onPageSelected(int position){
if(position==0)
return;
Fragment _page=getFragmentForPage(position);
if(_page instanceof BaseRecyclerFragment<?> page){
if(!page.loaded && !page.isDataLoading())

View File

@@ -4,6 +4,7 @@ import android.net.Uri;
import android.os.Bundle;
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.model.FilterContext;
import org.joinmastodon.android.model.Status;
@@ -17,6 +18,7 @@ import me.grishka.appkit.utils.MergeRecyclerAdapter;
public class DiscoverPostsFragment extends StatusListFragment{
private DiscoverInfoBannerHelper bannerHelper;
private int offset;
@Override
public void onCreate(Bundle savedInstanceState){
@@ -26,12 +28,17 @@ public class DiscoverPostsFragment extends StatusListFragment{
@Override
protected void doLoadData(int offset, int count){
protected void doLoadData(int o, int count){
if(refreshing) offset=0;
currentRequest=new GetTrendingStatuses(offset, count)
.setCallback(new SimpleCallback<>(this){
@Override
public void onSuccess(List<Status> result){
onDataLoaded(result, !result.isEmpty());
if(getActivity()==null) return;
boolean empty=result.isEmpty();
offset+=result.size();
AccountSessionManager.get(accountID).filterStatuses(result, getFilterContext());
onDataLoaded(result, !empty);
bannerHelper.onBannerBecameVisible();
}
}).exec(accountID);

View File

@@ -29,15 +29,14 @@ public class FederatedTimelineFragment extends StatusListFragment{
@Override
protected void doLoadData(int offset, int count){
currentRequest=new GetPublicTimeline(false, false, refreshing ? null : maxID, count, getLocalPrefs().timelineReplyVisibility)
currentRequest=new GetPublicTimeline(false, false, getMaxID(), count, getLocalPrefs().timelineReplyVisibility)
.setCallback(new SimpleCallback<>(this){
@Override
public void onSuccess(List<Status> result){
if(!result.isEmpty())
maxID=result.get(result.size()-1).id;
boolean empty=result.isEmpty();
AccountSessionManager.get(accountID).filterStatuses(result, FilterContext.PUBLIC);
onDataLoaded(result, !empty);
if(getActivity()==null) return;
boolean more=applyMaxID(result);
AccountSessionManager.get(accountID).filterStatuses(result, getFilterContext());
onDataLoaded(result, more);
bannerHelper.onBannerBecameVisible();
}
})

View File

@@ -2,7 +2,6 @@ package org.joinmastodon.android.fragments.discover;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import org.joinmastodon.android.api.requests.timelines.GetPublicTimeline;
import org.joinmastodon.android.api.session.AccountSessionManager;
@@ -30,15 +29,14 @@ public class LocalTimelineFragment extends StatusListFragment{
@Override
protected void doLoadData(int offset, int count){
currentRequest=new GetPublicTimeline(true, false, refreshing ? null : maxID, count, getLocalPrefs().timelineReplyVisibility)
currentRequest=new GetPublicTimeline(true, false, getMaxID(), count, getLocalPrefs().timelineReplyVisibility)
.setCallback(new SimpleCallback<>(this){
@Override
public void onSuccess(List<Status> result){
if(!result.isEmpty())
maxID=result.get(result.size()-1).id;
boolean empty=result.isEmpty();
AccountSessionManager.get(accountID).filterStatuses(result, FilterContext.PUBLIC);
onDataLoaded(result, !empty);
if(getActivity()==null) return;
boolean more=applyMaxID(result);
AccountSessionManager.get(accountID).filterStatuses(result, getFilterContext());
onDataLoaded(result, more);
bannerHelper.onBannerBecameVisible();
}
})

View File

@@ -83,10 +83,11 @@ public class ReportAddPostsChoiceFragment extends StatusListFragment{
@Override
protected void doLoadData(int offset, int count){
currentRequest=new GetAccountStatuses(reportAccount.id, offset>0 ? getMaxID() : null, null, count, GetAccountStatuses.Filter.OWN_POSTS_AND_REPLIES)
currentRequest=new GetAccountStatuses(reportAccount.id, getMaxID(), null, count, GetAccountStatuses.Filter.OWN_POSTS_AND_REPLIES)
.setCallback(new SimpleCallback<>(this){
@Override
public void onSuccess(List<Status> result){
if(getActivity()==null) return;
for(Status s:result){
s.sensitive=true;
}

View File

@@ -97,7 +97,7 @@ public class FilterWordsFragment extends BaseSettingsFragment<FilterKeyword> imp
public void onViewCreated(View view, Bundle savedInstanceState){
super.onViewCreated(view, savedInstanceState);
fab=view.findViewById(R.id.fab);
fab.setImageResource(R.drawable.ic_add_24px);
fab.setImageResource(R.drawable.ic_fluent_add_24_regular);
fab.setContentDescription(getString(R.string.add_muted_word));
fab.setOnClickListener(v->onFabClick());
}

View File

@@ -18,6 +18,7 @@ import org.joinmastodon.android.GlobalUserPreferences;
import org.joinmastodon.android.MastodonApp;
import org.joinmastodon.android.R;
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.AccountSessionManager;
import org.joinmastodon.android.events.StatusDisplaySettingsChangedEvent;
@@ -38,7 +39,7 @@ public class SettingsDisplayFragment extends BaseSettingsFragment<Void>{
private CheckableListItem<Void> revealCWsItem, hideSensitiveMediaItem, interactionCountsItem, emojiInNamesItem;
// MEGALODON
private CheckableListItem<Void> trueBlackModeItem, marqueeItem, disableSwipeItem, reduceMotionItem, altIndicatorItem, noAltIndicatorItem, collapsePostsItem, spectatorModeItem, hideFabItem, translateOpenedItem, disablePillItem, showNavigationLabelsItem;
private CheckableListItem<Void> trueBlackModeItem, marqueeItem, disableSwipeItem, reduceMotionItem, altIndicatorItem, noAltIndicatorItem, collapsePostsItem, spectatorModeItem, hideFabItem, translateOpenedItem, disablePillItem, showNavigationLabelsItem, likeIconItem, underlinedLinksItem;
private ListItem<Void> colorItem, publishTextItem, autoRevealCWsItem;
private CheckableListItem<Void> pronounsInUserListingsItem, pronounsInTimelinesItem, pronounsInThreadsItem;
@@ -76,6 +77,8 @@ 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)),
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)),
likeIconItem=new CheckableListItem<>(R.string.sk_settings_like_icon, 0, CheckableListItem.Style.SWITCH, lp.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)),
showPostDividersItem=new CheckableListItem<>(R.string.mo_enable_dividers, 0, CheckableListItem.Style.SWITCH, GlobalUserPreferences.showDividers, R.drawable.ic_fluent_timeline_24_regular, ()->toggleCheckableItem(showPostDividersItem)),
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),
@@ -106,6 +109,8 @@ public class SettingsDisplayFragment extends BaseSettingsFragment<Void>{
boolean restartPlease=
GlobalUserPreferences.disableM3PillActiveIndicator!=disablePillItem.checked ||
GlobalUserPreferences.showNavigationLabels!=showNavigationLabelsItem.checked ||
lp.likeIcon!=likeIconItem.checked;
GlobalUserPreferences.showNavigationLabels!=showNavigationLabelsItem.checked ||
GlobalUserPreferences.showMediaPreview !=showMediaPreviewItem.checked ||
GlobalUserPreferences.showDividers!=showPostDividersItem.checked;
@@ -113,6 +118,7 @@ public class SettingsDisplayFragment extends BaseSettingsFragment<Void>{
lp.hideSensitiveMedia=hideSensitiveMediaItem.checked;
lp.showInteractionCounts=interactionCountsItem.checked;
lp.customEmojiInNames=emojiInNamesItem.checked;
lp.likeIcon=likeIconItem.checked;
lp.save();
GlobalUserPreferences.toolbarMarquee=marqueeItem.checked;
GlobalUserPreferences.relocatePublishButton=relocatePublishButtonItem.checked;
@@ -126,6 +132,7 @@ public class SettingsDisplayFragment extends BaseSettingsFragment<Void>{
GlobalUserPreferences.spectatorMode=spectatorModeItem.checked;
GlobalUserPreferences.autoHideFab=hideFabItem.checked;
GlobalUserPreferences.translateButtonOpenedOnly=translateOpenedItem.checked;
GlobalUserPreferences.underlinedLinks=underlinedLinksItem.checked;
GlobalUserPreferences.showDividers=showPostDividersItem.checked;
GlobalUserPreferences.disableM3PillActiveIndicator=disablePillItem.checked;
GlobalUserPreferences.showNavigationLabels=showNavigationLabelsItem.checked;
@@ -147,7 +154,7 @@ public class SettingsDisplayFragment extends BaseSettingsFragment<Void>{
}
private @StringRes int getColorPaletteValue(){
return switch(GlobalUserPreferences.color){
return switch(AccountSessionManager.get(accountID).getLocalPreferences().color){
case MATERIAL3 -> R.string.sk_color_palette_material3;
case PINK -> R.string.sk_color_palette_pink;
case PURPLE -> R.string.sk_color_palette_purple;
@@ -214,18 +221,18 @@ public class SettingsDisplayFragment extends BaseSettingsFragment<Void>{
}
private void onColorClick(){
int selected=GlobalUserPreferences.color.ordinal();
int selected=lp.color.ordinal();
int[] newSelected={selected};
String[] names=Arrays.stream(GlobalUserPreferences.ColorPreference.values()).map(GlobalUserPreferences.ColorPreference::getName).map(this::getString).toArray(String[]::new);
String[] names=Arrays.stream(ColorPreference.values()).map(ColorPreference::getName).map(this::getString).toArray(String[]::new);
new M3AlertDialogBuilder(getActivity())
.setTitle(R.string.settings_theme)
.setTitle(R.string.sk_settings_color_palette)
.setSingleChoiceItems(names,
selected, (dlg, item)->newSelected[0]=item)
.setPositiveButton(R.string.ok, (dlg, item)->{
GlobalUserPreferences.ColorPreference pref=GlobalUserPreferences.ColorPreference.values()[newSelected[0]];
if(pref!=GlobalUserPreferences.color){
GlobalUserPreferences.ColorPreference prev=GlobalUserPreferences.color;
GlobalUserPreferences.color=pref;
ColorPreference pref=ColorPreference.values()[newSelected[0]];
if(pref!=lp.color){
ColorPreference prev=lp.color;
lp.color=pref;
GlobalUserPreferences.save();
colorItem.subtitleRes=getColorPaletteValue();
rebindItem(colorItem);
@@ -275,17 +282,17 @@ public class SettingsDisplayFragment extends BaseSettingsFragment<Void>{
.show();
}
private void maybeApplyNewThemeRightNow(GlobalUserPreferences.ThemePreference prevTheme, GlobalUserPreferences.ColorPreference prevColor, Boolean prevTrueBlack){
private void maybeApplyNewThemeRightNow(GlobalUserPreferences.ThemePreference prevTheme, ColorPreference prevColor, Boolean prevTrueBlack){
if(prevTheme==null) prevTheme=GlobalUserPreferences.theme;
if(prevTrueBlack==null) prevTrueBlack=GlobalUserPreferences.trueBlackTheme;
if(prevColor==null) prevColor=GlobalUserPreferences.color;
if(prevColor==null) prevColor=lp.color;
boolean isCurrentDark=prevTheme==GlobalUserPreferences.ThemePreference.DARK ||
(prevTheme==GlobalUserPreferences.ThemePreference.AUTO && Build.VERSION.SDK_INT>=30 && getResources().getConfiguration().isNightModeActive());
boolean isNewDark=GlobalUserPreferences.theme==GlobalUserPreferences.ThemePreference.DARK ||
(GlobalUserPreferences.theme==GlobalUserPreferences.ThemePreference.AUTO && Build.VERSION.SDK_INT>=30 && getResources().getConfiguration().isNightModeActive());
boolean isNewBlack=GlobalUserPreferences.trueBlackTheme;
if(isCurrentDark!=isNewDark || prevColor!=GlobalUserPreferences.color || (isNewDark && prevTrueBlack!=isNewBlack)){
if(isCurrentDark!=isNewDark || prevColor!=lp.color || (isNewDark && prevTrueBlack!=isNewBlack)){
restartActivityToApplyNewTheme();
}
}

View File

@@ -55,7 +55,7 @@ public class SettingsFiltersFragment extends BaseSettingsFragment<Filter>{
MergeRecyclerAdapter adapter=new MergeRecyclerAdapter();
adapter.addAdapter(super.getAdapter());
adapter.addAdapter(new GenericListItemsAdapter<>(Collections.singletonList(
new ListItem<Void>(R.string.settings_add_filter, 0, R.drawable.ic_add_24px, this::onAddFilterClick)
new ListItem<Void>(R.string.settings_add_filter, 0, R.drawable.ic_fluent_add_24_regular, this::onAddFilterClick)
)));
return adapter;
}

View File

@@ -64,7 +64,7 @@ public class SettingsMainFragment extends BaseSettingsFragment<Void>{
Instance instance = AccountSessionManager.getInstance().getInstanceInfo(account.domain);
if (!instance.isAkkoma())
data.add(2, new ListItem<>(R.string.settings_filters, 0, R.drawable.ic_fluent_filter_24_regular, this::onFiltersClick));
data.add(3, new ListItem<>(R.string.settings_filters, 0, R.drawable.ic_fluent_filter_24_regular, this::onFiltersClick));
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));
@@ -163,7 +163,7 @@ public class SettingsMainFragment extends BaseSettingsFragment<Void>{
.setMessage(getString(R.string.confirm_log_out, session.getFullUsername()))
.setPositiveButton(R.string.log_out, (dialog, which)->account.logOut(getActivity(), ()->{
loggedOut=true;
((MainActivity)getActivity()).restartHomeFragment();
((MainActivity)getActivity()).restartActivity();
}))
.setNegativeButton(R.string.cancel, null)
.show();

View File

@@ -18,8 +18,8 @@ public class SettingsPrivacyFragment extends BaseSettingsFragment<Void>{
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_thumbs_up_down_24px, ()->toggleCheckableItem(discoverableItem)),
indexableItem=new CheckableListItem<>(R.string.settings_indexable, 0, CheckableListItem.Style.SWITCH, self.source.indexable!=null ? self.source.indexable : true, R.drawable.ic_search_24px, ()->toggleCheckableItem(indexableItem))
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;

View File

@@ -68,6 +68,14 @@ public class Attachment extends BaseModel{
return 1080;
}
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(){
if(meta==null)
return 0;

View File

@@ -148,8 +148,7 @@ public class AccountSwitcherSheet extends BottomSheet{
private void logOut(String accountID){
AccountSessionManager.get(accountID).logOut(activity, ()->{
dismiss();
((MainActivity)activity).restartHomeFragment();
((MainActivity)activity).restartActivity();
});
}
@@ -318,14 +317,14 @@ public class AccountSwitcherSheet extends BottomSheet{
@Override
public void onClick(){
setOnDismissListener(null);
dismiss();
if (onClick != null) {
dismiss();
onClick.accept(item.getID(), false);
return;
}
if(AccountSessionManager.getInstance().tryGetAccount(item.getID())!=null){
AccountSessionManager.getInstance().setLastActiveAccountID(item.getID());
((MainActivity)activity).restartHomeFragment();
((MainActivity)activity).restartActivity();
}
}

File diff suppressed because one or more lines are too long

View File

@@ -170,12 +170,14 @@ public class EmojiReactionsStatusDisplayItem extends StatusDisplayItem {
@Override
public void onBind(EmojiReactionsStatusDisplayItem item) {
if(emojiKeyboard != null) root.removeView(emojiKeyboard.getView());
addButton.setSelected(false);
AccountSession session=item.parentFragment.getSession();
item.status.reactions.forEach(r->r.request=r.getUrl(item.playGifs)!=null
? new UrlImageLoaderRequest(r.getUrl(item.playGifs), V.sp(24), V.sp(24))
: null);
emojiKeyboard=new CustomEmojiPopupKeyboard(
(Activity) item.parentFragment.getContext(),
item.accountID,
AccountSessionManager.getInstance().getCustomEmojis(session.domain),
session.domain, true, item.accountID);
emojiKeyboard.setListener(this);

View File

@@ -14,6 +14,8 @@ import android.widget.ImageView;
import android.widget.TextView;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.session.AccountSession;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.fragments.BaseStatusListFragment;
import org.joinmastodon.android.fragments.StatusEditHistoryFragment;
@@ -75,6 +77,9 @@ public class ExtendedFooterStatusDisplayItem extends StatusDisplayItem{
@Override
public void onBind(ExtendedFooterStatusDisplayItem item){
Status s=item.status;
AccountSession session=AccountSessionManager.get(item.accountID);
boolean like=session!=null && session.getLocalPreferences().likeIcon;
favorites.setCompoundDrawablesRelativeWithIntrinsicBounds(like ? 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));
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);

View File

@@ -1,12 +1,10 @@
package org.joinmastodon.android.ui.displayitems;
import static org.joinmastodon.android.ui.utils.UiUtils.opacityIn;
import static org.joinmastodon.android.ui.utils.UiUtils.opacityOut;
import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
import android.content.Intent;
import android.content.res.ColorStateList;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
@@ -25,6 +23,7 @@ import android.view.animation.AnimationSet;
import android.view.animation.RotateAnimation;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
import org.joinmastodon.android.GlobalUserPreferences;
@@ -65,6 +64,7 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
public static class Holder extends StatusDisplayItem.Holder<FooterStatusDisplayItem>{
private final TextView replies, boosts, favorites;
private final View reply, boost, favorite, share, bookmark;
private final ImageView favIcon;
private static final Animation opacityOut, opacityIn;
private static AnimationSet animSet;
@@ -74,7 +74,7 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
private final Runnable longClickRunnable = () -> {
longClickPerformed = touchingView != null && touchingView.performLongClick();
if (longClickPerformed && touchingView != null) {
touchingView.startAnimation(opacityIn);
UiUtils.opacityIn(touchingView);
touchingView.animate().scaleX(1).scaleY(1).setInterpolator(CubicBezierInterpolator.DEFAULT).setDuration(150).start();
}
};
@@ -88,27 +88,6 @@ 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);
Animation spin = new RotateAnimation(0, 360,
Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
0.5f);
animSet = new AnimationSet(true);
animSet.setInterpolator(CubicBezierInterpolator.DEFAULT);
animSet.addAnimation(spin);
animSet.addAnimation(opacityIn);
animSet.setDuration(400);
}
public Holder(Activity activity, ViewGroup parent){
super(activity, R.layout.display_item_footer, parent);
@@ -121,6 +100,7 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
favorite=findViewById(R.id.favorite_btn);
share=findViewById(R.id.share_btn);
bookmark=findViewById(R.id.bookmark_btn);
favIcon=findViewById(R.id.favorite_icon);
reply.setOnTouchListener(this::onButtonTouch);
reply.setOnClickListener(this::onReplyClick);
@@ -164,6 +144,16 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
boolean condenseBottom = !item.isMainStatus && item.hasDescendantNeighbor &&
!nextIsWarning;
AccountSession session=AccountSessionManager.get(item.accountID);
boolean like=session!=null && session.getLocalPreferences().likeIcon;
ColorStateList color=item.parentFragment.getResources().getColorStateList(
like ? R.color.like_icon : R.color.favorite_icon, item.parentFragment.getContext().getTheme()
);
favIcon.setImageResource(like ? 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();
params.setMargins(params.leftMargin, params.topMargin, params.rightMargin,
condenseBottom ? V.dp(-5) : 0);
@@ -192,7 +182,7 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
if (!longClickPerformed) v.animate().scaleX(1).scaleY(1).setInterpolator(CubicBezierInterpolator.DEFAULT).setDuration(150).start();
if (disabled) return true;
if (action == MotionEvent.ACTION_UP && !longClickPerformed) v.performClick();
else if (!longClickPerformed) v.startAnimation(opacityIn);
else if (!longClickPerformed) UiUtils.opacityIn(v);
} else if (action == MotionEvent.ACTION_DOWN) {
longClickPerformed = false;
touchingView = v;
@@ -200,7 +190,7 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
v.animate().scaleX(0.85f).scaleY(0.85f).setInterpolator(CubicBezierInterpolator.DEFAULT).setDuration(75).start();
if (disabled) return true;
v.postDelayed(longClickRunnable, ViewConfiguration.getLongPressTimeout());
v.startAnimation(opacityOut);
UiUtils.opacityOut(v);
}
return true;
}
@@ -219,7 +209,7 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
);
return;
}
v.startAnimation(opacityIn);
UiUtils.opacityIn(v);
Bundle args=new Bundle();
args.putString("account", item.accountID);
args.putParcelable("replyTo", Parcels.wrap(item.status));
@@ -243,7 +233,7 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
private void onBoostClick(View v){
if (GlobalUserPreferences.confirmBoost) {
v.startAnimation(opacityIn);
UiUtils.opacityIn(v);
onBoostLongClick(v);
return;
}
@@ -266,7 +256,7 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
}
private void boostConsumer(View v, Status r) {
v.startAnimation(opacityIn);
UiUtils.opacityIn(v);
bindText(boosts, r.reblogsCount);
}
@@ -277,7 +267,7 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
AccountSession session = AccountSessionManager.getInstance().getAccount(item.accountID);
Consumer<StatusPrivacy> doReblog = (visibility) -> {
v.startAnimation(opacityOut);
UiUtils.opacityOut(v);
if(item.status.isRemote){
UiUtils.lookupStatus(v.getContext(),
item.status, item.accountID, null,
@@ -343,7 +333,7 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
menu.findViewById(R.id.quote).setOnClickListener(c->{
dialog.dismiss();
v.startAnimation(opacityIn);
UiUtils.opacityIn(v);
Bundle args=new Bundle();
args.putString("account", item.accountID);
AccountSession accountSession=AccountSessionManager.getInstance().getAccount(item.accountID);
@@ -407,7 +397,8 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
R.string.sk_favorite_as,
R.string.sk_favorited_as,
R.string.sk_already_favorited,
R.drawable.ic_fluent_star_28_regular
AccountSessionManager.get(item.accountID).getLocalPreferences().likeIcon ?
R.drawable.ic_fluent_heart_28_regular : R.drawable.ic_fluent_star_28_regular
);
return true;
}
@@ -431,7 +422,7 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
bookmark.setSelected(!item.status.bookmarked);
vibrateForAction(bookmark, !item.status.bookmarked);
AccountSessionManager.getInstance().getAccount(item.accountID).getStatusInteractionController().setBookmarked(item.status, !item.status.bookmarked, r->{
v.startAnimation(opacityIn);
UiUtils.opacityIn(v);
});
}
@@ -439,7 +430,7 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
if (AccountSessionManager.getInstance().getLoggedInAccounts().size() < 2) return false;
UiUtils.pickInteractAs(v.getContext(),
item.accountID, item.status,
s -> s.bookmarked,
s -> s.bookmarked,w
(ic, status, consumer) -> ic.setBookmarked(status, true, consumer),
R.string.sk_bookmark_as,
R.string.sk_bookmarked_as,
@@ -450,7 +441,7 @@ public class FooterStatusDisplayItem extends StatusDisplayItem{
}
private void onShareClick(View v){
v.startAnimation(opacityIn);
UiUtils.opacityIn(v);
Intent intent=new Intent(Intent.ACTION_SEND);
intent.setType("text/plain");
intent.putExtra(Intent.EXTRA_TEXT, item.status.url);

View File

@@ -72,7 +72,7 @@ public class GapStatusDisplayItem extends StatusDisplayItem{
private void onViewClick(View v){
if(item.loading) return;
boolean isTop=v==top;
(isTop ? textTop : textBottom).startAnimation(UiUtils.opacityOut);
UiUtils.opacityOut(isTop ? textTop : textBottom);
V.setVisibilityAnimated((isTop ? progressTop : progressBottom), View.VISIBLE);
item.parentFragment.onGapClick(this, isTop);
}

View File

@@ -68,6 +68,7 @@ import me.grishka.appkit.api.ErrorResponse;
import me.grishka.appkit.imageloader.ImageLoaderViewHolder;
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest;
import me.grishka.appkit.utils.CubicBezierInterpolator;
import me.grishka.appkit.utils.V;
public class HeaderStatusDisplayItem extends StatusDisplayItem{
@@ -78,7 +79,7 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
private CustomEmojiHelper emojiHelper=new CustomEmojiHelper();
private SpannableStringBuilder parsedName;
public final Status status;
private boolean hasVisibilityToggle;
public boolean hasVisibilityToggle;
boolean needBottomPadding;
private CharSequence extraText;
private Notification notification;
@@ -298,17 +299,6 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
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")
@Override
public void onBind(HeaderStatusDisplayItem item){
@@ -335,23 +325,13 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
botIcon.setColorFilter(username.getCurrentTextColor());
deleteNotification.setVisibility(GlobalUserPreferences.enableDeleteNotifications && item.notification!=null && !item.inset ? View.VISIBLE : View.GONE);
visibility.setVisibility(item.hasVisibilityToggle ? View.VISIBLE : View.GONE);
if (item.hasVisibilityToggle){
boolean hidden = !item.status.sensitiveRevealed || (item.status.hasSpoiler() && !item.status.spoilerRevealed);
// doing this because V.setVisibilityAnimated ignores changes between INVISIBLE and GONE
int newVis=hidden ? View.INVISIBLE : View.VISIBLE;
if(newVis==View.INVISIBLE && visibility.getVisibility()==View.GONE)
visibility.setVisibility(newVis);
else
V.setVisibilityAnimated(visibility, newVis);
visibility.setEnabled(!hidden);
visibility.setContentDescription(item.parentFragment.getString(item.status.sensitiveRevealed ? R.string.spoiler_hide : R.string.spoiler_show));
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.O){
visibility.setTooltipText(visibility.getContentDescription());
}
} else {
visibility.setVisibility(View.GONE);
boolean visible = item.status.sensitiveRevealed && (!item.status.hasSpoiler() || item.status.spoilerRevealed);
visibility.setAlpha(visible ? 1 : 0f);
visibility.setScaleY(visible ? 1 : 0.8f);
visibility.setScaleX(visible ? 1 : 0.8f);
visibility.setEnabled(visible);
}
itemView.setPadding(itemView.getPaddingLeft(), itemView.getPaddingTop(), itemView.getPaddingRight(), item.needBottomPadding ? V.dp(16) : 0);
if(TextUtils.isEmpty(item.extraText)){
@@ -416,6 +396,16 @@ public class HeaderStatusDisplayItem extends StatusDisplayItem{
item.inset ? V.dp(10) : V.dp(4), itemView.getPaddingBottom());
}
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
public void setImage(int index, Drawable drawable){
if(index>0){

View File

@@ -1,7 +1,6 @@
package org.joinmastodon.android.ui.displayitems;
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 android.annotation.SuppressLint;
@@ -9,7 +8,6 @@ import android.app.Activity;
import android.content.res.ColorStateList;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.text.Html;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.util.TypedValue;
@@ -20,9 +18,11 @@ import android.widget.TextView;
import org.joinmastodon.android.GlobalUserPreferences;
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.NotificationsListFragment;
import org.joinmastodon.android.fragments.ProfileFragment;
import org.joinmastodon.android.model.Emoji;
import org.joinmastodon.android.model.Notification;
@@ -46,11 +46,15 @@ public class NotificationHeaderStatusDisplayItem extends StatusDisplayItem{
private final String accountID;
private final CustomEmojiHelper emojiHelper=new CustomEmojiHelper();
private final CharSequence text;
private final CharSequence timestamp;
private final AccountLocalPreferences lp;
public NotificationHeaderStatusDisplayItem(String parentID, BaseStatusListFragment parentFragment, Notification notification, String accountID){
super(parentID, parentFragment);
this.notification=notification;
this.accountID=accountID;
this.timestamp=notification.createdAt==null ? null : UiUtils.formatRelativeTimestamp(context, notification.createdAt);
this.lp=AccountSessionManager.get(accountID).getLocalPreferences();
if(notification.type==Notification.Type.POLL){
text=parentFragment.getString(R.string.poll_ended);
@@ -111,17 +115,25 @@ public class NotificationHeaderStatusDisplayItem extends StatusDisplayItem{
}
public static class Holder extends StatusDisplayItem.Holder<NotificationHeaderStatusDisplayItem> implements ImageLoaderViewHolder{
private final ImageView icon, avatar;
private final TextView text;
private final ImageView icon, avatar, deleteNotification;
private final TextView text, timestamp;
private final int selectableItemBackground;
public Holder(Activity activity, ViewGroup parent){
super(activity, R.layout.display_item_notification_header, parent);
icon=findViewById(R.id.icon);
avatar=findViewById(R.id.avatar);
text=findViewById(R.id.text);
timestamp=findViewById(R.id.timestamp);
deleteNotification=findViewById(R.id.delete_notification);
avatar.setOutlineProvider(OutlineProviders.roundedRect(8));
avatar.setClipToOutline(true);
deleteNotification.setOnClickListener(v->UiUtils.confirmDeleteNotification(activity, item.parentFragment.getAccountID(), item.notification, ()->{
if (item.parentFragment instanceof NotificationsListFragment fragment) {
fragment.removeNotification(item.notification);
}
}));
icon.setOnClickListener(this::onItemClick);
avatar.setOnClickListener(this::onItemClick);
@@ -149,9 +161,10 @@ public class NotificationHeaderStatusDisplayItem extends StatusDisplayItem{
@Override
public void onBind(NotificationHeaderStatusDisplayItem item){
text.setText(item.text);
timestamp.setText(item.timestamp);
avatar.setVisibility(item.notification.type==Notification.Type.POLL ? View.GONE : View.VISIBLE);
icon.setImageResource(switch(item.notification.type){
case FAVORITE -> R.drawable.ic_fluent_star_24_filled;
case FAVORITE -> item.lp.likeIcon ? R.drawable.ic_fluent_heart_24_filled : R.drawable.ic_fluent_star_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 POLL -> R.drawable.ic_fluent_poll_24_filled;
@@ -162,11 +175,12 @@ public class NotificationHeaderStatusDisplayItem extends StatusDisplayItem{
default -> throw new IllegalStateException("Unexpected value: "+item.notification.type);
});
icon.setImageTintList(ColorStateList.valueOf(UiUtils.getThemeColor(item.parentFragment.getActivity(), switch(item.notification.type){
case FAVORITE -> R.attr.colorFavorite;
case FAVORITE -> item.lp.likeIcon ? R.attr.colorLike : R.attr.colorFavorite;
case REBLOG -> R.attr.colorBoost;
case POLL -> R.attr.colorPoll;
default -> android.R.attr.colorAccent;
})));
deleteNotification.setVisibility(GlobalUserPreferences.enableDeleteNotifications && item.notification != null ? View.VISIBLE : View.GONE);
itemView.setBackgroundResource(0);
}

View File

@@ -69,7 +69,7 @@ public class PollOptionStatusDisplayItem extends StatusDisplayItem{
private final TextView text, percent;
private final View button;
private final ImageView icon;
private final Drawable progressBg, progressBgInset;
private final Drawable progressBg;
public Holder(Activity activity, ViewGroup parent){
super(activity, R.layout.display_item_poll_option, parent);
@@ -78,7 +78,6 @@ public class PollOptionStatusDisplayItem extends StatusDisplayItem{
icon=findViewById(R.id.icon);
button=findViewById(R.id.button);
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);
button.setOutlineProvider(OutlineProviders.roundedRect(20));
button.setClipToOutline(true);
@@ -94,23 +93,18 @@ public class PollOptionStatusDisplayItem extends StatusDisplayItem{
item.showResults ? R.drawable.ic_poll_option_button : R.drawable.ic_fluent_radio_button_24_selector
));
if(item.showResults){
Drawable bg=item.inset ? progressBgInset : progressBg;
Drawable bg=progressBg;
bg.setLevel(Math.round(10000f*item.votesFraction));
button.setBackground(bg);
itemView.setSelected(item.poll.ownVotes!=null && item.poll.ownVotes.contains(item.optionIndex));
percent.setText(String.format(Locale.getDefault(), "%d%%", Math.round(item.votesFraction*100f)));
}else{
itemView.setSelected(item.poll.selectedOptions!=null && item.poll.selectedOptions.contains(item.option));
button.setBackgroundResource(item.inset ? R.drawable.bg_poll_option_clickable_inset : R.drawable.bg_poll_option_clickable);
button.setBackgroundResource(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));
}
}
@Override
public void setImage(int index, Drawable image){

View File

@@ -30,6 +30,7 @@ import org.joinmastodon.android.model.LegacyFilter;
import org.joinmastodon.android.model.FilterAction;
import org.joinmastodon.android.model.FilterContext;
import org.joinmastodon.android.model.FilterResult;
import org.joinmastodon.android.model.Notification;
import org.joinmastodon.android.model.Poll;
import org.joinmastodon.android.model.ScheduledStatus;
import org.joinmastodon.android.model.Status;
@@ -147,7 +148,7 @@ public abstract class StatusDisplayItem{
Status statusForContent=status.getContentStatus();
Bundle args=new Bundle();
args.putString("account", accountID);
ScheduledStatus scheduledStatus = parentObject instanceof ScheduledStatus ? (ScheduledStatus) parentObject : null;
ScheduledStatus scheduledStatus = parentObject instanceof ScheduledStatus s ? s : null;
HeaderStatusDisplayItem header=null;
boolean hideCounts=!AccountSessionManager.get(accountID).getLocalPreferences().showInteractionCounts;
@@ -206,14 +207,15 @@ public abstract class StatusDisplayItem{
if((flags & FLAG_CHECKABLE)!=0)
items.add(header=new CheckableHeaderStatusDisplayItem(parentID, statusForContent.account, statusForContent.createdAt, fragment, accountID, statusForContent, null));
else
items.add(header=new HeaderStatusDisplayItem(parentID, statusForContent.account, statusForContent.createdAt, fragment, accountID, statusForContent, null, null, scheduledStatus));
items.add(header=new HeaderStatusDisplayItem(parentID, statusForContent.account, statusForContent.createdAt, fragment, accountID, statusForContent, null, parentObject instanceof Notification n ? n : null, scheduledStatus));
}
boolean filtered=false;
LegacyFilter applyingFilter=null;
if(status.filtered!=null){
for(FilterResult filter:status.filtered){
if(filter.filter.isActive()){
filtered=true;
LegacyFilter f=filter.filter;
if(f.isActive() && filterContext != null && f.context.contains(filterContext)){
applyingFilter=f;
break;
}
}
@@ -276,14 +278,14 @@ public abstract class StatusDisplayItem{
contentItems.add(new AudioStatusDisplayItem(parentID, fragment, statusForContent, att));
}
if(att.type==Attachment.Type.UNKNOWN){
contentItems.add(new FileStatusDisplayItem(parentID, fragment, att, statusForContent));
contentItems.add(new FileStatusDisplayItem(parentID, fragment, att));
}
}
if(statusForContent.poll!=null){
buildPollItems(parentID, fragment, statusForContent.poll, contentItems, statusForContent);
buildPollItems(parentID, fragment, statusForContent.poll, contentItems);
}
if(statusForContent.card!=null && statusForContent.mediaAttachments.isEmpty()){
contentItems.add(new LinkCardStatusDisplayItem(parentID, fragment, statusForContent, (flags & FLAG_NO_MEDIA_PREVIEW)==0));
contentItems.add(new LinkCardStatusDisplayItem(parentID, fragment, statusForContent));
}
if(contentItems!=items && statusForContent.spoilerRevealed){
items.addAll(contentItems);
@@ -324,18 +326,11 @@ public abstract class StatusDisplayItem{
}
}
LegacyFilter applyingFilter = null;
if (!statusForContent.filterRevealed) {
StatusFilterPredicate predicate = new StatusFilterPredicate(accountID, filterContext, FilterAction.WARN);
statusForContent.filterRevealed = predicate.test(status);
applyingFilter = predicate.getApplyingFilter();
}
// Hide statuses that have a filter action of hide
if(!new StatusFilterPredicate(accountID, filterContext, FilterAction.HIDE).test(status))
return new ArrayList<StatusDisplayItem>() ;
return statusForContent.filterRevealed ? items :
return applyingFilter==null ? items :
new ArrayList<>(List.of(new WarningFilteredStatusDisplayItem(parentID, fragment, statusForContent, items, applyingFilter)));
}

View File

@@ -1,7 +1,5 @@
package org.joinmastodon.android.ui.displayitems;
import static org.joinmastodon.android.ui.utils.UiUtils.opacityIn;
import android.app.Activity;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.Drawable;
@@ -33,6 +31,7 @@ import me.grishka.appkit.api.ErrorResponse;
import me.grishka.appkit.imageloader.ImageLoaderViewHolder;
import me.grishka.appkit.imageloader.MovieDrawable;
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
import me.grishka.appkit.utils.CubicBezierInterpolator;
import me.grishka.appkit.utils.V;
public class TextStatusDisplayItem extends StatusDisplayItem{
@@ -116,7 +115,8 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
}else{
text.setText(item.text);
}
text.setTextIsSelectable(item.textSelectable);
text.setTextIsSelectable(false);
if(item.textSelectable) itemView.post(() -> text.setTextIsSelectable(true));
text.setInvalidateOnEveryFrame(false);
itemView.setClickable(false);
itemView.setPadding(itemView.getPaddingLeft(), item.reduceTopPadding ? V.dp(6) : V.dp(12), itemView.getPaddingRight(), itemView.getPaddingBottom());
@@ -204,17 +204,20 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
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 lang=existingTrans!=null ? existingTrans.detectedSourceLanguage : null;
String displayLang=Locale.forLanguageTag(lang!=null ? lang : (item.status.getContentStatus().language != null ? item.status.getContentStatus().language : AccountSessionManager.get(item.parentFragment.getAccountID()).preferences.postingDefaultLanguage)).getDisplayLanguage();
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.setEnabled(true);
translationButton.setAlpha(1);
translationButton.setClickable(true);
translationButton.animate().alpha(1).setDuration(100).start();
translationInfo.setVisibility(View.GONE);
UiUtils.beginLayoutTransition((ViewGroup) translationButtonWrap);
}else{
@@ -222,8 +225,8 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
if(item.status.translationState==Status.TranslationState.SHOWN){
translationProgress.setVisibility(View.GONE);
translationButton.setText(R.string.translation_show_original);
translationButton.setEnabled(true);
translationButton.setAlpha(1);
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();
@@ -237,8 +240,8 @@ public class TextStatusDisplayItem extends StatusDisplayItem{
}
}else{ // LOADING
translationProgress.setVisibility(View.VISIBLE);
translationButton.setEnabled(false);
translationButton.setAlpha(0.5f);
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());
}

View File

@@ -66,6 +66,10 @@ public class BlurhashCrossfadeDrawable extends Drawable{
public void setImageDrawable(Drawable imageDrawable){
this.imageDrawable=imageDrawable;
if(imageDrawable!=null){
width=imageDrawable.getIntrinsicWidth();
height=imageDrawable.getIntrinsicHeight();
}
invalidateSelf();
}
@@ -99,11 +103,15 @@ public class BlurhashCrossfadeDrawable extends Drawable{
@Override
public int getIntrinsicWidth(){
if(width==0)
return imageDrawable==null ? 1920 : imageDrawable.getIntrinsicWidth();
return width;
}
@Override
public int getIntrinsicHeight(){
if(height==0)
return imageDrawable==null ? 1080 : imageDrawable.getIntrinsicHeight();
return height;
}

View File

@@ -564,7 +564,7 @@ public class PhotoViewer implements ZoomPanView.Listener{
if(player==null || !player.isPlaying())
return;
player.pause();
videoPlayPauseButton.setImageResource(R.drawable.ic_play_24);
videoPlayPauseButton.setImageResource(R.drawable.ic_fluent_play_24_filled);
videoPlayPauseButton.setContentDescription(activity.getString(R.string.play));
stopUpdatingVideoPosition();
windowView.removeCallbacks(uiAutoHider);
@@ -575,7 +575,7 @@ public class PhotoViewer implements ZoomPanView.Listener{
if(player==null || player.isPlaying())
return;
player.start();
videoPlayPauseButton.setImageResource(R.drawable.ic_pause_24);
videoPlayPauseButton.setImageResource(R.drawable.ic_fluent_pause_24_filled);
videoPlayPauseButton.setContentDescription(activity.getString(R.string.pause));
startUpdatingVideoPosition(player);
}
@@ -734,9 +734,18 @@ public class PhotoViewer implements ZoomPanView.Listener{
public void onBind(Attachment item){
super.onBind(item);
FrameLayout.LayoutParams params=(FrameLayout.LayoutParams) imageView.getLayoutParams();
Drawable currentDrawable=listener.getPhotoViewCurrentDrawable(getAbsoluteAdapterPosition());
if(item.hasKnownDimensions()){
params.width=item.getWidth();
params.height=item.getHeight();
ViewImageLoader.load(this, listener.getPhotoViewCurrentDrawable(getAbsoluteAdapterPosition()), new UrlImageLoaderRequest(item.url), false);
}else if(currentDrawable!=null){
params.width=currentDrawable.getIntrinsicWidth();
params.height=currentDrawable.getIntrinsicHeight();
}else{
params.width=1920;
params.height=1080;
}
ViewImageLoader.load(this, currentDrawable, new UrlImageLoaderRequest(item.url), false);
}
@Override
@@ -778,9 +787,18 @@ public class PhotoViewer implements ZoomPanView.Listener{
super.onBind(item);
playerReady=false;
FrameLayout.LayoutParams params=(FrameLayout.LayoutParams) wrap.getLayoutParams();
Drawable currentDrawable=listener.getPhotoViewCurrentDrawable(getAbsoluteAdapterPosition());
if(item.hasKnownDimensions()){
params.width=item.getWidth();
params.height=item.getHeight();
wrap.setBackground(listener.getPhotoViewCurrentDrawable(getAbsoluteAdapterPosition()));
}else if(currentDrawable!=null){
params.width=currentDrawable.getIntrinsicWidth();
params.height=currentDrawable.getIntrinsicHeight();
}else{
params.width=1920;
params.height=1080;
}
wrap.setBackground(currentDrawable);
progressBar.setVisibility(item.type==Attachment.Type.VIDEO ? View.VISIBLE : View.GONE);
if(itemView.isAttachedToWindow()){
reset();
@@ -841,7 +859,9 @@ public class PhotoViewer implements ZoomPanView.Listener{
@Override
public boolean onError(MediaPlayer mp, int what, int extra){
Log.e(TAG, "video player onError() called with: mp = ["+mp+"], what = ["+what+"], extra = ["+extra+"]");
return false;
Toast.makeText(activity, R.string.error_playing_video, Toast.LENGTH_SHORT).show();
onStartSwipeToDismissTransition(0f);
return true;
}
public void prepareAndStartPlayer(){
@@ -862,6 +882,8 @@ public class PhotoViewer implements ZoomPanView.Listener{
player.prepareAsync();
}catch(IOException x){
Log.w(TAG, "Error initializing gif player", x);
Toast.makeText(activity, R.string.error_playing_video, Toast.LENGTH_SHORT).show();
onStartSwipeToDismissTransition(0f);
}
}
@@ -918,7 +940,7 @@ public class PhotoViewer implements ZoomPanView.Listener{
@Override
public void onCompletion(MediaPlayer mp){
videoPlayPauseButton.setImageResource(R.drawable.ic_play_24);
videoPlayPauseButton.setImageResource(R.drawable.ic_fluent_play_24_filled);
videoPlayPauseButton.setContentDescription(activity.getString(R.string.play));
stopUpdatingVideoPosition();
if(!uiVisible)

View File

@@ -119,8 +119,10 @@ public class ZoomPanView extends FrameLayout implements ScaleGestureDetector.OnS
int width=right-left;
int height=bottom-top;
if(width==0 || height==0)
if(width==0 || height==0 || child.getWidth()==0 || child.getWidth()==0){
matrix.reset();
return;
}
float scale=Math.min(width/(float)child.getWidth(), height/(float)child.getHeight());
minScale=scale;

View File

@@ -5,6 +5,7 @@ import android.text.TextPaint;
import android.text.style.CharacterStyle;
import android.view.View;
import org.joinmastodon.android.GlobalUserPreferences;
import org.joinmastodon.android.model.Hashtag;
import org.joinmastodon.android.ui.utils.UiUtils;
@@ -34,7 +35,7 @@ public class LinkSpan extends CharacterStyle {
@Override
public void updateDrawState(TextPaint tp) {
tp.setColor(color=tp.linkColor);
tp.setUnderlineText(true);
tp.setUnderlineText(GlobalUserPreferences.underlinedLinks);
}
public void onClick(Context context){
@@ -45,7 +46,7 @@ public class LinkSpan extends CharacterStyle {
if(linkObject instanceof Hashtag ht)
UiUtils.openHashtagTimeline(context, accountID, ht);
else
UiUtils.openHashtagTimeline(context, accountID, text);
UiUtils.openHashtagTimeline(context, accountID, link);
}
case CUSTOM -> listener.onLinkClick(this);
}

View File

@@ -1,8 +1,8 @@
package org.joinmastodon.android.ui.utils;
import static org.joinmastodon.android.GlobalUserPreferences.ColorPreference;
import static org.joinmastodon.android.GlobalUserPreferences.ThemePreference;
import static org.joinmastodon.android.GlobalUserPreferences.trueBlackTheme;
import static org.joinmastodon.android.api.session.AccountLocalPreferences.ColorPreference.*;
import android.content.Context;
import android.content.res.Resources;
@@ -11,22 +11,24 @@ import androidx.annotation.StyleRes;
import org.joinmastodon.android.GlobalUserPreferences;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.session.AccountLocalPreferences;
import java.util.Map;
public class ColorPalette {
public static final Map<GlobalUserPreferences.ColorPreference, ColorPalette> palettes = Map.of(
ColorPreference.MATERIAL3, new ColorPalette(R.style.ColorPalette_Material3)
public static final Map<AccountLocalPreferences.ColorPreference, ColorPalette> palettes = Map.of(
MATERIAL3, new ColorPalette(R.style.ColorPalette_Material3)
.dark(R.style.ColorPalette_Material3_Dark, R.style.ColorPalette_Material3_AutoLightDark),
ColorPreference.PINK, new ColorPalette(R.style.ColorPalette_Pink),
ColorPreference.PURPLE, new ColorPalette(R.style.ColorPalette_Purple),
ColorPreference.GREEN, new ColorPalette(R.style.ColorPalette_Green),
ColorPreference.BLUE, new ColorPalette(R.style.ColorPalette_Blue),
ColorPreference.BROWN, new ColorPalette(R.style.ColorPalette_Brown),
ColorPreference.RED, new ColorPalette(R.style.ColorPalette_Red),
ColorPreference.YELLOW, new ColorPalette(R.style.ColorPalette_Yellow),
ColorPreference.NORD, new ColorPalette(R.style.ColorPalette_Nord),
ColorPreference.WHITE, new ColorPalette(R.style.ColorPalette_White)
PINK, new ColorPalette(R.style.ColorPalette_Pink),
PURPLE, new ColorPalette(R.style.ColorPalette_Purple),
GREEN, new ColorPalette(R.style.ColorPalette_Green),
BLUE, new ColorPalette(R.style.ColorPalette_Blue),
BROWN, new ColorPalette(R.style.ColorPalette_Brown),
RED, new ColorPalette(R.style.ColorPalette_Red),
YELLOW, new ColorPalette(R.style.ColorPalette_Yellow)
NORD, new ColorPalette(R.style.ColorPalette_Nord),
WHITE, new ColorPalette(R.style.ColorPalette_White)
);
private @StyleRes int base;

View File

@@ -28,6 +28,7 @@ public class MediaAttachmentViewController{
private final Context context;
private boolean didClear;
private Status status;
private Attachment attachment;
public MediaAttachmentViewController(Context context, MediaGridStatusDisplayItem.GridItemType type){
view=context.getSystemService(LayoutInflater.class).inflate(switch(type){
@@ -54,6 +55,7 @@ public class MediaAttachmentViewController{
public void bind(Attachment attachment, Status status){
this.status=status;
this.attachment=attachment;
crossfadeDrawable.setSize(attachment.getWidth(), attachment.getHeight());
crossfadeDrawable.setBlurhashDrawable(attachment.blurhashPlaceholder);
crossfadeDrawable.setCrossfadeAlpha(0f);
@@ -76,6 +78,11 @@ public class MediaAttachmentViewController{
crossfadeDrawable.setImageDrawable(drawable);
if(didClear)
crossfadeDrawable.animateAlpha(0f);
// Make sure the image is not stretched if the server returned wrong dimensions
if(drawable!=null && (drawable.getIntrinsicWidth()!=attachment.getWidth() || drawable.getIntrinsicHeight()!=attachment.getHeight())){
photo.setImageDrawable(null);
photo.setImageDrawable(crossfadeDrawable);
}
}
public void clearImage(){

View File

@@ -1,6 +1,7 @@
package org.joinmastodon.android.ui.utils;
import static android.view.Menu.NONE;
import static org.joinmastodon.android.GlobalUserPreferences.ThemePreference.*;
import static org.joinmastodon.android.GlobalUserPreferences.theme;
import static org.joinmastodon.android.GlobalUserPreferences.trueBlackTheme;
@@ -53,9 +54,8 @@ import android.view.MenuItem;
import android.view.SubMenu;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewPropertyAnimator;
import android.view.WindowInsets;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.webkit.MimeTypeMap;
import android.widget.Button;
import android.widget.ImageView;
@@ -86,6 +86,7 @@ import org.joinmastodon.android.api.requests.statuses.DeleteStatus;
import org.joinmastodon.android.api.requests.statuses.GetStatusByID;
import org.joinmastodon.android.api.requests.statuses.SetStatusMuted;
import org.joinmastodon.android.api.requests.statuses.SetStatusPinned;
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.events.StatusMuteChangedEvent;
@@ -178,17 +179,6 @@ public class UiUtils {
public static int MAX_WIDTH, SCROLL_TO_TOP_DELTA;
public static final float ALPHA_PRESSED=0.55f;
public static final Animation opacityOut, opacityIn;
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);
}
private UiUtils() {
}
@@ -1032,14 +1022,20 @@ public class UiUtils {
}
public static void setUserPreferredTheme(Context context) {
context.setTheme(switch (theme) {
setUserPreferredTheme(context, null);
}
public static void setUserPreferredTheme(Context context, @Nullable AccountSession session) {
context.setTheme(switch(theme) {
case LIGHT -> R.style.Theme_Mastodon_Light;
case DARK -> R.style.Theme_Mastodon_Dark;
default -> R.style.Theme_Mastodon_AutoLightDark;
});
ColorPalette palette = ColorPalette.palettes.get(GlobalUserPreferences.color);
if (palette != null) palette.apply(context);
AccountLocalPreferences prefs=session != null ? session.getLocalPreferences() : null;
AccountLocalPreferences.ColorPreference color=prefs != null ? prefs.color : AccountLocalPreferences.ColorPreference.MATERIAL3;
ColorPalette palette = ColorPalette.palettes.get(color);
if (palette != null) palette.apply(context, theme);
Resources res = context.getResources();
MAX_WIDTH = (int) res.getDimension(R.dimen.layout_max_width);
@@ -1056,9 +1052,9 @@ public class UiUtils {
}
public static boolean isDarkTheme() {
if (theme == GlobalUserPreferences.ThemePreference.AUTO)
if (theme == AUTO)
return (MastodonApp.context.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
return theme == GlobalUserPreferences.ThemePreference.DARK;
return theme == DARK;
}
public static Optional<Pair<String, Optional<String>>> parseFediverseHandle(String maybeFediHandle) {
@@ -1801,4 +1797,16 @@ public class UiUtils {
.filter(Objects::nonNull)
.findFirst();
}
public static void opacityIn(View v){
v.animate().alpha(1).setDuration(400).setInterpolator(CubicBezierInterpolator.DEFAULT).start();
}
public static void opacityOut(View v){
opacityOut(v, ALPHA_PRESSED).start();
}
public static ViewPropertyAnimator opacityOut(View v, float alpha){
return v.animate().alpha(alpha).setDuration(300).setInterpolator(CubicBezierInterpolator.DEFAULT);
}
}

View File

@@ -18,6 +18,7 @@ import android.widget.TextView;
import org.joinmastodon.android.GlobalUserPreferences;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.MastodonAPIController;
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.ui.DividerItemDecoration;
@@ -66,7 +67,7 @@ public class ComposeLanguageAlertViewController{
.collect(Collectors.toList());
if(!TextUtils.isEmpty(preferred)){
MastodonLanguage lang = resolver.fromOrFallback(preferred);
MastodonLanguage lang=resolver.fromOrFallback(preferred);
specialLocales.add(new SpecialLocaleInfo(
lang,
lang.getDisplayName(context),
@@ -75,7 +76,7 @@ public class ComposeLanguageAlertViewController{
}
if(!Locale.getDefault().getLanguage().equals(preferred)){
MastodonLanguage lang = resolver.getDefault();
MastodonLanguage lang=resolver.getDefault();
specialLocales.add(new SpecialLocaleInfo(
lang,
lang.getDisplayName(context),
@@ -91,9 +92,19 @@ public class ComposeLanguageAlertViewController{
detectLanguage(detected, postText);
}
if (session!=null && session.getLocalPreferences().bottomEncoding) {
AccountLocalPreferences lp=session==null ? null : session.getLocalPreferences();
if(lp!=null){
for(String tag : lp.recentLanguages){
if(specialLocales.stream().anyMatch(l->l.language!=null && l.language.languageTag!=null
&& l.language.languageTag.equals(tag))) continue;
resolver.from(tag).ifPresent(lang->specialLocales.add(new SpecialLocaleInfo(
lang, lang.getDisplayName(context), null
)));
}
if(lp.bottomEncoding) {
specialLocales.add(new SpecialLocaleInfo(null, "\uD83E\uDD7A\uD83D\uDC49\uD83D\uDC48", "bottom"));
}
}
if(previouslySelected!=null){
if(previouslySelected.index!=-1 && ((previouslySelected.index<specialLocales.size() && Objects.equals(previouslySelected.language, specialLocales.get(previouslySelected.index).language)) ||
@@ -323,7 +334,7 @@ public class ComposeLanguageAlertViewController{
public void onClick(){
selectItem(getAbsoluteAdapterPosition());
selectedLocale=item.language;
selectedEncoding = item.title.equals("bottom") ? "bottom" : null;
selectedEncoding=item.title != null && item.title.equals("bottom") ? "bottom" : null;
}
@Override

View File

@@ -1,9 +1,11 @@
package org.joinmastodon.android.ui.views;
import android.content.Context;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.util.function.IntConsumer;
import java.util.function.IntPredicate;
@@ -45,9 +47,7 @@ public class TabBar extends LinearLayout{
listener.accept(v.getId());
if(v.getId()==selectedTabID)
return;
findViewById(selectedTabID).setSelected(false);
v.setSelected(true);
selectedTabID=v.getId();
selectTab(v.getId());
}
private boolean onChildLongClick(View v){
@@ -60,8 +60,17 @@ public class TabBar extends LinearLayout{
}
public void selectTab(int id){
findViewById(selectedTabID).setSelected(false);
toggleSelected(selectedTabID, false);
selectedTabID=id;
findViewById(selectedTabID).setSelected(true);
toggleSelected(id, true);
}
private void toggleSelected(int selectedTabID, boolean selected){
LinearLayout tab=findViewById(selectedTabID);
tab.setSelected(selected);
View v=tab.findViewWithTag("label");
if(v instanceof TextView text){
text.setTypeface(Typeface.create(text.getTypeface(), selected ? Typeface.BOLD : Typeface.NORMAL));
}
}
}

File diff suppressed because one or more lines are too long

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="?attr/colorBookmark" android:state_selected="true"/>
<item android:color="?android:textColorSecondary"/>
<item android:color="?colorM3OnSurfaceVariant"/>
</selector>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="?colorM3Primary" android:state_selected="true"/>
<item android:color="?android:textColorSecondary" android:state_enabled="true"/>
<item android:color="@color/m3_on_surface_alpha38"/>
<item android:color="?colorM3OnSurfaceVariant" android:state_enabled="true"/>
<item android:color="@color/m3_on_background_alpha38" />
</selector>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="?colorFavorite" android:state_selected="true"/>
<item android:color="?android:textColorSecondary"/>
<item android:color="?colorM3OnSurfaceVariant"/>
</selector>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/like_selected" android:state_selected="true"/>
<item android:color="?android:textColorSecondary"/>
</selector>

View File

@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="?colorM3TertiaryContainer" android:state_selected="true"/>
<item android:color="?colorM3SurfaceVariant"/>
</selector>

View File

@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="?colorM3OnTertiaryContainer" android:state_selected="true"/>
<item android:color="?colorM3OnSurface"/>
</selector>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<color android:color="?colorM3SurfaceVariant"/>
</item>
<item android:drawable="?android:selectableItemBackground"/>
</layer-list>

View File

@@ -1,16 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="?android:colorControlHighlight">
<item>
<shape>
<stroke android:width="1dp" android:color="?colorM3Outline"/>
<solid android:color="?colorM3Surface"/>
<corners android:radius="20dp"/>
</shape>
</item>
<item android:id="@android:id/mask">
<shape>
<solid android:color="#000"/>
<corners android:radius="20dp"/>
</shape>
</item>
</ripple>

View File

@@ -1,23 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape>
<solid android:color="?colorM3Surface"/>
<corners android:radius="20dp"/>
</shape>
</item>
<item>
<scale android:scaleGravity="start|fill_vertical" android:scaleWidth="100%">
<shape>
<solid android:color="@color/poll_option_progress_inset"/>
<corners android:radius="20dp"/>
</shape>
</scale>
</item>
<item>
<shape>
<stroke android:width="1dp" android:color="?colorM3Outline"/>
<corners android:radius="20dp"/>
</shape>
</item>
</layer-list>

View File

@@ -1,5 +0,0 @@
<vector android:height="24dp" android:tint="?colorM3OnPrimary"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
</vector>

View File

@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M11,19V13H5V11H11V5H13V11H19V13H13V19Z"/>
</vector>

View File

@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M14,3V5H5Q5,5 5,5Q5,5 5,5V19Q5,19 5,19Q5,19 5,19H19Q19,19 19,19Q19,19 19,19V10H21V19Q21,19.825 20.413,20.413Q19.825,21 19,21H5Q4.175,21 3.587,20.413Q3,19.825 3,19V5Q3,4.175 3.587,3.587Q4.175,3 5,3ZM19,3V5H21V7H19V9H17V7H15V5H17V3ZM6,17H18L14.25,12L11.25,16L9,13ZM5,5V8V10V19Q5,19 5,19Q5,19 5,19Q5,19 5,19Q5,19 5,19V5Q5,5 5,5Q5,5 5,5Z"/>
</vector>

View File

@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="48dp"
android:height="48dp"
android:viewportWidth="48"
android:viewportHeight="48">
<path
android:fillColor="@android:color/white"
android:pathData="M29.45,6V9H9Q9,9 9,9Q9,9 9,9V39Q9,39 9,39Q9,39 9,39H39Q39,39 39,39Q39,39 39,39V18.6H42V39Q42,40.2 41.1,41.1Q40.2,42 39,42H9Q7.8,42 6.9,41.1Q6,40.2 6,39V9Q6,7.8 6.9,6.9Q7.8,6 9,6ZM38,6V10.05H42.05V13.05H38V17.1H35V13.05H30.95V10.05H35V6ZM12,33.9H36L28.8,24.3L22.45,32.65L17.75,26.45ZM9,9V14.55V18.6V39Q9,39 9,39Q9,39 9,39Q9,39 9,39Q9,39 9,39V9Q9,9 9,9Q9,9 9,9Z"/>
</vector>

View File

@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="48dp"
android:height="48dp"
android:viewportWidth="48"
android:viewportHeight="48">
<path
android:fillColor="@color/m3_sys_dark_on_surface"
android:pathData="M29.45,6V9H9Q9,9 9,9Q9,9 9,9V39Q9,39 9,39Q9,39 9,39H39Q39,39 39,39Q39,39 39,39V18.6H42V39Q42,40.2 41.1,41.1Q40.2,42 39,42H9Q7.8,42 6.9,41.1Q6,40.2 6,39V9Q6,7.8 6.9,6.9Q7.8,6 9,6ZM38,6V10.05H42.05V13.05H38V17.1H35V13.05H30.95V10.05H35V6ZM12,33.9H36L28.8,24.3L22.45,32.65L17.75,26.45ZM9,9V14.55V18.6V39Q9,39 9,39Q9,39 9,39Q9,39 9,39Q9,39 9,39V9Q9,9 9,9Q9,9 9,9Z"/>
</vector>

View File

@@ -1,16 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M3,16V10.167C3,9.836 3.11,9.564 3.33,9.35C3.57,9.117 3.86,9 4.2,9H7.8C8.14,9 8.42,9.117 8.64,9.35C8.88,9.564 9,9.836 9,10.167V16H7.2V14.25H4.8V16H3ZM4.8,12.5H7.2V10.75H4.8V12.5Z"
android:fillColor="#49454F"
android:fillType="evenOdd"/>
<path
android:pathData="M17.1,10.75V16H18.9V10.75H21V9H15V10.75H17.1Z"
android:fillColor="#49454F"/>
<path
android:pathData="M10.2,9V16H15V14.25H12V9H10.2Z"
android:fillColor="#49454F"/>
</vector>

View File

@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="20dp"
android:height="20dp"
android:viewportWidth="20"
android:viewportHeight="20">
<path
android:fillColor="@android:color/white"
android:pathData="M10,18.333Q8.271,18.333 6.75,17.677Q5.229,17.021 4.104,15.896Q2.979,14.771 2.323,13.25Q1.667,11.729 1.667,10Q1.667,8.271 2.323,6.75Q2.979,5.229 4.104,4.104Q5.229,2.979 6.75,2.323Q8.271,1.667 10,1.667Q11.729,1.667 13.25,2.323Q14.771,2.979 15.896,4.104Q17.021,5.229 17.677,6.75Q18.333,8.271 18.333,10V11.271Q18.333,12.5 17.49,13.354Q16.646,14.208 15.417,14.208Q14.688,14.208 14.042,13.906Q13.396,13.604 12.958,13.042Q12.375,13.625 11.604,13.927Q10.833,14.229 10,14.229Q8.229,14.229 7,13Q5.771,11.771 5.771,10Q5.771,8.229 7,7Q8.229,5.771 10,5.771Q11.771,5.771 13,7Q14.229,8.229 14.229,10V11.271Q14.229,11.75 14.583,12.104Q14.938,12.458 15.417,12.458Q15.896,12.458 16.24,12.104Q16.583,11.75 16.583,11.271V10Q16.583,7.25 14.667,5.333Q12.75,3.417 10,3.417Q7.25,3.417 5.333,5.333Q3.417,7.25 3.417,10Q3.417,12.75 5.333,14.667Q7.25,16.583 10,16.583H14.125V18.333ZM10,12.479Q11.042,12.479 11.76,11.76Q12.479,11.042 12.479,10Q12.479,8.958 11.76,8.24Q11.042,7.521 10,7.521Q8.958,7.521 8.24,8.24Q7.521,8.958 7.521,10Q7.521,11.042 8.24,11.76Q8.958,12.479 10,12.479Z"/>
</vector>

View File

@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M9,22Q7.55,22 6.275,21.45Q5,20.9 4.05,19.95Q3.1,19 2.55,17.725Q2,16.45 2,15Q2,12.975 3.05,11.3Q4.1,9.625 5.8,8.75Q6.3,7.775 7.038,7.037Q7.775,6.3 8.75,5.8Q9.575,4.1 11.275,3.05Q12.975,2 15,2Q16.45,2 17.725,2.55Q19,3.1 19.95,4.05Q20.9,5 21.45,6.275Q22,7.55 22,9Q22,11.125 20.95,12.75Q19.9,14.375 18.2,15.25Q17.7,16.225 16.962,16.962Q16.225,17.7 15.25,18.2Q14.375,19.9 12.7,20.95Q11.025,22 9,22ZM9,20Q9.825,20 10.588,19.75Q11.35,19.5 12,19Q10.55,19 9.275,18.45Q8,17.9 7.05,16.95Q6.1,16 5.55,14.725Q5,13.45 5,12Q4.5,12.65 4.25,13.412Q4,14.175 4,15Q4,16.05 4.4,16.95Q4.8,17.85 5.475,18.525Q6.15,19.2 7.05,19.6Q7.95,20 9,20ZM12,17Q12.825,17 13.613,16.75Q14.4,16.5 15.05,16Q13.575,16 12.3,15.438Q11.025,14.875 10.075,13.925Q9.125,12.975 8.562,11.7Q8,10.425 8,8.95Q7.5,9.6 7.25,10.387Q7,11.175 7,12Q7,13.05 7.388,13.95Q7.775,14.85 8.475,15.525Q9.15,16.225 10.05,16.613Q10.95,17 12,17ZM15,14Q15.45,14 15.863,13.925Q16.275,13.85 16.7,13.7Q17.25,12.2 16.863,10.812Q16.475,9.425 15.525,8.475Q14.575,7.525 13.188,7.137Q11.8,6.75 10.3,7.3Q10.15,7.725 10.075,8.137Q10,8.55 10,9Q10,10.05 10.387,10.95Q10.775,11.85 11.475,12.525Q12.15,13.225 13.05,13.613Q13.95,14 15,14ZM19,12.05Q19.5,11.4 19.75,10.612Q20,9.825 20,9Q20,7.95 19.613,7.05Q19.225,6.15 18.525,5.475Q17.85,4.775 16.95,4.387Q16.05,4 15,4Q14.125,4 13.363,4.25Q12.6,4.5 11.95,5Q13.425,5 14.7,5.562Q15.975,6.125 16.925,7.075Q17.875,8.025 18.438,9.3Q19,10.575 19,12.05Z"/>
</vector>

View File

@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M4,22Q3.175,22 2.588,21.413Q2,20.825 2,20V4Q2,3.175 2.588,2.587Q3.175,2 4,2H12L18,8V12.25H16V9H11V4H4Q4,4 4,4Q4,4 4,4V20Q4,20 4,20Q4,20 4,20H15V22ZM4,20V12.25V9V4Q4,4 4,4Q4,4 4,4V20Q4,20 4,20Q4,20 4,20ZM5,19Q5.1,17.775 5.75,16.75Q6.4,15.725 7.45,15.125L6.5,13.425Q6.5,13.4 6.6,13.05Q6.725,13 6.838,13Q6.95,13 7,13.125L7.975,14.875Q8.475,14.675 8.975,14.562Q9.475,14.45 10,14.45Q10.525,14.45 11.025,14.562Q11.525,14.675 12.025,14.875L13,13.125Q13,13.125 13.375,13.025Q13.5,13.075 13.525,13.2Q13.55,13.325 13.5,13.425L12.55,15.125Q13.6,15.725 14.25,16.75Q14.9,17.775 15,19ZM7.75,17.5Q7.95,17.5 8.1,17.35Q8.25,17.2 8.25,17Q8.25,16.8 8.1,16.65Q7.95,16.5 7.75,16.5Q7.55,16.5 7.4,16.65Q7.25,16.8 7.25,17Q7.25,17.2 7.4,17.35Q7.55,17.5 7.75,17.5ZM12.25,17.5Q12.45,17.5 12.6,17.35Q12.75,17.2 12.75,17Q12.75,16.8 12.6,16.65Q12.45,16.5 12.25,16.5Q12.05,16.5 11.9,16.65Q11.75,16.8 11.75,17Q11.75,17.2 11.9,17.35Q12.05,17.5 12.25,17.5ZM20,22 L16,18 17.4,16.575 19,18.15V14H21V18.15L22.6,16.575L24,18Z"/>
</vector>

View File

@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="@android:color/white"
android:pathData="M480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480ZM80,480Q80,397 111.5,324Q143,251 197,197Q251,143 324,111.5Q397,80 480,80Q507,80 533,83.5Q559,87 584,94Q568,109 555.5,127Q543,145 535,165Q521,163 507.5,161.5Q494,160 480,160Q346,160 253,253Q160,346 160,480Q160,614 253,707Q346,800 480,800Q614,800 707,707Q800,614 800,480Q800,466 798.5,452.5Q797,439 795,425Q815,417 833,404.5Q851,392 866,376Q873,401 876.5,427Q880,453 880,480Q880,563 848.5,636Q817,709 763,763Q709,817 636,848.5Q563,880 480,880Q397,880 324,848.5Q251,817 197,763Q143,709 111.5,636Q80,563 80,480ZM720,360Q670,360 635,325Q600,290 600,240Q600,190 635,155Q670,120 720,120Q770,120 805,155Q840,190 840,240Q840,290 805,325Q770,360 720,360Z"/>
</vector>

View File

@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M11.4,16 L14,13.4 16.6,16 18,14.6 15.4,12 18,9.4 16.6,8 14,10.6 11.4,8 10,9.4 12.6,12 10,14.6ZM3,12 L7.35,5.85Q7.625,5.45 8.062,5.225Q8.5,5 9,5H19Q19.825,5 20.413,5.588Q21,6.175 21,7V17Q21,17.825 20.413,18.413Q19.825,19 19,19H9Q8.5,19 8.062,18.775Q7.625,18.55 7.35,18.15ZM5.45,12 L9,17Q9,17 9,17Q9,17 9,17H19Q19,17 19,17Q19,17 19,17V7Q19,7 19,7Q19,7 19,7H9Q9,7 9,7Q9,7 9,7ZM19,12V7Q19,7 19,7Q19,7 19,7Q19,7 19,7Q19,7 19,7V17Q19,17 19,17Q19,17 19,17Q19,17 19,17Q19,17 19,17Z"/>
</vector>

View File

@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="20dp"
android:height="20dp"
android:viewportWidth="20"
android:viewportHeight="20">
<path
android:fillColor="@android:color/white"
android:pathData="M10,18Q8.354,18 6.896,17.375Q5.438,16.75 4.344,15.656Q3.25,14.562 2.625,13.104Q2,11.646 2,10Q2,8.333 2.625,6.885Q3.25,5.438 4.344,4.344Q5.438,3.25 6.896,2.625Q8.354,2 10,2Q11.667,2 13.115,2.625Q14.562,3.25 15.656,4.344Q16.75,5.438 17.375,6.885Q18,8.333 18,10Q18,11.646 17.375,13.104Q16.75,14.562 15.656,15.656Q14.562,16.75 13.115,17.375Q11.667,18 10,18ZM10,16.5Q12.708,16.5 14.604,14.604Q16.5,12.708 16.5,10Q16.5,8.854 16.125,7.833Q15.75,6.812 15.083,5.979L5.979,15.083Q6.812,15.75 7.833,16.125Q8.854,16.5 10,16.5ZM4.917,14.021 L14.021,4.917Q13.188,4.25 12.167,3.875Q11.146,3.5 10,3.5Q7.292,3.5 5.396,5.396Q3.5,7.292 3.5,10Q3.5,11.146 3.875,12.167Q4.25,13.188 4.917,14.021Z"/>
</vector>

View File

@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M7,17.95 L12,15.8 17,17.95V5Q17,5 17,5Q17,5 17,5H7Q7,5 7,5Q7,5 7,5ZM5,21V5Q5,4.175 5.588,3.587Q6.175,3 7,3H17Q17.825,3 18.413,3.587Q19,4.175 19,5V21L12,18ZM17,5H12H7Q7,5 7,5Q7,5 7,5H17Q17,5 17,5Q17,5 17,5Z"/>
</vector>

View File

@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="20dp"
android:height="20dp"
android:viewportWidth="20"
android:viewportHeight="20">
<path
android:fillColor="@android:color/white"
android:pathData="M8.229,14.062 L4.708,10.521 5.75,9.479 8.229,11.938 14.25,5.938 15.292,7Z"/>
</vector>

View File

@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M9.55,18 L3.85,12.3 5.275,10.875 9.55,15.15 18.725,5.975 20.15,7.4Z"/>
</vector>

View File

@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="16dp"
android:height="16dp"
android:viewportWidth="16"
android:viewportHeight="16">
<path
android:fillColor="@android:color/white"
android:pathData="M6.333,11.729 L3,8.396 4.062,7.333 6.333,9.604 11.938,4 13,5.062Z"/>
</vector>

View File

@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true" android:drawable="@drawable/ic_warning_fill1_24px"/>
<item android:drawable="@drawable/ic_warning_24px"/>
</selector>

View File

@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true" android:drawable="@drawable/ic_mood_fill1_24px"/>
<item android:drawable="@drawable/ic_mood_24px"/>
</selector>

View File

@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true" android:drawable="@drawable/ic_insert_chart_fill1_24px"/>
<item android:drawable="@drawable/ic_insert_chart_24px"/>
</selector>

View File

@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M12,21Q8.25,21 5.625,18.375Q3,15.75 3,12Q3,8.25 5.625,5.625Q8.25,3 12,3Q12.35,3 12.688,3.025Q13.025,3.05 13.35,3.1Q12.325,3.825 11.713,4.987Q11.1,6.15 11.1,7.5Q11.1,9.75 12.675,11.325Q14.25,12.9 16.5,12.9Q17.875,12.9 19.025,12.287Q20.175,11.675 20.9,10.65Q20.95,10.975 20.975,11.312Q21,11.65 21,12Q21,15.75 18.375,18.375Q15.75,21 12,21ZM12,19Q14.2,19 15.95,17.788Q17.7,16.575 18.5,14.625Q18,14.75 17.5,14.825Q17,14.9 16.5,14.9Q13.425,14.9 11.262,12.738Q9.1,10.575 9.1,7.5Q9.1,7 9.175,6.5Q9.25,6 9.375,5.5Q7.425,6.3 6.213,8.05Q5,9.8 5,12Q5,14.9 7.05,16.95Q9.1,19 12,19ZM11.75,12.25Q11.75,12.25 11.75,12.25Q11.75,12.25 11.75,12.25Q11.75,12.25 11.75,12.25Q11.75,12.25 11.75,12.25Q11.75,12.25 11.75,12.25Q11.75,12.25 11.75,12.25Q11.75,12.25 11.75,12.25Q11.75,12.25 11.75,12.25Q11.75,12.25 11.75,12.25Q11.75,12.25 11.75,12.25Q11.75,12.25 11.75,12.25Q11.75,12.25 11.75,12.25Z"/>
</vector>

Some files were not shown because too many files have changed in this diff Show More