From 94c09d46c29589b18405b6602c13a39bb99fd30a Mon Sep 17 00:00:00 2001 From: sk Date: Thu, 25 May 2023 14:53:26 +0200 Subject: [PATCH 01/27] fix contentType being a required field re: sk22#516 --- .../android/api/requests/statuses/GetStatusSourceText.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mastodon/src/main/java/org/joinmastodon/android/api/requests/statuses/GetStatusSourceText.java b/mastodon/src/main/java/org/joinmastodon/android/api/requests/statuses/GetStatusSourceText.java index a84479075..1a1df5a5f 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/api/requests/statuses/GetStatusSourceText.java +++ b/mastodon/src/main/java/org/joinmastodon/android/api/requests/statuses/GetStatusSourceText.java @@ -2,6 +2,7 @@ package org.joinmastodon.android.api.requests.statuses; import org.joinmastodon.android.api.AllFieldsAreRequired; import org.joinmastodon.android.api.MastodonAPIRequest; +import org.joinmastodon.android.api.RequiredField; import org.joinmastodon.android.model.BaseModel; import org.joinmastodon.android.model.ContentType; @@ -10,10 +11,12 @@ public class GetStatusSourceText extends MastodonAPIRequest Date: Thu, 25 May 2023 15:11:40 +0200 Subject: [PATCH 02/27] increase read timeout re: sk22#392 --- .../org/joinmastodon/android/api/MastodonAPIController.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mastodon/src/main/java/org/joinmastodon/android/api/MastodonAPIController.java b/mastodon/src/main/java/org/joinmastodon/android/api/MastodonAPIController.java index 560f81fbf..32b7cad8d 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/api/MastodonAPIController.java +++ b/mastodon/src/main/java/org/joinmastodon/android/api/MastodonAPIController.java @@ -28,6 +28,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.TimeUnit; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -51,7 +52,9 @@ public class MastodonAPIController{ .registerTypeAdapter(Status.class, new Status.StatusDeserializer()) .create(); private static WorkerThread thread=new WorkerThread("MastodonAPIController"); - private static OkHttpClient httpClient=new OkHttpClient.Builder().build(); + private static OkHttpClient httpClient=new OkHttpClient.Builder() + .readTimeout(5, TimeUnit.MINUTES) + .build(); private AccountSession session; private static List badDomains = new ArrayList<>(); From 85c3d9f65f9a46b1285f4601c7677c82973932ef Mon Sep 17 00:00:00 2001 From: sk Date: Thu, 25 May 2023 19:51:45 +0200 Subject: [PATCH 03/27] fix wrong tab being selected on restore --- .../java/org/joinmastodon/android/fragments/HomeFragment.java | 1 + 1 file changed, 1 insertion(+) diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/HomeFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/HomeFragment.java index 5b8072c0a..c2a357f50 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/HomeFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/HomeFragment.java @@ -145,6 +145,7 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene notificationsFragment=(NotificationsFragment) getChildFragmentManager().getFragment(savedInstanceState, "notificationsFragment"); profileFragment=(ProfileFragment) getChildFragmentManager().getFragment(savedInstanceState, "profileFragment"); currentTab=savedInstanceState.getInt("selectedTab"); + tabBar.selectTab(currentTab); Fragment current=fragmentForTab(currentTab); getChildFragmentManager().beginTransaction() .hide(homeTimelineFragment) From b1e0dc58435921c6045f3a5a739f14cf10638823 Mon Sep 17 00:00:00 2001 From: sk Date: Thu, 25 May 2023 20:26:59 +0200 Subject: [PATCH 04/27] show compose button when switching tab closes sk22#506 --- .../fragments/BaseStatusListFragment.java | 55 ++++++++++--------- .../android/fragments/HasFab.java | 2 + .../android/fragments/HomeFragment.java | 1 + .../android/fragments/HomeTabFragment.java | 12 +++- .../android/fragments/ProfileFragment.java | 10 ++++ 5 files changed, 54 insertions(+), 26 deletions(-) diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/BaseStatusListFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/BaseStatusListFragment.java index 935fa0b39..29e39e38d 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/BaseStatusListFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/BaseStatusListFragment.java @@ -68,7 +68,7 @@ import me.grishka.appkit.utils.BindableViewHolder; import me.grishka.appkit.utils.V; import me.grishka.appkit.views.UsableRecyclerView; -public abstract class BaseStatusListFragment extends RecyclerFragment implements PhotoViewerHost, ScrollableToTop{ +public abstract class BaseStatusListFragment extends RecyclerFragment implements PhotoViewerHost, ScrollableToTop, HasFab{ protected ArrayList displayItems=new ArrayList<>(); protected DisplayItemsAdapter adapter; protected String accountID; @@ -268,34 +268,39 @@ public abstract class BaseStatusListFragment exten }); } + @Override public @Nullable View getFab() { if (getParentFragment() instanceof HasFab l) return l.getFab(); else return fab; } - public void animateFab(boolean show) { + @Override + public void showFab() { View fab = getFab(); - if (fab == null) return; - if (show && fab.getVisibility() != View.VISIBLE) { - fab.setVisibility(View.VISIBLE); - TranslateAnimation animate = new TranslateAnimation( - 0, - 0, - fab.getHeight() * 2, - 0); - animate.setDuration(300); - fab.startAnimation(animate); - } else if (!show && fab.getVisibility() == View.VISIBLE) { - TranslateAnimation animate = new TranslateAnimation( - 0, - 0, - 0, - fab.getHeight() * 2); - animate.setDuration(300); - fab.startAnimation(animate); - fab.setVisibility(View.INVISIBLE); - scrollDiff = 0; - } + if (fab == null || fab.getVisibility() == View.VISIBLE) return; + fab.setVisibility(View.VISIBLE); + TranslateAnimation animate = new TranslateAnimation( + 0, + 0, + fab.getHeight() * 2, + 0); + animate.setDuration(300); + fab.startAnimation(animate); + } + + @Override + public void hideFab() { + View fab = getFab(); + if (fab == null || fab.getVisibility() != View.VISIBLE) return; + TranslateAnimation animate = new TranslateAnimation( + 0, + 0, + 0, + fab.getHeight() * 2); + animate.setDuration(300); + fab.startAnimation(animate); + fab.setVisibility(View.INVISIBLE); + scrollDiff = 0; } @Override @@ -312,10 +317,10 @@ public abstract class BaseStatusListFragment exten View fab = getFab(); if (fab!=null && GlobalUserPreferences.autoHideFab) { if (dy > 0 && fab.getVisibility() == View.VISIBLE) { - animateFab(false); + hideFab(); } else if (dy < 0 && fab.getVisibility() != View.VISIBLE) { if (list.getChildAt(0).getTop() == 0 || scrollDiff > 400) { - animateFab(true); + showFab(); scrollDiff = 0; } else { scrollDiff += Math.abs(dy); diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/HasFab.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/HasFab.java index 9245eafcd..056e80044 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/HasFab.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/HasFab.java @@ -4,4 +4,6 @@ import android.view.View; public interface HasFab { View getFab(); + void showFab(); + void hideFab(); } diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/HomeFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/HomeFragment.java index 9cfc29b26..6fa71d645 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/HomeFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/HomeFragment.java @@ -228,6 +228,7 @@ public class HomeFragment extends AppKitFragment implements OnBackPressedListene } getChildFragmentManager().beginTransaction().hide(fragmentForTab(currentTab)).show(newFragment).commit(); maybeTriggerLoading(newFragment); + if (newFragment instanceof HasFab fabulous) fabulous.showFab(); currentTab=tab; ((FragmentStackActivity)getActivity()).invalidateSystemBarColors(this); } diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/HomeTabFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/HomeTabFragment.java index 44a7bb4f9..0fba783f5 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/HomeTabFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/HomeTabFragment.java @@ -448,10 +448,20 @@ public class HomeTabFragment extends MastodonToolbarFragment implements Scrollab updateSwitcherIcon(i); } + @Override + public void showFab() { + if (fragments[pager.getCurrentItem()] instanceof BaseStatusListFragment l) l.showFab(); + } + + @Override + public void hideFab() { + if (fragments[pager.getCurrentItem()] instanceof BaseStatusListFragment l) l.hideFab(); + } + private void updateSwitcherIcon(int i) { timelineIcon.setImageResource(timelines[i].getIcon().iconRes); timelineTitle.setText(timelines[i].getTitle(getContext())); - if (fragments[i] instanceof BaseStatusListFragment l) l.animateFab(true); + showFab(); } @Override diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java index ed4adfd6b..a6fe0b022 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/ProfileFragment.java @@ -758,6 +758,16 @@ public class ProfileFragment extends LoaderFragment implements OnBackPressedList return fab; } + @Override + public void showFab() { + if (getFragmentForPage(pager.getCurrentItem()) instanceof HasFab fabulous) fabulous.showFab(); + } + + @Override + public void hideFab() { + if (getFragmentForPage(pager.getCurrentItem()) instanceof HasFab fabulous) fabulous.hideFab(); + } + private void onScrollChanged(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY){ int topBarsH=getToolbar().getHeight()+statusBarHeight; if(scrollY>avatarBorder.getTop()-topBarsH){ From 07f4ef1697bd929e3ca6f310dbf3b84ff939c897 Mon Sep 17 00:00:00 2001 From: sk Date: Thu, 25 May 2023 21:22:27 +0200 Subject: [PATCH 05/27] remove unused imports --- .../android/fragments/AccountTimelineFragment.java | 4 ---- .../joinmastodon/android/fragments/HomeTimelineFragment.java | 2 -- 2 files changed, 6 deletions(-) diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/AccountTimelineFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/AccountTimelineFragment.java index 680db45eb..41311e6a3 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/AccountTimelineFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/AccountTimelineFragment.java @@ -3,10 +3,6 @@ package org.joinmastodon.android.fragments; import android.app.Activity; import android.os.Bundle; import android.view.View; -import android.view.animation.TranslateAnimation; - -import androidx.annotation.NonNull; -import androidx.recyclerview.widget.RecyclerView; import org.joinmastodon.android.R; import org.joinmastodon.android.api.requests.accounts.GetAccountStatuses; diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/HomeTimelineFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/HomeTimelineFragment.java index 8b1babae1..bd8dec69c 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/HomeTimelineFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/HomeTimelineFragment.java @@ -8,8 +8,6 @@ import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; import org.joinmastodon.android.GlobalUserPreferences; -import org.joinmastodon.android.E; -import org.joinmastodon.android.R; import org.joinmastodon.android.api.requests.markers.SaveMarkers; import org.joinmastodon.android.api.requests.timelines.GetHomeTimeline; import org.joinmastodon.android.api.session.AccountSessionManager; From 220cd35d825847a21d814fb68744849fe8953df3 Mon Sep 17 00:00:00 2001 From: sk Date: Thu, 25 May 2023 21:25:09 +0200 Subject: [PATCH 06/27] fix context not checked for warnings closes sk22#518 --- .../fragments/AccountTimelineFragment.java | 6 ++++ .../BookmarkedStatusListFragment.java | 6 ++++ .../FavoritedStatusListFragment.java | 6 ++++ .../fragments/HomeTimelineFragment.java | 5 ++++ .../fragments/ListTimelineFragment.java | 6 ++++ .../android/fragments/StatusListFragment.java | 6 +++- .../android/fragments/ThreadFragment.java | 6 ++++ .../joinmastodon/android/model/Filter.java | 2 -- .../android/model/FilterResult.java | 7 +++++ .../joinmastodon/android/model/Status.java | 3 ++ .../ui/displayitems/StatusDisplayItem.java | 2 +- .../android/utils/StatusFilterPredicate.java | 29 +++++++++---------- 12 files changed, 65 insertions(+), 19 deletions(-) diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/AccountTimelineFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/AccountTimelineFragment.java index 41311e6a3..b3e2c3794 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/AccountTimelineFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/AccountTimelineFragment.java @@ -118,4 +118,10 @@ public class AccountTimelineFragment extends StatusListFragment{ protected void onRemoveAccountPostsEvent(RemoveAccountPostsEvent ev){ // no-op } + + + @Override + protected Filter.FilterContext getFilterContext() { + return Filter.FilterContext.ACCOUNT; + } } diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/BookmarkedStatusListFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/BookmarkedStatusListFragment.java index 6fd12d95d..6f863bdd2 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/BookmarkedStatusListFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/BookmarkedStatusListFragment.java @@ -4,6 +4,7 @@ import android.app.Activity; import org.joinmastodon.android.R; import org.joinmastodon.android.api.requests.statuses.GetBookmarkedStatuses; +import org.joinmastodon.android.model.Filter; import org.joinmastodon.android.model.HeaderPaginationList; import org.joinmastodon.android.model.Status; @@ -35,4 +36,9 @@ public class BookmarkedStatusListFragment extends StatusListFragment{ }) .exec(accountID); } + + @Override + protected Filter.FilterContext getFilterContext() { + return Filter.FilterContext.ACCOUNT; + } } diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/FavoritedStatusListFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/FavoritedStatusListFragment.java index 31fdd8442..41e6e0c7f 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/FavoritedStatusListFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/FavoritedStatusListFragment.java @@ -4,6 +4,7 @@ import android.app.Activity; import org.joinmastodon.android.R; import org.joinmastodon.android.api.requests.statuses.GetFavoritedStatuses; +import org.joinmastodon.android.model.Filter; import org.joinmastodon.android.model.HeaderPaginationList; import org.joinmastodon.android.model.Status; @@ -35,4 +36,9 @@ public class FavoritedStatusListFragment extends StatusListFragment{ }) .exec(accountID); } + + @Override + protected Filter.FilterContext getFilterContext() { + return Filter.FilterContext.ACCOUNT; + } } diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/HomeTimelineFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/HomeTimelineFragment.java index bd8dec69c..204a0bbd6 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/HomeTimelineFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/HomeTimelineFragment.java @@ -280,4 +280,9 @@ public class HomeTimelineFragment extends StatusListFragment { protected boolean shouldRemoveAccountPostsWhenUnfollowing(){ return true; } + + @Override + protected Filter.FilterContext getFilterContext() { + return Filter.FilterContext.HOME; + } } diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/ListTimelineFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/ListTimelineFragment.java index fd2155b54..20712f76f 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/ListTimelineFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/ListTimelineFragment.java @@ -162,4 +162,10 @@ public class ListTimelineFragment extends PinnableStatusListFragment { protected void onSetFabBottomInset(int inset) { ((ViewGroup.MarginLayoutParams) fab.getLayoutParams()).bottomMargin=V.dp(24)+inset; } + + + @Override + protected Filter.FilterContext getFilterContext() { + return Filter.FilterContext.HOME; + } } diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/StatusListFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/StatusListFragment.java index 3023bd20b..487b84759 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/StatusListFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/StatusListFragment.java @@ -34,7 +34,11 @@ public abstract class StatusListFragment extends BaseStatusListFragment{ protected List buildDisplayItems(Status s){ boolean addFooter = !GlobalUserPreferences.spectatorMode || (this instanceof ThreadFragment t && s.id.equals(t.mainStatus.id)); - return StatusDisplayItem.buildItems(this, s, accountID, s, knownAccounts, false, addFooter, null, Filter.FilterContext.HOME); + return StatusDisplayItem.buildItems(this, s, accountID, s, knownAccounts, false, addFooter, null, getFilterContext()); + } + + protected Filter.FilterContext getFilterContext() { + return Filter.FilterContext.PUBLIC; } @Override diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/ThreadFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/ThreadFragment.java index c16d776c4..206792f63 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/ThreadFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/ThreadFragment.java @@ -181,4 +181,10 @@ public class ThreadFragment extends StatusListFragment{ public boolean wantsLightNavigationBar(){ return !UiUtils.isDarkTheme(); } + + + @Override + protected Filter.FilterContext getFilterContext() { + return Filter.FilterContext.THREAD; + } } diff --git a/mastodon/src/main/java/org/joinmastodon/android/model/Filter.java b/mastodon/src/main/java/org/joinmastodon/android/model/Filter.java index 867679498..4a134c1cd 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/model/Filter.java +++ b/mastodon/src/main/java/org/joinmastodon/android/model/Filter.java @@ -15,9 +15,7 @@ import java.util.regex.Pattern; @Parcel public class Filter extends BaseModel{ - @RequiredField public String id; - @RequiredField public String phrase; public String title; public transient EnumSet context=EnumSet.noneOf(FilterContext.class); diff --git a/mastodon/src/main/java/org/joinmastodon/android/model/FilterResult.java b/mastodon/src/main/java/org/joinmastodon/android/model/FilterResult.java index 2b67ef4bf..361a5fac5 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/model/FilterResult.java +++ b/mastodon/src/main/java/org/joinmastodon/android/model/FilterResult.java @@ -1,8 +1,15 @@ package org.joinmastodon.android.model; +import org.joinmastodon.android.api.ObjectValidationException; import org.parceler.Parcel; @Parcel public class FilterResult extends BaseModel { public Filter filter; + + @Override + public void postprocess() throws ObjectValidationException { + super.postprocess(); + if (filter != null) filter.postprocess(); + } } diff --git a/mastodon/src/main/java/org/joinmastodon/android/model/Status.java b/mastodon/src/main/java/org/joinmastodon/android/model/Status.java index add0dd772..156a3d874 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/model/Status.java +++ b/mastodon/src/main/java/org/joinmastodon/android/model/Status.java @@ -101,6 +101,9 @@ public class Status extends BaseModel implements DisplayItemsParent, Searchable{ card.postprocess(); if(reblog!=null) reblog.postprocess(); + if(filtered!=null) + for(FilterResult fr : filtered) + fr.postprocess(); spoilerRevealed=GlobalUserPreferences.alwaysExpandContentWarnings || !sensitive; if (visibility.equals(StatusPrivacy.LOCAL)) localOnly = true; diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/StatusDisplayItem.java b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/StatusDisplayItem.java index 300f16185..f52382f5a 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/StatusDisplayItem.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/StatusDisplayItem.java @@ -107,7 +107,7 @@ public abstract class StatusDisplayItem{ StatusFilterPredicate filterPredicate = new StatusFilterPredicate(filters); if(!statusForContent.filterRevealed){ - statusForContent.filterRevealed = filterPredicate.testWithWarning(status); + statusForContent.filterRevealed = !filterPredicate.testHasStatusWarning(status, filterContext); } ReblogOrReplyLineStatusDisplayItem replyLine = null; diff --git a/mastodon/src/main/java/org/joinmastodon/android/utils/StatusFilterPredicate.java b/mastodon/src/main/java/org/joinmastodon/android/utils/StatusFilterPredicate.java index 9e4b91413..b4f170339 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/utils/StatusFilterPredicate.java +++ b/mastodon/src/main/java/org/joinmastodon/android/utils/StatusFilterPredicate.java @@ -20,6 +20,7 @@ public class StatusFilterPredicate implements Predicate{ filters=AccountSessionManager.getInstance().getAccount(accountID).wordFilters.stream().filter(f->f.context.contains(context)).collect(Collectors.toList()); } + // TODO: rewrite (see testHasStatusWarning) and generalize @Override public boolean test(Status status){ if(status.filtered!=null){ @@ -39,21 +40,19 @@ public class StatusFilterPredicate implements Predicate{ return true; } - public boolean testWithWarning(Status status) { - if(status.filtered!=null){ - if (status.filtered.isEmpty()){ - return true; - } - boolean matches=status.filtered.stream() - .map(filterResult->filterResult.filter) - .filter(filter->filter.expiresAt==null||filter.expiresAt.isAfter(Instant.now())) - .anyMatch(filter->filter.filterAction==Filter.FilterAction.WARN); - return !matches; + // TODO: move this method elsewhere; it's not part of the actual StatusFilterPredicate + public boolean testHasStatusWarning(Status status, Filter.FilterContext context) { + if (status.filtered != null) { + // use server-provided info on whether this status was filtered + if (status.filtered.isEmpty()) return false; + return status.filtered.stream() + .map(filterResult -> filterResult.filter) + .filter(filter -> filter.expiresAt == null || filter.expiresAt.isAfter(Instant.now())) + .filter(filter -> filter.context.contains(context)) + .anyMatch(filter -> filter.filterAction == Filter.FilterAction.WARN); + } else { + // look through local filters instead + return filters.stream().anyMatch(filter -> filter.matches(status)); } - for(Filter filter:filters){ - if(filter.matches(status)) - return false; - } - return true; } } From 99f0817bdbe64b5107dd38b0d01b9b8d0dbfc524 Mon Sep 17 00:00:00 2001 From: sk Date: Fri, 26 May 2023 02:07:50 +0200 Subject: [PATCH 07/27] generalize filtering logic --- .../android/api/CacheController.java | 20 ++---- .../fragments/AccountTimelineFragment.java | 7 +- .../fragments/HashtagTimelineFragment.java | 7 +- .../fragments/HomeTimelineFragment.java | 4 +- .../fragments/ListTimelineFragment.java | 2 +- .../ScheduledStatusListFragment.java | 2 +- .../fragments/StatusEditHistoryFragment.java | 8 ++- .../android/fragments/StatusListFragment.java | 4 +- .../android/fragments/ThreadFragment.java | 2 +- .../discover/DiscoverPostsFragment.java | 8 ++- .../discover/FederatedTimelineFragment.java | 7 +- .../discover/LocalTimelineFragment.java | 7 +- .../fragments/discover/SearchFragment.java | 3 +- .../report/ReportAddPostsChoiceFragment.java | 6 ++ .../joinmastodon/android/model/Filter.java | 4 +- .../ui/displayitems/StatusDisplayItem.java | 16 +---- .../android/utils/StatusFilterPredicate.java | 68 ++++++++++--------- 17 files changed, 98 insertions(+), 77 deletions(-) diff --git a/mastodon/src/main/java/org/joinmastodon/android/api/CacheController.java b/mastodon/src/main/java/org/joinmastodon/android/api/CacheController.java index a7a0ed78b..e4e54b451 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/api/CacheController.java +++ b/mastodon/src/main/java/org/joinmastodon/android/api/CacheController.java @@ -74,10 +74,8 @@ public class CacheController{ int flags=cursor.getInt(1); status.hasGapAfter=((flags & POST_FLAG_GAP_AFTER)!=0); newMaxID=status.id; - for(Filter filter:filters){ - if(filter.matches(status)) - continue outer; - } + if (!new StatusFilterPredicate(filters, Filter.FilterContext.HOME).test(status)) + continue outer; result.add(status); }while(cursor.moveToNext()); String _newMaxID=newMaxID; @@ -92,7 +90,7 @@ public class CacheController{ .setCallback(new Callback<>(){ @Override public void onSuccess(List result){ - callback.onSuccess(new CacheablePaginatedResponse<>(result.stream().filter(new StatusFilterPredicate(filters)).collect(Collectors.toList()), result.isEmpty() ? null : result.get(result.size()-1).id, false)); + callback.onSuccess(new CacheablePaginatedResponse<>(result.stream().filter(new StatusFilterPredicate(filters, Filter.FilterContext.HOME)).collect(Collectors.toList()), result.isEmpty() ? null : result.get(result.size()-1).id, false)); putHomeTimeline(result, maxID==null); } @@ -148,10 +146,8 @@ public class CacheController{ ntf.postprocess(); newMaxID=ntf.id; if(ntf.status!=null){ - for(Filter filter:filters){ - if(filter.matches(ntf.status)) - continue outer; - } + if (!new StatusFilterPredicate(filters, Filter.FilterContext.NOTIFICATIONS).test(ntf.status)) + continue outer; } result.add(ntf); }while(cursor.moveToNext()); @@ -170,11 +166,7 @@ public class CacheController{ public void onSuccess(List result){ callback.onSuccess(new CacheablePaginatedResponse<>(result.stream().filter(ntf->{ if(ntf.status!=null){ - for(Filter filter:filters){ - if(filter.matches(ntf.status)){ - return false; - } - } + return new StatusFilterPredicate(filters, Filter.FilterContext.NOTIFICATIONS).test(ntf.status); } return true; }).collect(Collectors.toList()), result.isEmpty() ? null : result.get(result.size()-1).id, false)); diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/AccountTimelineFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/AccountTimelineFragment.java index b3e2c3794..eb7106b47 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/AccountTimelineFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/AccountTimelineFragment.java @@ -60,7 +60,12 @@ public class AccountTimelineFragment extends StatusListFragment{ @Override public void onSuccess(List result){ if(getActivity()==null) return; - result=result.stream().filter(new StatusFilterPredicate(accountID, Filter.FilterContext.ACCOUNT)).collect(Collectors.toList()); + AccountSessionManager asm = AccountSessionManager.getInstance(); + result=result.stream().filter(status -> { + // don't hide own posts in own profile + if (asm.isSelf(accountID, user) && asm.isSelf(accountID, status.account)) return true; + else return new StatusFilterPredicate(accountID, getFilterContext()).test(status); + }).collect(Collectors.toList()); onDataLoaded(result, !result.isEmpty()); } }) diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/HashtagTimelineFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/HashtagTimelineFragment.java index 91cf498b7..815a6c307 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/HashtagTimelineFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/HashtagTimelineFragment.java @@ -123,7 +123,7 @@ public class HashtagTimelineFragment extends PinnableStatusListFragment { @Override public void onSuccess(List result){ if (getActivity() == null) return; - result=result.stream().filter(new StatusFilterPredicate(accountID, Filter.FilterContext.PUBLIC)).collect(Collectors.toList()); + result=result.stream().filter(new StatusFilterPredicate(accountID, getFilterContext())).collect(Collectors.toList()); onDataLoaded(result, !result.isEmpty()); } }) @@ -154,4 +154,9 @@ public class HashtagTimelineFragment extends PinnableStatusListFragment { protected void onSetFabBottomInset(int inset){ ((ViewGroup.MarginLayoutParams) fab.getLayoutParams()).bottomMargin=V.dp(24)+inset; } + + @Override + protected Filter.FilterContext getFilterContext() { + return Filter.FilterContext.PUBLIC; + } } diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/HomeTimelineFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/HomeTimelineFragment.java index 204a0bbd6..1e3ca22a8 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/HomeTimelineFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/HomeTimelineFragment.java @@ -148,7 +148,7 @@ public class HomeTimelineFragment extends StatusListFragment { result.get(result.size()-1).hasGapAfter=true; toAdd=result; } - StatusFilterPredicate filterPredicate=new StatusFilterPredicate(accountID, Filter.FilterContext.HOME); + StatusFilterPredicate filterPredicate=new StatusFilterPredicate(accountID, getFilterContext()); toAdd=toAdd.stream().filter(filterPredicate).collect(Collectors.toList()); if(!toAdd.isEmpty()){ prependItems(toAdd, true); @@ -227,7 +227,7 @@ public class HomeTimelineFragment extends StatusListFragment { List targetList=displayItems.subList(gapPos, gapPos+1); targetList.clear(); List insertedPosts=data.subList(gapPostIndex+1, gapPostIndex+1); - StatusFilterPredicate filterPredicate=new StatusFilterPredicate(accountID, Filter.FilterContext.HOME); + StatusFilterPredicate filterPredicate=new StatusFilterPredicate(accountID, getFilterContext()); for(Status s:result){ if(idsBelowGap.contains(s.id)) break; diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/ListTimelineFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/ListTimelineFragment.java index 20712f76f..da6322a85 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/ListTimelineFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/ListTimelineFragment.java @@ -137,7 +137,7 @@ public class ListTimelineFragment extends PinnableStatusListFragment { @Override public void onSuccess(List result) { if (getActivity() == null) return; - result=result.stream().filter(new StatusFilterPredicate(accountID, Filter.FilterContext.HOME)).collect(Collectors.toList()); + result=result.stream().filter(new StatusFilterPredicate(accountID, getFilterContext())).collect(Collectors.toList()); onDataLoaded(result, !result.isEmpty()); } }) diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/ScheduledStatusListFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/ScheduledStatusListFragment.java index 8b4f50023..1fb25efbf 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/ScheduledStatusListFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/ScheduledStatusListFragment.java @@ -79,7 +79,7 @@ public class ScheduledStatusListFragment extends BaseStatusListFragment buildDisplayItems(ScheduledStatus s) { - return StatusDisplayItem.buildItems(this, s.toStatus(), accountID, s, knownAccounts, false, false, null, true); + return StatusDisplayItem.buildItems(this, s.toStatus(), accountID, s, knownAccounts, false, false, null, true, null); } @Override diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/StatusEditHistoryFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/StatusEditHistoryFragment.java index aa2a59c46..4cf0e0639 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/StatusEditHistoryFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/StatusEditHistoryFragment.java @@ -6,6 +6,7 @@ import android.view.View; import org.joinmastodon.android.R; import org.joinmastodon.android.api.requests.statuses.GetStatusEditHistory; +import org.joinmastodon.android.model.Filter; import org.joinmastodon.android.model.Status; import org.joinmastodon.android.ui.displayitems.ReblogOrReplyLineStatusDisplayItem; import org.joinmastodon.android.ui.displayitems.StatusDisplayItem; @@ -55,7 +56,7 @@ public class StatusEditHistoryFragment extends StatusListFragment{ @Override protected List buildDisplayItems(Status s){ - List items=StatusDisplayItem.buildItems(this, s, accountID, s, knownAccounts, true, false, null); + List items=StatusDisplayItem.buildItems(this, s, accountID, s, knownAccounts, true, false, null, null); int idx=data.indexOf(s); if(idx>=0){ String date=UiUtils.DATE_TIME_FORMATTER.format(s.createdAt.atZone(ZoneId.systemDefault())); @@ -156,4 +157,9 @@ public class StatusEditHistoryFragment extends StatusListFragment{ public boolean isItemEnabled(String id){ return false; } + + @Override + protected Filter.FilterContext getFilterContext() { + return null; + } } diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/StatusListFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/StatusListFragment.java index 487b84759..e330631d4 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/StatusListFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/StatusListFragment.java @@ -37,9 +37,7 @@ public abstract class StatusListFragment extends BaseStatusListFragment{ return StatusDisplayItem.buildItems(this, s, accountID, s, knownAccounts, false, addFooter, null, getFilterContext()); } - protected Filter.FilterContext getFilterContext() { - return Filter.FilterContext.PUBLIC; - } + protected abstract Filter.FilterContext getFilterContext(); @Override protected void addAccountToKnown(Status s){ diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/ThreadFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/ThreadFragment.java index 206792f63..627e7a0ad 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/ThreadFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/ThreadFragment.java @@ -137,7 +137,7 @@ public class ThreadFragment extends StatusListFragment{ } private List filterStatuses(List statuses){ - StatusFilterPredicate statusFilterPredicate=new StatusFilterPredicate(accountID,Filter.FilterContext.THREAD); + StatusFilterPredicate statusFilterPredicate=new StatusFilterPredicate(accountID,getFilterContext()); return statuses.stream() .filter(statusFilterPredicate) .collect(Collectors.toList()); diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/discover/DiscoverPostsFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/discover/DiscoverPostsFragment.java index 0d73a4e9f..8b16f9c58 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/discover/DiscoverPostsFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/discover/DiscoverPostsFragment.java @@ -26,7 +26,7 @@ public class DiscoverPostsFragment extends StatusListFragment implements IsOnTop @Override public void onSuccess(List result){ if (getActivity() == null) return; - result=result.stream().filter(new StatusFilterPredicate(accountID, Filter.FilterContext.PUBLIC)).collect(Collectors.toList()); + result=result.stream().filter(new StatusFilterPredicate(accountID, getFilterContext())).collect(Collectors.toList()); onDataLoaded(result, !result.isEmpty()); } }).exec(accountID); @@ -42,4 +42,10 @@ public class DiscoverPostsFragment extends StatusListFragment implements IsOnTop public boolean isOnTop() { return isRecyclerViewOnTop(list); } + + + @Override + protected Filter.FilterContext getFilterContext() { + return Filter.FilterContext.PUBLIC; + } } diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/discover/FederatedTimelineFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/discover/FederatedTimelineFragment.java index 91c3e7073..bd3fecce2 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/discover/FederatedTimelineFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/discover/FederatedTimelineFragment.java @@ -35,7 +35,7 @@ public class FederatedTimelineFragment extends StatusListFragment { if(!result.isEmpty()) maxID=result.get(result.size()-1).id; if (getActivity() == null) return; - result=result.stream().filter(new StatusFilterPredicate(accountID, Filter.FilterContext.PUBLIC)).collect(Collectors.toList()); + result=result.stream().filter(new StatusFilterPredicate(accountID, getFilterContext())).collect(Collectors.toList()); onDataLoaded(result, !result.isEmpty()); } }) @@ -47,4 +47,9 @@ public class FederatedTimelineFragment extends StatusListFragment { super.onViewCreated(view, savedInstanceState); bannerHelper.maybeAddBanner(contentWrap); } + + @Override + protected Filter.FilterContext getFilterContext() { + return Filter.FilterContext.PUBLIC; + } } diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/discover/LocalTimelineFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/discover/LocalTimelineFragment.java index 7ccbfc91e..eea58b6a8 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/discover/LocalTimelineFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/discover/LocalTimelineFragment.java @@ -34,7 +34,7 @@ public class LocalTimelineFragment extends StatusListFragment { if(!result.isEmpty()) maxID=result.get(result.size()-1).id; if (getActivity() == null) return; - result=result.stream().filter(new StatusFilterPredicate(accountID, Filter.FilterContext.PUBLIC)).collect(Collectors.toList()); + result=result.stream().filter(new StatusFilterPredicate(accountID, getFilterContext())).collect(Collectors.toList()); onDataLoaded(result, !result.isEmpty()); } }) @@ -46,4 +46,9 @@ public class LocalTimelineFragment extends StatusListFragment { super.onViewCreated(view, savedInstanceState); bannerHelper.maybeAddBanner(contentWrap); } + + @Override + protected Filter.FilterContext getFilterContext() { + return Filter.FilterContext.PUBLIC; + } } diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/discover/SearchFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/discover/SearchFragment.java index 9a1511c34..e9fd83fca 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/discover/SearchFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/discover/SearchFragment.java @@ -15,6 +15,7 @@ import org.joinmastodon.android.fragments.IsOnTop; import org.joinmastodon.android.fragments.ProfileFragment; import org.joinmastodon.android.fragments.ThreadFragment; import org.joinmastodon.android.model.Account; +import org.joinmastodon.android.model.Filter; import org.joinmastodon.android.model.Hashtag; import org.joinmastodon.android.model.SearchResult; import org.joinmastodon.android.model.SearchResults; @@ -80,7 +81,7 @@ public class SearchFragment extends BaseStatusListFragment impleme return switch(s.type){ case ACCOUNT -> Collections.singletonList(new AccountStatusDisplayItem(s.id, this, s.account)); case HASHTAG -> Collections.singletonList(new HashtagStatusDisplayItem(s.id, this, s.hashtag)); - case STATUS -> StatusDisplayItem.buildItems(this, s.status, accountID, s, knownAccounts, false, true, null); + case STATUS -> StatusDisplayItem.buildItems(this, s.status, accountID, s, knownAccounts, false, true, null, Filter.FilterContext.PUBLIC); }; } diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/report/ReportAddPostsChoiceFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/report/ReportAddPostsChoiceFragment.java index c2a8dd2b3..a04d0a9a8 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/report/ReportAddPostsChoiceFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/report/ReportAddPostsChoiceFragment.java @@ -21,6 +21,7 @@ import org.joinmastodon.android.api.requests.accounts.GetAccountStatuses; import org.joinmastodon.android.events.FinishReportFragmentsEvent; import org.joinmastodon.android.fragments.StatusListFragment; import org.joinmastodon.android.model.Account; +import org.joinmastodon.android.model.Filter; import org.joinmastodon.android.model.Status; import org.joinmastodon.android.ui.displayitems.AudioStatusDisplayItem; import org.joinmastodon.android.ui.displayitems.HeaderStatusDisplayItem; @@ -261,4 +262,9 @@ public class ReportAddPostsChoiceFragment extends StatusListFragment{ protected boolean wantsOverlaySystemNavigation(){ return false; } + + @Override + protected Filter.FilterContext getFilterContext() { + return null; + } } diff --git a/mastodon/src/main/java/org/joinmastodon/android/model/Filter.java b/mastodon/src/main/java/org/joinmastodon/android/model/Filter.java index 4a134c1cd..019d249a6 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/model/Filter.java +++ b/mastodon/src/main/java/org/joinmastodon/android/model/Filter.java @@ -2,16 +2,18 @@ package org.joinmastodon.android.model; import android.text.TextUtils; +import androidx.annotation.Nullable; + import com.google.gson.annotations.SerializedName; import org.joinmastodon.android.api.ObjectValidationException; -import org.joinmastodon.android.api.RequiredField; import org.parceler.Parcel; import java.time.Instant; import java.util.EnumSet; import java.util.List; import java.util.regex.Pattern; +import java.util.stream.Stream; @Parcel public class Filter extends BaseModel{ diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/StatusDisplayItem.java b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/StatusDisplayItem.java index f52382f5a..69d3786c8 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/StatusDisplayItem.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/StatusDisplayItem.java @@ -81,18 +81,10 @@ public abstract class StatusDisplayItem{ }; } - public static ArrayList buildItems(BaseStatusListFragment fragment, Status status, String accountID, DisplayItemsParent parentObject, Map knownAccounts, boolean inset, boolean addFooter, Notification notification){ - return buildItems(fragment, status, accountID, parentObject, knownAccounts, inset, addFooter, notification, false, Filter.FilterContext.HOME); - } - public static ArrayList buildItems(BaseStatusListFragment fragment, Status status, String accountID, DisplayItemsParent parentObject, Map knownAccounts, boolean inset, boolean addFooter, Notification notification, Filter.FilterContext filterContext){ return buildItems(fragment, status, accountID, parentObject, knownAccounts, inset, addFooter, notification, false, filterContext); } - public static ArrayList buildItems(BaseStatusListFragment fragment, Status status, String accountID, DisplayItemsParent parentObject, Map knownAccounts, boolean inset, boolean addFooter, Notification notification, boolean disableTranslate){ - return buildItems(fragment, status, accountID, parentObject, knownAccounts, inset, addFooter, notification, disableTranslate, Filter.FilterContext.HOME); - } - public static ArrayList buildItems(BaseStatusListFragment fragment, Status status, String accountID, DisplayItemsParent parentObject, Map knownAccounts, boolean inset, boolean addFooter, Notification notification, boolean disableTranslate, Filter.FilterContext filterContext){ String parentID=parentObject.getID(); ArrayList items=new ArrayList<>(); @@ -102,12 +94,8 @@ public abstract class StatusDisplayItem{ args.putString("account", accountID); ScheduledStatus scheduledStatus = parentObject instanceof ScheduledStatus ? (ScheduledStatus) parentObject : null; - List filters = AccountSessionManager.getInstance().getAccount(accountID).wordFilters.stream() - .filter(f -> f.context.contains(filterContext)).collect(Collectors.toList()); - StatusFilterPredicate filterPredicate = new StatusFilterPredicate(filters); - - if(!statusForContent.filterRevealed){ - statusForContent.filterRevealed = !filterPredicate.testHasStatusWarning(status, filterContext); + if (!statusForContent.filterRevealed) { + statusForContent.filterRevealed = new StatusFilterPredicate(accountID, filterContext, Filter.FilterAction.WARN).test(status); } ReblogOrReplyLineStatusDisplayItem replyLine = null; diff --git a/mastodon/src/main/java/org/joinmastodon/android/utils/StatusFilterPredicate.java b/mastodon/src/main/java/org/joinmastodon/android/utils/StatusFilterPredicate.java index b4f170339..d1fdf5305 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/utils/StatusFilterPredicate.java +++ b/mastodon/src/main/java/org/joinmastodon/android/utils/StatusFilterPredicate.java @@ -8,51 +8,53 @@ import java.time.Instant; import java.util.List; import java.util.function.Predicate; import java.util.stream.Collectors; +import java.util.stream.Stream; public class StatusFilterPredicate implements Predicate{ private final List filters; + private final Filter.FilterContext context; + private final Filter.FilterAction action; - public StatusFilterPredicate(List filters){ + public StatusFilterPredicate(List filters, Filter.FilterContext context){ this.filters=filters; + this.context = context; + this.action = Filter.FilterAction.HIDE; } + /** + * @param context null makes the predicate pass automatically + */ public StatusFilterPredicate(String accountID, Filter.FilterContext context){ - filters=AccountSessionManager.getInstance().getAccount(accountID).wordFilters.stream().filter(f->f.context.contains(context)).collect(Collectors.toList()); + this(accountID, context, Filter.FilterAction.HIDE); + } + + /** + * @param context null makes the predicate pass automatically + * @param action defines what the predicate should check: + * should not be hidden or should not display with warning + */ + public StatusFilterPredicate(String accountID, Filter.FilterContext context, Filter.FilterAction action){ + filters=AccountSessionManager.getInstance().getAccount(accountID).wordFilters.stream().filter(f->f.context.contains(context)).collect(Collectors.toList()); + this.context = context; + this.action = action; } - // TODO: rewrite (see testHasStatusWarning) and generalize @Override public boolean test(Status status){ - if(status.filtered!=null){ - if (status.filtered.isEmpty()){ - return true; - } - boolean matches=status.filtered.stream() - .map(filterResult->filterResult.filter) - .filter(filter->filter.expiresAt==null||filter.expiresAt.isAfter(Instant.now())) - .anyMatch(filter->filter.filterAction==Filter.FilterAction.HIDE); - return !matches; - } - for(Filter filter:filters){ - if(filter.matches(status)) - return false; - } - return true; - } + if (context == null) return true; - // TODO: move this method elsewhere; it's not part of the actual StatusFilterPredicate - public boolean testHasStatusWarning(Status status, Filter.FilterContext context) { - if (status.filtered != null) { - // use server-provided info on whether this status was filtered - if (status.filtered.isEmpty()) return false; - return status.filtered.stream() - .map(filterResult -> filterResult.filter) - .filter(filter -> filter.expiresAt == null || filter.expiresAt.isAfter(Instant.now())) - .filter(filter -> filter.context.contains(context)) - .anyMatch(filter -> filter.filterAction == Filter.FilterAction.WARN); - } else { - // look through local filters instead - return filters.stream().anyMatch(filter -> filter.matches(status)); - } + Stream stream = status.filtered != null + // use server-provided per-status info (status.filtered) if available + ? status.filtered.stream().map(f -> f.filter) + // or fall back to cached filters + : filters.stream().filter(filter -> filter.matches(status)); + + return stream + // discard expired filters + .filter(filter -> filter.expiresAt == null || filter.expiresAt.isAfter(Instant.now())) + // only apply filters for given context + .filter(filter -> filter.context.contains(context)) + // treating filterAction = null (from filters list) as FilterAction.HIDE + .noneMatch(filter -> filter.filterAction == null ? action == Filter.FilterAction.HIDE : filter.filterAction == action); } } From be569cbe72de76722f4a3c09b81ad16b706242f7 Mon Sep 17 00:00:00 2001 From: Choukajohn Date: Sat, 22 Apr 2023 22:46:59 +0000 Subject: [PATCH 08/27] Translated using Weblate (French) Currently translated at 100.0% (275 of 275 strings) Translation: Megalodon/values Translate-URL: https://translate.codeberg.org/projects/megalodon/values/fr/ --- mastodon/src/main/res/values-fr-rFR/strings_sk.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mastodon/src/main/res/values-fr-rFR/strings_sk.xml b/mastodon/src/main/res/values-fr-rFR/strings_sk.xml index b40cc90b8..84a7d3d32 100644 --- a/mastodon/src/main/res/values-fr-rFR/strings_sk.xml +++ b/mastodon/src/main/res/values-fr-rFR/strings_sk.xml @@ -274,4 +274,6 @@ Ligne boost/réponse compacte Ligne \"En réponse à\" au-dessus de l\'avatar Confirmer avant de booster + a réagi + a réagi avec %s \ No newline at end of file From 56e9cc3406ce6512c50b18e2094d2666b5eaed35 Mon Sep 17 00:00:00 2001 From: Linerly Date: Sun, 23 Apr 2023 16:43:03 +0000 Subject: [PATCH 09/27] Translated using Weblate (Indonesian) Currently translated at 100.0% (275 of 275 strings) Translation: Megalodon/values Translate-URL: https://translate.codeberg.org/projects/megalodon/values/id/ --- mastodon/src/main/res/values-in-rID/strings_sk.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mastodon/src/main/res/values-in-rID/strings_sk.xml b/mastodon/src/main/res/values-in-rID/strings_sk.xml index 92d01deda..fee306969 100644 --- a/mastodon/src/main/res/values-in-rID/strings_sk.xml +++ b/mastodon/src/main/res/values-in-rID/strings_sk.xml @@ -274,4 +274,6 @@ Tampilkan utasan Baris berbagi/balasan Konfirmasi sebelum membagikan ulang + bereaksi dengan %s + bereaksi \ No newline at end of file From 95432949961985f4bb900cd619a4958489ebd202 Mon Sep 17 00:00:00 2001 From: gicorada Date: Sun, 23 Apr 2023 10:01:57 +0000 Subject: [PATCH 10/27] Translated using Weblate (Italian) Currently translated at 100.0% (275 of 275 strings) Translation: Megalodon/values Translate-URL: https://translate.codeberg.org/projects/megalodon/values/it/ --- .../src/main/res/values-it-rIT/strings_sk.xml | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/mastodon/src/main/res/values-it-rIT/strings_sk.xml b/mastodon/src/main/res/values-it-rIT/strings_sk.xml index e853b6f4a..9ed722573 100644 --- a/mastodon/src/main/res/values-it-rIT/strings_sk.xml +++ b/mastodon/src/main/res/values-it-rIT/strings_sk.xml @@ -67,8 +67,8 @@ Aggiungi ai preferiti con un altro account Inserito tra i preferiti come %s Già aggiunto ai preferiti - Condividi con un altro account - Già condiviso + Boost con un altro account + Già boostato Impostazioni del profilo Preferenze di pubblicazione Configura i filtri @@ -86,8 +86,8 @@ Sei sicuro di voler eliminare tutte le notifiche\? Cercando nel Fediverso Crea un post riguardo a questo - Annulla la condivisione - Reblog con visibilità + Annulla il boost + Boost con visibilità Copia il link al post Apri con un altro account Non è stato possibile trovare la risorsa @@ -131,7 +131,7 @@ Cercando su %s Hashtag che segui Già aggiunto ai segnalibri - Condivisione eseguita come %s + Boost eseguito come %s Post non pubblicati Salvare la bozza\? Salvare le modifiche\? @@ -220,7 +220,7 @@ Mappamondo Modifica timeline Modifica timeline - Modifica un post condiviso + Modifica un post boostato Post modificati Allega file Puntina @@ -239,7 +239,7 @@ · Solo istanza locale Funzionalità dell\'istanza - Il server supporta solo post locali + Il server supporta anche post solo locali La tua istanza deve supportare post solo locali per far funzionare questa opzione. Molte versioni modificate di Mastodon lo fanno, ma Mastodon no. Modalità solo locale di Glitch Abilita se la tua istanza utilizza Glitch. Non necessario per Hometown o Akkoma. @@ -261,4 +261,17 @@ Segui con un altro account Seguito con %s Nascondi automaticamente il pulsante Pubblica + In risposta + Citando %s + Visibilità delle risposte + Tutte le risposte + Risposte ai miei follower + Mi risponde + Risposta inviata a %s + Conferma prima del boost + ha reagito + Mostra discussione + Linea boost/risposta compatta + ha reagito con %s + Linea \"In risposta a\" sopra l\'avatar \ No newline at end of file From 630064500d660efe5eb153faeabaca12fe5e0c82 Mon Sep 17 00:00:00 2001 From: Eryk Michalak Date: Sun, 23 Apr 2023 12:34:01 +0000 Subject: [PATCH 11/27] Translated using Weblate (Polish) Currently translated at 100.0% (275 of 275 strings) Translation: Megalodon/values Translate-URL: https://translate.codeberg.org/projects/megalodon/values/pl/ --- .../src/main/res/values-pl-rPL/strings_sk.xml | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/mastodon/src/main/res/values-pl-rPL/strings_sk.xml b/mastodon/src/main/res/values-pl-rPL/strings_sk.xml index 9930482df..d4822e6b0 100644 --- a/mastodon/src/main/res/values-pl-rPL/strings_sk.xml +++ b/mastodon/src/main/res/values-pl-rPL/strings_sk.xml @@ -69,8 +69,8 @@ Zakładka została już zapisana Polubiono jako %s Już polubiono - Zrebloguj jako %s - Już zreblogowano + Podbite jako %s + Już podbito Odpowiedz innym kontem Identyczna ikona dla wszystkich notyfikacji Tłumacz tylko otwarte wpisy @@ -96,8 +96,8 @@ Usuń wszystkie Czy jesteś pewien że chcesz usunąć wszystkie powiadomienia\? Wyszukiwanie na Fediwersum - Cofnij reblog - Reblog z widocznością + Cofnij podbicie + Podbij z widocznością Wpis o tym Hashtagi które obserwujesz Kopiuj link do wpisu @@ -127,7 +127,7 @@ Nie twórz wersji roboczej Zaplanowany wpis lub kopia robocza Polub innym kontem - Już zreblogowano + Podbij z innego konta Zmniejsz ruch animacji Oznacz jako przeczytane O instancji @@ -220,7 +220,7 @@ Słuchawki Człowiek Glob - Edytuje reblogowany wpis + Edytuje podbijany wpis Pinezka Usuń obserwującego Usunąć %s z obserwatorów, poprzez zablokowanie i natychmiastowe odblokowanie ich\? @@ -270,6 +270,8 @@ Wysłano odpowiedź do %s Tekst \"W odpowiedzi na\" nad avatarem Pokaż wątek - Zmniejsz linię reblogu/odpowiedzi - Potwierdź przed reblogowaniem + Zmniejsz linię podbijania/odpowiedzi + Potwierdź przed podbiciem + zareagował(a) z %s + zareagował(a) \ No newline at end of file From 96e4a4933ccfebd1f4ed1c4c21693c146f3437e0 Mon Sep 17 00:00:00 2001 From: ihor_ck Date: Sat, 22 Apr 2023 18:32:31 +0000 Subject: [PATCH 12/27] Translated using Weblate (Ukrainian) Currently translated at 100.0% (275 of 275 strings) Translation: Megalodon/values Translate-URL: https://translate.codeberg.org/projects/megalodon/values/uk/ --- mastodon/src/main/res/values-uk-rUA/strings_sk.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mastodon/src/main/res/values-uk-rUA/strings_sk.xml b/mastodon/src/main/res/values-uk-rUA/strings_sk.xml index f07c6c240..ac7b05590 100644 --- a/mastodon/src/main/res/values-uk-rUA/strings_sk.xml +++ b/mastodon/src/main/res/values-uk-rUA/strings_sk.xml @@ -273,4 +273,6 @@ Показати потік Компактний рядок для поширеного допису/відповіді Підтверджувати поширення + реагує + реагує з %s \ No newline at end of file From b057c9f7a8154f5c4f62377750775b45900ba57c Mon Sep 17 00:00:00 2001 From: Eryk Michalak Date: Sun, 23 Apr 2023 12:32:01 +0000 Subject: [PATCH 13/27] Translated using Weblate (Polish) Currently translated at 100.0% (17 of 17 strings) Translation: Megalodon/metadata Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/pl/ --- metadata/pl/changelogs/83.txt | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 metadata/pl/changelogs/83.txt diff --git a/metadata/pl/changelogs/83.txt b/metadata/pl/changelogs/83.txt new file mode 100644 index 000000000..2dcf7fdc4 --- /dev/null +++ b/metadata/pl/changelogs/83.txt @@ -0,0 +1,5 @@ +- Polubienia / dodawanie zakładek / odpowiadanie bezpośrednio z powiadomienia +- Ładniejsze i bardziej spójne nagłówki dla reblogów i odpowiedzi na osi czasu +- Kropka powiadomień (która w rzeczywistości nie ładuje jeszcze powiadomień) +- Dla użytkowników Akkoma: widoczność odpowiedzi, posortowane odpowiedzi na wątek, cytowanie, itp. +- Naprawienie błędów i inne drobne poprawki From a8589cc5b0542937d917eb9626d4be44dfe5dfb5 Mon Sep 17 00:00:00 2001 From: gicorada Date: Sun, 23 Apr 2023 10:07:16 +0000 Subject: [PATCH 14/27] Translated using Weblate (Italian) Currently translated at 100.0% (17 of 17 strings) Translation: Megalodon/metadata Translate-URL: https://translate.codeberg.org/projects/megalodon/metadata/it/ --- metadata/it-IT/changelogs/83.txt | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 metadata/it-IT/changelogs/83.txt diff --git a/metadata/it-IT/changelogs/83.txt b/metadata/it-IT/changelogs/83.txt new file mode 100644 index 000000000..015d08c32 --- /dev/null +++ b/metadata/it-IT/changelogs/83.txt @@ -0,0 +1,5 @@ +- Mi piace/salva/rispondi direttamente dalla notifica +- Intestazioni più belle e coerenti per i reblog e le risposte nella timeline +- Punto di notifica (che, in realtà, non carica ancora le notifiche) +- Per utenti di Akkoma: visibilità delle risposte, citazioni, risposte ai thread ordinate... +- Correzione di alcuni crash e piccole modifiche From a410d19114cd14e6c922bd16e9669c82c9f89e81 Mon Sep 17 00:00:00 2001 From: gallegonovato Date: Tue, 25 Apr 2023 12:11:01 +0000 Subject: [PATCH 15/27] Translated using Weblate (Spanish) Currently translated at 100.0% (275 of 275 strings) Translation: Megalodon/values Translate-URL: https://translate.codeberg.org/projects/megalodon/values/es/ --- .../src/main/res/values-es-rES/strings_sk.xml | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/mastodon/src/main/res/values-es-rES/strings_sk.xml b/mastodon/src/main/res/values-es-rES/strings_sk.xml index 7cc725731..ef52a840d 100644 --- a/mastodon/src/main/res/values-es-rES/strings_sk.xml +++ b/mastodon/src/main/res/values-es-rES/strings_sk.xml @@ -83,8 +83,8 @@ %s no parece admitir la traducción. Buscándolo en el Fediverso Publicar sobre esto - Deshacer reblogueo - Rebloguea con visibilidad + Deshacer compartir + Comparte con visibilidad Etiquetas que sigues Copiar enlace de la publicación Abrir con otra cuenta @@ -94,9 +94,9 @@ Ya en marcadores Marcado favorito como %s Ya es un favorito - Impulsar con otra cuenta - Impulsado como %s - Ya se había impulsado + Compartir con otra cuenta + Compartido como %s + Ya impulsado Responder con otra cuenta Añadir a marcadores con otra cuenta Marcar favorito con otra cuenta @@ -214,7 +214,7 @@ ALT editado Publicaciones editadas - Editó una publicación impulsada + editado una publicación compartida Buscando… Sin resultados ¿Guardar el borrador\? @@ -271,6 +271,8 @@ Respuesta enviada a %s \"En respuesta a\" línea sobre el avatar Mostrar hilo - Línea compacta de reblog/respuesta + Línea compacta compartida/respondida Confirmar antes de volver a publicar + reaccionó con %s + reaccionó \ No newline at end of file From 5b747bfc743a8686121985d986583f16f7046051 Mon Sep 17 00:00:00 2001 From: Daudix_UFO Date: Wed, 3 May 2023 16:18:52 +0000 Subject: [PATCH 16/27] Translated using Weblate (Russian) Currently translated at 100.0% (275 of 275 strings) Translation: Megalodon/values Translate-URL: https://translate.codeberg.org/projects/megalodon/values/ru/ --- .../src/main/res/values-ru-rRU/strings_sk.xml | 32 ++++++++++++++++--- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/mastodon/src/main/res/values-ru-rRU/strings_sk.xml b/mastodon/src/main/res/values-ru-rRU/strings_sk.xml index c6c48cbef..a071ab499 100644 --- a/mastodon/src/main/res/values-ru-rRU/strings_sk.xml +++ b/mastodon/src/main/res/values-ru-rRU/strings_sk.xml @@ -81,7 +81,7 @@ Очистить все уведомления Удалить все Вы точно хотите удалить все уведомления\? - Отменить продвижение поста + Отменить продвижение Продвижение с видимостью Создать публикацию об этом Скопировать ссылку на публикацию @@ -91,14 +91,14 @@ Добавить в избранное с другого аккаунта Уже в избранном Продвигать на другом аккаунте - Реблог на %s + Продвинуть на %s Ответить с другого аккаунта Единая иконка для всех уведомлений Ищем это в Fediverse Добавить в закладки с другого аккаунта В закладки как %s В избранное как %s - Уже реблогнуто + Уже продвинуто Хештеги, на которые вы подписаны Просматриваем на %s Неотправленные сообщения @@ -225,7 +225,7 @@ Щит Книга Прикрепить файл - Изменяет репост + Изменяет продвинутый пост Поиск… Нет результатов Сохранить черновик\? @@ -250,4 +250,28 @@ Версия сервера: %s Итоги голосования Для работы этой функции ваш инстанс должен поддерживать локальные публикации. Большинство модифицированных версий Mastodon это поддерживают, но сам Mastodon нет. + Префикс ответа CW с “re:” + Отфильтровано: %s + Развернуть + Свернуть + Сворачивать очень длинные посты + Исправить вложения\? + Некоторые вложения еще не загрузились. + Цитирование%s + Видимость ответа + Скрыть кнопки взаимодействия + Подписаться с другого аккаунта + Подписан с %s + В ответ + строка \"В ответ на\" над аватаром + Автоматическое скрытие кнопки Составить + Все ответы + Ответы на мои подписки + Ответы мне + Подтверждение перед продвижением + Компактная строка продвижения/ответа + отреагировал с %s + отреагировал + Отправлен ответ на %s + Показать тему \ No newline at end of file From b30d4a025f494a34a94ebbde2c007a99ee557272 Mon Sep 17 00:00:00 2001 From: Espasant3 Date: Thu, 4 May 2023 20:55:07 +0000 Subject: [PATCH 17/27] Translated using Weblate (Galician) Currently translated at 99.6% (274 of 275 strings) Translation: Megalodon/values Translate-URL: https://translate.codeberg.org/projects/megalodon/values/gl/ --- mastodon/src/main/res/values-gl-rES/strings_sk.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mastodon/src/main/res/values-gl-rES/strings_sk.xml b/mastodon/src/main/res/values-gl-rES/strings_sk.xml index b36237b35..ee92fcbe0 100644 --- a/mastodon/src/main/res/values-gl-rES/strings_sk.xml +++ b/mastodon/src/main/res/values-gl-rES/strings_sk.xml @@ -273,4 +273,6 @@ Mostrar chío Compactar liña de impulso/resposta Confirma antes de impulsar + Redactado con %s + redactado \ No newline at end of file From a6fd6ae1359a583e11ee407148de6851bcc0c144 Mon Sep 17 00:00:00 2001 From: sk Date: Fri, 26 May 2023 02:37:25 +0200 Subject: [PATCH 18/27] add javadoc --- .../org/joinmastodon/android/utils/StatusFilterPredicate.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mastodon/src/main/java/org/joinmastodon/android/utils/StatusFilterPredicate.java b/mastodon/src/main/java/org/joinmastodon/android/utils/StatusFilterPredicate.java index d1fdf5305..7193b5c11 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/utils/StatusFilterPredicate.java +++ b/mastodon/src/main/java/org/joinmastodon/android/utils/StatusFilterPredicate.java @@ -15,6 +15,9 @@ public class StatusFilterPredicate implements Predicate{ private final Filter.FilterContext context; private final Filter.FilterAction action; + /** + * @param context null makes the predicate pass automatically + */ public StatusFilterPredicate(List filters, Filter.FilterContext context){ this.filters=filters; this.context = context; From ab7489a049cccf619525c6a39bb2dfa0d2c5f4a3 Mon Sep 17 00:00:00 2001 From: sk Date: Fri, 26 May 2023 02:37:32 +0200 Subject: [PATCH 19/27] bump version --- mastodon/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mastodon/build.gradle b/mastodon/build.gradle index dcab3037f..0f20606e2 100644 --- a/mastodon/build.gradle +++ b/mastodon/build.gradle @@ -15,8 +15,8 @@ android { applicationId "org.joinmastodon.android.sk" minSdk 23 targetSdk 33 - versionCode 83 - versionName "1.2.3+fork.83" + versionCode 84 + versionName "1.2.3+fork.84" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" resourceConfigurations += ['ar-rSA', 'ar-rDZ', 'be-rBY', 'bn-rBD', 'bs-rBA', 'ca-rES', 'cs-rCZ', 'da-rDK', 'de-rDE', 'el-rGR', 'es-rES', 'eu-rES', 'fa-rIR', 'fi-rFI', 'fil-rPH', 'fr-rFR', 'ga-rIE', 'gd-rGB', 'gl-rES', 'hi-rIN', 'hr-rHR', 'hu-rHU', 'hy-rAM', 'ig-rNG', 'in-rID', 'is-rIS', 'it-rIT', 'iw-rIL', 'ja-rJP', 'kab', 'ko-rKR', 'my-rMM', 'nl-rNL', 'no-rNO', 'oc-rFR', 'pl-rPL', 'pt-rBR', 'pt-rPT', 'ro-rRO', 'ru-rRU', 'si-rLK', 'sl-rSI', 'sv-rSE', 'th-rTH', 'tr-rTR', 'uk-rUA', 'ur-rIN', 'vi-rVN', 'zh-rCN', 'zh-rTW'] } From 6abfe6ddd7f6b00d6aa603f154f8ec7008a535b7 Mon Sep 17 00:00:00 2001 From: sk Date: Fri, 26 May 2023 17:02:39 +0200 Subject: [PATCH 20/27] add unit tests for status filter predicate --- .../utils/StatusFilterPredicateTest.java | 81 +++++++++++++++++++ .../joinmastodon/android/model/Status.java | 3 +- .../android/utils/StatusFilterPredicate.java | 36 ++++++--- 3 files changed, 107 insertions(+), 13 deletions(-) create mode 100644 mastodon/src/androidTest/java/org/joinmastodon/android/utils/StatusFilterPredicateTest.java diff --git a/mastodon/src/androidTest/java/org/joinmastodon/android/utils/StatusFilterPredicateTest.java b/mastodon/src/androidTest/java/org/joinmastodon/android/utils/StatusFilterPredicateTest.java new file mode 100644 index 000000000..0a00fc665 --- /dev/null +++ b/mastodon/src/androidTest/java/org/joinmastodon/android/utils/StatusFilterPredicateTest.java @@ -0,0 +1,81 @@ +package org.joinmastodon.android.utils; + +import static org.joinmastodon.android.model.Filter.FilterAction.*; +import static org.joinmastodon.android.model.Filter.FilterContext.*; +import static org.junit.Assert.*; + +import org.joinmastodon.android.model.Filter; +import org.joinmastodon.android.model.Status; +import org.junit.Test; + +import java.time.Instant; +import java.util.EnumSet; +import java.util.List; + +public class StatusFilterPredicateTest { + + private static final Filter hideMeFilter = new Filter(), warnMeFilter = new Filter(); + private static final List allFilters = List.of(hideMeFilter, warnMeFilter); + + private static final Status + hideInHomePublic = Status.ofFake(null, "hide me, please", Instant.now()), + warnInHomePublic = Status.ofFake(null, "display me with a warning", Instant.now()); + + static { + hideMeFilter.phrase = "hide me"; + hideMeFilter.filterAction = HIDE; + hideMeFilter.context = EnumSet.of(PUBLIC, HOME); + + warnMeFilter.phrase = "warning"; + warnMeFilter.filterAction = WARN; + warnMeFilter.context = EnumSet.of(PUBLIC, HOME); + } + + @Test + public void testHide() { + assertFalse("should not pass because matching filter applies to given context", + new StatusFilterPredicate(allFilters, HOME).test(hideInHomePublic)); + } + + @Test + public void testHideRegardlessOfContext() { + assertTrue("filters without context should always pass", + new StatusFilterPredicate(allFilters, null).test(hideInHomePublic)); + } + + @Test + public void testHideInDifferentContext() { + assertTrue("should pass because matching filter does not apply to given context", + new StatusFilterPredicate(allFilters, THREAD).test(hideInHomePublic)); + } + + @Test + public void testHideWithWarningText() { + assertTrue("should pass because matching filter is for warnings", + new StatusFilterPredicate(allFilters, HOME).test(warnInHomePublic)); + } + + @Test + public void testWarn() { + assertFalse("should not pass because filter applies to given context", + new StatusFilterPredicate(allFilters, HOME, WARN).test(warnInHomePublic)); + } + + @Test + public void testWarnRegardlessOfContext() { + assertTrue("filters without context should always pass", + new StatusFilterPredicate(allFilters, null, WARN).test(warnInHomePublic)); + } + + @Test + public void testWarnInDifferentContext() { + assertTrue("should pass because filter does not apply to given context", + new StatusFilterPredicate(allFilters, THREAD, WARN).test(warnInHomePublic)); + } + + @Test + public void testWarnWithHideText() { + assertTrue("should pass because matching filter is for hiding", + new StatusFilterPredicate(allFilters, HOME, WARN).test(hideInHomePublic)); + } +} \ No newline at end of file diff --git a/mastodon/src/main/java/org/joinmastodon/android/model/Status.java b/mastodon/src/main/java/org/joinmastodon/android/model/Status.java index 156a3d874..8df45b976 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/model/Status.java +++ b/mastodon/src/main/java/org/joinmastodon/android/model/Status.java @@ -50,6 +50,8 @@ public class Status extends BaseModel implements DisplayItemsParent, Searchable{ public long favouritesCount; public long repliesCount; public Instant editedAt; + // might not be provided (by older mastodon servers), + // so megalodon will use the locally cached filters if filtered == null public List filtered; public String url; @@ -180,7 +182,6 @@ public class Status extends BaseModel implements DisplayItemsParent, Searchable{ s.mentions = List.of(); s.tags = List.of(); s.emojis = List.of(); - s.filtered = List.of(); return s; } diff --git a/mastodon/src/main/java/org/joinmastodon/android/utils/StatusFilterPredicate.java b/mastodon/src/main/java/org/joinmastodon/android/utils/StatusFilterPredicate.java index 7193b5c11..8c73d3a87 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/utils/StatusFilterPredicate.java +++ b/mastodon/src/main/java/org/joinmastodon/android/utils/StatusFilterPredicate.java @@ -17,11 +17,28 @@ public class StatusFilterPredicate implements Predicate{ /** * @param context null makes the predicate pass automatically + * @param action defines what the predicate should check: + * status should not be hidden or should not display with warning */ - public StatusFilterPredicate(List filters, Filter.FilterContext context){ - this.filters=filters; + public StatusFilterPredicate(List filters, Filter.FilterContext context, Filter.FilterAction action){ + this.filters = filters; this.context = context; - this.action = Filter.FilterAction.HIDE; + this.action = action; + } + + public StatusFilterPredicate(List filters, Filter.FilterContext context){ + this(filters, context, Filter.FilterAction.HIDE); + } + + /** + * @param context null makes the predicate pass automatically + * @param action defines what the predicate should check: + * status should not be hidden or should not display with warning + */ + public StatusFilterPredicate(String accountID, Filter.FilterContext context, Filter.FilterAction action){ + filters=AccountSessionManager.getInstance().getAccount(accountID).wordFilters.stream().filter(f->f.context.contains(context)).collect(Collectors.toList()); + this.context = context; + this.action = action; } /** @@ -32,16 +49,11 @@ public class StatusFilterPredicate implements Predicate{ } /** - * @param context null makes the predicate pass automatically - * @param action defines what the predicate should check: - * should not be hidden or should not display with warning + * @return whether the status should be displayed without being hidden/warned about. + * will always return true if the context is null. + * true = display this status, + * false = filter this status */ - public StatusFilterPredicate(String accountID, Filter.FilterContext context, Filter.FilterAction action){ - filters=AccountSessionManager.getInstance().getAccount(accountID).wordFilters.stream().filter(f->f.context.contains(context)).collect(Collectors.toList()); - this.context = context; - this.action = action; - } - @Override public boolean test(Status status){ if (context == null) return true; From 274bca84d9924b7d4a53c7c18835c97cf4081a74 Mon Sep 17 00:00:00 2001 From: LucasGGamerM Date: Sat, 13 May 2023 18:42:25 -0300 Subject: [PATCH 21/27] Add display item for unknown/file attachments Co-authored-by: LucasGGamerM --- .../displayitems/FileStatusDisplayItem.java | 58 ++++++++++++++++++ .../ui/displayitems/StatusDisplayItem.java | 15 ++++- .../main/res/drawable/bg_search_button.xml | 11 ++++ .../drawable/ic_fluent_attach_24_regular.xml | 9 +++ .../src/main/res/layout/display_item_file.xml | 59 +++++++++++++++++++ .../src/main/res/values-de-rDE/strings_sk.xml | 1 + mastodon/src/main/res/values/strings_sk.xml | 1 + 7 files changed, 152 insertions(+), 2 deletions(-) create mode 100644 mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/FileStatusDisplayItem.java create mode 100644 mastodon/src/main/res/drawable/bg_search_button.xml create mode 100644 mastodon/src/main/res/drawable/ic_fluent_attach_24_regular.xml create mode 100644 mastodon/src/main/res/layout/display_item_file.xml diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/FileStatusDisplayItem.java b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/FileStatusDisplayItem.java new file mode 100644 index 000000000..1d460a3e9 --- /dev/null +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/FileStatusDisplayItem.java @@ -0,0 +1,58 @@ +package org.joinmastodon.android.ui.displayitems; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.text.TextUtils; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import org.joinmastodon.android.R; +import org.joinmastodon.android.fragments.BaseStatusListFragment; +import org.joinmastodon.android.model.Attachment; +import org.joinmastodon.android.model.Card; +import org.joinmastodon.android.model.Status; +import org.joinmastodon.android.ui.drawables.BlurhashCrossfadeDrawable; +import org.joinmastodon.android.ui.utils.UiUtils; + +import me.grishka.appkit.imageloader.ImageLoaderViewHolder; + +public class FileStatusDisplayItem extends StatusDisplayItem{ + private final Attachment attachment; + + public FileStatusDisplayItem(String parentID, BaseStatusListFragment parentFragment, Attachment attachment) { + super(parentID, parentFragment); + this.attachment=attachment; + } + + @Override + public Type getType() { + return Type.FILE; + } + + public static class Holder extends StatusDisplayItem.Holder { + private final TextView title, domain; + + public Holder(Context context, ViewGroup parent) { + super(context, R.layout.display_item_file, parent); + title=findViewById(R.id.title); + domain=findViewById(R.id.domain); + findViewById(R.id.inner).setOnClickListener(this::onClick); + } + + @Override + public void onBind(FileStatusDisplayItem item) { + title.setText(item.attachment.description == null + ? title.getContext().getText(R.string.sk_attachment) + : item.attachment.description); + domain.setText(Uri.parse(item.attachment.url).getHost()); + } + + private void onClick(View v) { + UiUtils.openURL(itemView.getContext(), item.parentFragment.getAccountID(), item.attachment.url); + } + } +} + diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/StatusDisplayItem.java b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/StatusDisplayItem.java index 69d3786c8..8014853b0 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/StatusDisplayItem.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/StatusDisplayItem.java @@ -78,6 +78,7 @@ public abstract class StatusDisplayItem{ case EXTENDED_FOOTER -> new ExtendedFooterStatusDisplayItem.Holder(activity, parent); case MEDIA_GRID -> new MediaGridStatusDisplayItem.Holder(activity, parent); case WARNING -> new WarningFilteredStatusDisplayItem.Holder(activity, parent); + case FILE -> new FileStatusDisplayItem.Holder(activity, parent); }; } @@ -171,7 +172,10 @@ public abstract class StatusDisplayItem{ replyLine.needBottomPadding=true; else header.needBottomPadding=true; - List imageAttachments=statusForContent.mediaAttachments.stream().filter(att->att.type.isImage()).collect(Collectors.toList()); + + List imageAttachments=statusForContent.mediaAttachments.stream() + .filter(att->att.type.isImage() && !att.type.equals(Attachment.Type.UNKNOWN)) + .collect(Collectors.toList()); if(!imageAttachments.isEmpty()){ PhotoLayoutHelper.TiledLayoutResult layout=PhotoLayoutHelper.processThumbs(imageAttachments); items.add(new MediaGridStatusDisplayItem(parentID, fragment, layout, imageAttachments, statusForContent)); @@ -181,6 +185,12 @@ public abstract class StatusDisplayItem{ items.add(new AudioStatusDisplayItem(parentID, fragment, statusForContent, att)); } } + + statusForContent.mediaAttachments.stream() + .filter(att->att.type.equals(Attachment.Type.UNKNOWN)) + .map(att -> new FileStatusDisplayItem(parentID, fragment, att)) + .forEach(items::add); + if(statusForContent.poll!=null){ buildPollItems(parentID, fragment, statusForContent.poll, items); } @@ -230,7 +240,8 @@ public abstract class StatusDisplayItem{ GAP, EXTENDED_FOOTER, MEDIA_GRID, - WARNING + WARNING, + FILE } public static abstract class Holder extends BindableViewHolder implements UsableRecyclerView.DisableableClickable{ diff --git a/mastodon/src/main/res/drawable/bg_search_button.xml b/mastodon/src/main/res/drawable/bg_search_button.xml new file mode 100644 index 000000000..9b7d630ff --- /dev/null +++ b/mastodon/src/main/res/drawable/bg_search_button.xml @@ -0,0 +1,11 @@ + + + + + + + + + diff --git a/mastodon/src/main/res/drawable/ic_fluent_attach_24_regular.xml b/mastodon/src/main/res/drawable/ic_fluent_attach_24_regular.xml new file mode 100644 index 000000000..7c31688d8 --- /dev/null +++ b/mastodon/src/main/res/drawable/ic_fluent_attach_24_regular.xml @@ -0,0 +1,9 @@ + + + diff --git a/mastodon/src/main/res/layout/display_item_file.xml b/mastodon/src/main/res/layout/display_item_file.xml new file mode 100644 index 000000000..f206777ba --- /dev/null +++ b/mastodon/src/main/res/layout/display_item_file.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + diff --git a/mastodon/src/main/res/values-de-rDE/strings_sk.xml b/mastodon/src/main/res/values-de-rDE/strings_sk.xml index d828668c7..15d77d62c 100644 --- a/mastodon/src/main/res/values-de-rDE/strings_sk.xml +++ b/mastodon/src/main/res/values-de-rDE/strings_sk.xml @@ -274,4 +274,5 @@ Vor dem Teilen bestätigen hat reagiert hat mit %s reagiert + Anhang \ No newline at end of file diff --git a/mastodon/src/main/res/values/strings_sk.xml b/mastodon/src/main/res/values/strings_sk.xml index 6eae86635..cafbe7de6 100644 --- a/mastodon/src/main/res/values/strings_sk.xml +++ b/mastodon/src/main/res/values/strings_sk.xml @@ -286,4 +286,5 @@ Allows setting a content type like Markdown when creating a post. Keep in mind that not all instances support this. Default content type This lets you have a content type be pre-selected when creating new posts, overriding the value set in “Posting preferences”. + Attachment \ No newline at end of file From f933bdbc53ac97a639bcb09d5a5d4c250cdff4d2 Mon Sep 17 00:00:00 2001 From: sk Date: Sat, 27 May 2023 01:13:32 +0200 Subject: [PATCH 22/27] button with ripple for files and instance picker --- .../fragments/onboarding/CustomWelcomeFragment.java | 1 + mastodon/src/main/res/drawable/bg_search_button.xml | 7 +------ mastodon/src/main/res/layout/display_item_file.xml | 2 +- mastodon/src/main/res/layout/item_instance_custom.xml | 2 +- 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/CustomWelcomeFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/CustomWelcomeFragment.java index 32f721c3a..4132cfaf2 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/CustomWelcomeFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/onboarding/CustomWelcomeFragment.java @@ -124,6 +124,7 @@ public class CustomWelcomeFragment extends InstanceCatalogFragment { super.onViewCreated(view, savedInstanceState); view.setBackgroundColor(UiUtils.getThemeColor(getActivity(), R.attr.colorWindowBackground)); list.setItemAnimator(new BetterItemAnimator()); + ((UsableRecyclerView) list).setSelector(null); } @Override diff --git a/mastodon/src/main/res/drawable/bg_search_button.xml b/mastodon/src/main/res/drawable/bg_search_button.xml index 9b7d630ff..717bda7ce 100644 --- a/mastodon/src/main/res/drawable/bg_search_button.xml +++ b/mastodon/src/main/res/drawable/bg_search_button.xml @@ -2,10 +2,5 @@ - - - - - - + diff --git a/mastodon/src/main/res/layout/display_item_file.xml b/mastodon/src/main/res/layout/display_item_file.xml index f206777ba..e428abf26 100644 --- a/mastodon/src/main/res/layout/display_item_file.xml +++ b/mastodon/src/main/res/layout/display_item_file.xml @@ -16,7 +16,7 @@ android:layout_height="wrap_content" android:layout_marginHorizontal="16dp" android:layout_marginVertical="4dp" - android:background="@drawable/bg_search_field" + android:background="@drawable/bg_search_button" android:paddingVertical="12dp" android:orientation="horizontal"> diff --git a/mastodon/src/main/res/layout/item_instance_custom.xml b/mastodon/src/main/res/layout/item_instance_custom.xml index c8cf010f6..25a2058a3 100644 --- a/mastodon/src/main/res/layout/item_instance_custom.xml +++ b/mastodon/src/main/res/layout/item_instance_custom.xml @@ -8,7 +8,7 @@ + android:background="@drawable/bg_search_button"> Date: Sat, 27 May 2023 01:32:49 +0200 Subject: [PATCH 23/27] use remote url; file name as fallback for alt text --- .../ui/displayitems/FileStatusDisplayItem.java | 12 +++++++----- mastodon/src/main/res/layout/display_item_file.xml | 4 ++-- mastodon/src/main/res/values-de-rDE/strings_sk.xml | 1 - mastodon/src/main/res/values/strings_sk.xml | 1 - 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/FileStatusDisplayItem.java b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/FileStatusDisplayItem.java index 1d460a3e9..dd33e03ad 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/FileStatusDisplayItem.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/FileStatusDisplayItem.java @@ -44,14 +44,16 @@ public class FileStatusDisplayItem extends StatusDisplayItem{ @Override public void onBind(FileStatusDisplayItem item) { - title.setText(item.attachment.description == null - ? title.getContext().getText(R.string.sk_attachment) - : item.attachment.description); - domain.setText(Uri.parse(item.attachment.url).getHost()); + Uri url = Uri.parse(item.attachment.remoteUrl); + title.setText(item.attachment.description != null + ? item.attachment.description + : url.getLastPathSegment()); + title.setEllipsize(item.attachment.description != null ? TextUtils.TruncateAt.END : TextUtils.TruncateAt.MIDDLE); + domain.setText(url.getHost()); } private void onClick(View v) { - UiUtils.openURL(itemView.getContext(), item.parentFragment.getAccountID(), item.attachment.url); + UiUtils.openURL(itemView.getContext(), item.parentFragment.getAccountID(), item.attachment.remoteUrl); } } } diff --git a/mastodon/src/main/res/layout/display_item_file.xml b/mastodon/src/main/res/layout/display_item_file.xml index e428abf26..1001aeac6 100644 --- a/mastodon/src/main/res/layout/display_item_file.xml +++ b/mastodon/src/main/res/layout/display_item_file.xml @@ -42,8 +42,8 @@ android:layout_height="wrap_content" android:textAppearance="@style/m3_body_large" android:singleLine="true" - android:ellipsize="end" - tools:text="Link title"/> + android:ellipsize="middle" + tools:text="07a9d88625a9d63d680f35baf040156e71ed87e5f068f14d332c0c5a83f7a939.webm"/> Vor dem Teilen bestätigen hat reagiert hat mit %s reagiert - Anhang \ No newline at end of file diff --git a/mastodon/src/main/res/values/strings_sk.xml b/mastodon/src/main/res/values/strings_sk.xml index cafbe7de6..6eae86635 100644 --- a/mastodon/src/main/res/values/strings_sk.xml +++ b/mastodon/src/main/res/values/strings_sk.xml @@ -286,5 +286,4 @@ Allows setting a content type like Markdown when creating a post. Keep in mind that not all instances support this. Default content type This lets you have a content type be pre-selected when creating new posts, overriding the value set in “Posting preferences”. - Attachment \ No newline at end of file From a6ac68499cf0177fbffd82d8492c7aed5540eae4 Mon Sep 17 00:00:00 2001 From: sk Date: Sat, 27 May 2023 01:37:10 +0200 Subject: [PATCH 24/27] use url as fallback for remote url --- .../android/ui/displayitems/FileStatusDisplayItem.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/FileStatusDisplayItem.java b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/FileStatusDisplayItem.java index dd33e03ad..50e720a02 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/FileStatusDisplayItem.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/FileStatusDisplayItem.java @@ -44,7 +44,7 @@ public class FileStatusDisplayItem extends StatusDisplayItem{ @Override public void onBind(FileStatusDisplayItem item) { - Uri url = Uri.parse(item.attachment.remoteUrl); + Uri url = Uri.parse(getUrl()); title.setText(item.attachment.description != null ? item.attachment.description : url.getLastPathSegment()); @@ -53,7 +53,11 @@ public class FileStatusDisplayItem extends StatusDisplayItem{ } private void onClick(View v) { - UiUtils.openURL(itemView.getContext(), item.parentFragment.getAccountID(), item.attachment.remoteUrl); + UiUtils.openURL(itemView.getContext(), item.parentFragment.getAccountID(), getUrl()); + } + + private String getUrl() { + return item.attachment.remoteUrl == null ? item.attachment.url : item.attachment.remoteUrl; } } } From 69ddc95c2c1c594b5a60623e7f1c4885d5440845 Mon Sep 17 00:00:00 2001 From: LucasGGamerM Date: Fri, 28 Apr 2023 15:10:14 -0300 Subject: [PATCH 25/27] fix crash when notification markers are null This would happen when an account had 0 notifications and received one. After which, the user would tap on the notification icon on the tab bar and the app would crash. --- .../android/fragments/NotificationsListFragment.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/NotificationsListFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/NotificationsListFragment.java index 929aec242..18915c7e1 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/NotificationsListFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/NotificationsListFragment.java @@ -151,7 +151,7 @@ public class NotificationsListFragment extends BaseStatusListFragment Date: Sat, 27 May 2023 01:50:10 +0200 Subject: [PATCH 26/27] fix creating posts on other people's account timelines closes sk22#508 --- .../android/fragments/AccountTimelineFragment.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/AccountTimelineFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/AccountTimelineFragment.java index eb7106b47..ade79f8c9 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/AccountTimelineFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/AccountTimelineFragment.java @@ -85,7 +85,8 @@ public class AccountTimelineFragment extends StatusListFragment{ } protected void onStatusCreated(StatusCreatedEvent ev){ - if(!AccountSessionManager.getInstance().isSelf(accountID, ev.status.account)) + AccountSessionManager asm = AccountSessionManager.getInstance(); + if(!asm.isSelf(accountID, ev.status.account) || !asm.isSelf(accountID, user)) return; if(filter==GetAccountStatuses.Filter.PINNED) return; if(filter==GetAccountStatuses.Filter.DEFAULT){ From c38eb545b16a18bcb713a9639a49ce77b2e60307 Mon Sep 17 00:00:00 2001 From: sk Date: Sat, 27 May 2023 13:09:36 +0200 Subject: [PATCH 27/27] use matched filter for determining warning title fixes a bug where, when multiple filters apply, the WarningFilteredStatusDisplayItem would not check if the warning applies to the current context. now, matched filter is determined through the predicate (though not exactly what a predicate is supposed to do, i guess) and passed down to the WarningFilteredStatusDisplayItem. cc @LucasGGamerM --- .../ui/displayitems/StatusDisplayItem.java | 13 ++++++++----- .../WarningFilteredStatusDisplayItem.java | 7 +++++-- .../android/utils/StatusFilterPredicate.java | 16 +++++++++++++--- 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/StatusDisplayItem.java b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/StatusDisplayItem.java index 8014853b0..6932173bc 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/StatusDisplayItem.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/StatusDisplayItem.java @@ -95,10 +95,6 @@ public abstract class StatusDisplayItem{ args.putString("account", accountID); ScheduledStatus scheduledStatus = parentObject instanceof ScheduledStatus ? (ScheduledStatus) parentObject : null; - if (!statusForContent.filterRevealed) { - statusForContent.filterRevealed = new StatusFilterPredicate(accountID, filterContext, Filter.FilterAction.WARN).test(status); - } - ReblogOrReplyLineStatusDisplayItem replyLine = null; boolean threadReply = statusForContent.inReplyToAccountId != null && statusForContent.inReplyToAccountId.equals(statusForContent.account.id); @@ -206,8 +202,15 @@ public abstract class StatusDisplayItem{ item.index=i++; } + Filter applyingFilter = null; + if (!statusForContent.filterRevealed) { + StatusFilterPredicate predicate = new StatusFilterPredicate(accountID, filterContext, Filter.FilterAction.WARN); + statusForContent.filterRevealed = predicate.test(status); + applyingFilter = predicate.getApplyingFilter(); + } + ArrayList result = statusForContent.filterRevealed ? items : - new ArrayList<>(List.of(new WarningFilteredStatusDisplayItem(parentID, fragment, statusForContent, items))); + new ArrayList<>(List.of(new WarningFilteredStatusDisplayItem(parentID, fragment, statusForContent, items, applyingFilter))); if (addFooter && status.hasGapAfter && !(fragment instanceof ThreadFragment)) { StatusDisplayItem gap = new GapStatusDisplayItem(parentID, fragment); diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/WarningFilteredStatusDisplayItem.java b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/WarningFilteredStatusDisplayItem.java index fd1e91259..932a9bf10 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/WarningFilteredStatusDisplayItem.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/WarningFilteredStatusDisplayItem.java @@ -7,6 +7,7 @@ import android.widget.TextView; import org.joinmastodon.android.R; import org.joinmastodon.android.fragments.BaseStatusListFragment; +import org.joinmastodon.android.model.Filter; import org.joinmastodon.android.model.Status; import java.util.List; @@ -15,11 +16,13 @@ public class WarningFilteredStatusDisplayItem extends StatusDisplayItem{ public boolean loading; public final Status status; public List filteredItems; + public Filter applyingFilter; - public WarningFilteredStatusDisplayItem(String parentID, BaseStatusListFragment parentFragment, Status status, List filteredItems){ + public WarningFilteredStatusDisplayItem(String parentID, BaseStatusListFragment parentFragment, Status status, List filteredItems, Filter applyingFilter){ super(parentID, parentFragment); this.status=status; this.filteredItems = filteredItems; + this.applyingFilter = applyingFilter; } @Override @@ -41,7 +44,7 @@ public class WarningFilteredStatusDisplayItem extends StatusDisplayItem{ @Override public void onBind(WarningFilteredStatusDisplayItem item) { filteredItems = item.filteredItems; - text.setText(item.parentFragment.getString(R.string.sk_filtered, item.status.filtered.get(item.status.filtered.size() -1).filter.title)); + text.setText(item.parentFragment.getString(R.string.sk_filtered, item.applyingFilter.title)); } @Override diff --git a/mastodon/src/main/java/org/joinmastodon/android/utils/StatusFilterPredicate.java b/mastodon/src/main/java/org/joinmastodon/android/utils/StatusFilterPredicate.java index 8c73d3a87..0c96bb9c0 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/utils/StatusFilterPredicate.java +++ b/mastodon/src/main/java/org/joinmastodon/android/utils/StatusFilterPredicate.java @@ -6,6 +6,7 @@ import org.joinmastodon.android.model.Status; import java.time.Instant; import java.util.List; +import java.util.Optional; import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -14,6 +15,7 @@ public class StatusFilterPredicate implements Predicate{ private final List filters; private final Filter.FilterContext context; private final Filter.FilterAction action; + private Filter applyingFilter; /** * @param context null makes the predicate pass automatically @@ -58,18 +60,26 @@ public class StatusFilterPredicate implements Predicate{ public boolean test(Status status){ if (context == null) return true; - Stream stream = status.filtered != null + Stream matchingFilters = status.filtered != null // use server-provided per-status info (status.filtered) if available ? status.filtered.stream().map(f -> f.filter) // or fall back to cached filters : filters.stream().filter(filter -> filter.matches(status)); - return stream + Optional applyingFilter = matchingFilters // discard expired filters .filter(filter -> filter.expiresAt == null || filter.expiresAt.isAfter(Instant.now())) // only apply filters for given context .filter(filter -> filter.context.contains(context)) // treating filterAction = null (from filters list) as FilterAction.HIDE - .noneMatch(filter -> filter.filterAction == null ? action == Filter.FilterAction.HIDE : filter.filterAction == action); + .filter(filter -> filter.filterAction == null ? action == Filter.FilterAction.HIDE : filter.filterAction == action) + .findAny(); + + this.applyingFilter = applyingFilter.orElse(null); + return applyingFilter.isEmpty(); + } + + public Filter getApplyingFilter() { + return applyingFilter; } }