From 28c851a63093981fe86efa76935128b32ed37fd3 Mon Sep 17 00:00:00 2001 From: FineFindus Date: Wed, 8 May 2024 16:43:33 +0200 Subject: [PATCH] refactor: remove StatusFilterPredicate Removes the deprecated StatusFilterPredicate class, as it has been replaced upstream. Client-side filters are now directly applied in the when building a StatusDisplayItem. --- .../utils/StatusFilterPredicateTest.java | 104 --------------- .../CustomLocalTimelineFragment.java | 6 +- .../android/model/AltTextFilter.java | 10 +- .../ui/displayitems/StatusDisplayItem.java | 28 ++-- .../android/utils/StatusFilterPredicate.java | 120 ------------------ 5 files changed, 25 insertions(+), 243 deletions(-) delete mode 100644 mastodon/src/androidTest/java/org/joinmastodon/android/utils/StatusFilterPredicateTest.java delete mode 100644 mastodon/src/main/java/org/joinmastodon/android/utils/StatusFilterPredicate.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 deleted file mode 100644 index 87235de8f..000000000 --- a/mastodon/src/androidTest/java/org/joinmastodon/android/utils/StatusFilterPredicateTest.java +++ /dev/null @@ -1,104 +0,0 @@ -package org.joinmastodon.android.utils; - -import static org.joinmastodon.android.model.FilterAction.*; -import static org.joinmastodon.android.model.FilterContext.*; -import static org.junit.Assert.*; - -import android.graphics.drawable.ColorDrawable; - -import org.joinmastodon.android.model.Attachment; -import org.joinmastodon.android.model.LegacyFilter; -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 LegacyFilter hideMeFilter = new LegacyFilter(), warnMeFilter = new LegacyFilter(); - 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()), - noAltText = Status.ofFake(null, "display me with a warning", Instant.now()), - withAltText = 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); - -// noAltText.mediaAttachments = Attachment.createFakeAttachments("fakeurl", new ColorDrawable()); -// withAltText.mediaAttachments = Attachment.createFakeAttachments("fakeurl", new ColorDrawable()); -// for (Attachment mediaAttachment : withAltText.mediaAttachments) { -// mediaAttachment.description = "Alt Text"; -// } - } - - @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)); - } - - @Test - public void testAltTextFilterNoPass() { - assertFalse("should not pass because of no alt text", - new StatusFilterPredicate(allFilters, HOME).test(noAltText)); - } - - @Test - public void testAltTextFilterPass() { - assertTrue("should pass because of alt text", - new StatusFilterPredicate(allFilters, HOME).test(withAltText)); - } -} \ No newline at end of file diff --git a/mastodon/src/main/java/org/joinmastodon/android/fragments/CustomLocalTimelineFragment.java b/mastodon/src/main/java/org/joinmastodon/android/fragments/CustomLocalTimelineFragment.java index 9933555ff..d742a1451 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/fragments/CustomLocalTimelineFragment.java +++ b/mastodon/src/main/java/org/joinmastodon/android/fragments/CustomLocalTimelineFragment.java @@ -7,16 +7,14 @@ import android.view.MenuInflater; import org.joinmastodon.android.R; import org.joinmastodon.android.api.requests.timelines.GetPublicTimeline; -import org.joinmastodon.android.model.Filter; +import org.joinmastodon.android.api.session.AccountSessionManager; import org.joinmastodon.android.model.FilterContext; import org.joinmastodon.android.model.Status; import org.joinmastodon.android.model.TimelineDefinition; import org.joinmastodon.android.ui.utils.UiUtils; import org.joinmastodon.android.utils.ProvidesAssistContent; -import org.joinmastodon.android.utils.StatusFilterPredicate; import java.util.List; -import java.util.stream.Collectors; import me.grishka.appkit.api.SimpleCallback; @@ -53,7 +51,7 @@ public class CustomLocalTimelineFragment extends PinnableStatusListFragment impl if(!result.isEmpty()) maxID=result.get(result.size()-1).id; if (getActivity() == null) return; - result=result.stream().filter(new StatusFilterPredicate(accountID, FilterContext.PUBLIC)).collect(Collectors.toList()); + AccountSessionManager.get(accountID).filterStatuses(result, FilterContext.PUBLIC); result.stream().forEach(status -> { status.account.acct += "@"+domain; status.mentions.forEach(mention -> mention.id = null); diff --git a/mastodon/src/main/java/org/joinmastodon/android/model/AltTextFilter.java b/mastodon/src/main/java/org/joinmastodon/android/model/AltTextFilter.java index 4168e6505..880f2ef62 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/model/AltTextFilter.java +++ b/mastodon/src/main/java/org/joinmastodon/android/model/AltTextFilter.java @@ -1,19 +1,25 @@ package org.joinmastodon.android.model; +import org.joinmastodon.android.GlobalUserPreferences; import org.jsoup.internal.StringUtil; import java.util.EnumSet; public class AltTextFilter extends LegacyFilter { - public AltTextFilter(FilterAction filterAction, FilterContext firstContext, FilterContext... restContexts) { + public AltTextFilter(FilterAction filterAction, EnumSet filterContexts) { this.filterAction = filterAction; isRemote = false; - context = EnumSet.of(firstContext, restContexts); + context = filterContexts; } @Override public boolean matches(Status status) { return status.getContentStatus().mediaAttachments.stream().map(attachment -> attachment.description).anyMatch(StringUtil::isBlank); } + + @Override + public boolean isActive(){ + return !GlobalUserPreferences.showPostsWithoutAlt; + } } 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 835eaba66..baf17d8f1 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 @@ -26,6 +26,7 @@ import org.joinmastodon.android.fragments.ProfileFragment; import org.joinmastodon.android.fragments.StatusListFragment; import org.joinmastodon.android.fragments.ThreadFragment; import org.joinmastodon.android.model.Account; +import org.joinmastodon.android.model.AltTextFilter; import org.joinmastodon.android.model.Attachment; import org.joinmastodon.android.model.DisplayItemsParent; import org.joinmastodon.android.model.FilterAction; @@ -40,11 +41,11 @@ import org.joinmastodon.android.ui.PhotoLayoutHelper; import org.joinmastodon.android.ui.text.HtmlParser; import org.joinmastodon.android.ui.utils.UiUtils; import org.joinmastodon.android.ui.viewholders.AccountViewHolder; -import org.joinmastodon.android.utils.StatusFilterPredicate; import org.parceler.Parcels; import java.util.ArrayList; import java.util.Collections; +import java.util.EnumSet; import java.util.List; import java.util.Map; import java.util.Optional; @@ -168,10 +169,6 @@ public abstract class StatusDisplayItem{ args.putString("account", accountID); ScheduledStatus scheduledStatus = parentObject instanceof ScheduledStatus s ? s : null; - // Hide statuses that have a filter action of hide - if(!new StatusFilterPredicate(accountID, filterContext, FilterAction.HIDE).test(status)) - return new ArrayList() ; - HeaderStatusDisplayItem header=null; boolean hideCounts=!AccountSessionManager.get(accountID).getLocalPreferences().showInteractionCounts; @@ -233,20 +230,25 @@ public abstract class StatusDisplayItem{ LegacyFilter applyingFilter=null; if(status.filtered!=null){ - for(FilterResult filter:status.filtered){ + List filters = status.filtered; + + //add a client side filter to filter posts that have no alt text + //it only applies when activated in the settings + AltTextFilter altTextFilter=new AltTextFilter(FilterAction.WARN, EnumSet.allOf(FilterContext.class)); + if(altTextFilter.matches(status)){ + FilterResult filterResult=new FilterResult(); + filterResult.filter=altTextFilter; + filterResult.keywordMatches=List.of(); + filters.add(filterResult); + } + + for(FilterResult filter:filters){ LegacyFilter f=filter.filter; if(f.isActive() && filterContext != null && f.context.contains(filterContext)){ applyingFilter=f; break; } } - - // Moshidon - if(applyingFilter==null){ - StatusFilterPredicate predicate = new StatusFilterPredicate(accountID, filterContext, FilterAction.WARN); - predicate.test(status); - applyingFilter = predicate.getApplyingFilter(); - } } ArrayList contentItems; diff --git a/mastodon/src/main/java/org/joinmastodon/android/utils/StatusFilterPredicate.java b/mastodon/src/main/java/org/joinmastodon/android/utils/StatusFilterPredicate.java deleted file mode 100644 index 2529dcf64..000000000 --- a/mastodon/src/main/java/org/joinmastodon/android/utils/StatusFilterPredicate.java +++ /dev/null @@ -1,120 +0,0 @@ -package org.joinmastodon.android.utils; - -import static org.joinmastodon.android.model.FilterAction.HIDE; -import static org.joinmastodon.android.model.FilterAction.WARN; -import static org.joinmastodon.android.model.FilterContext.ACCOUNT; -import static org.joinmastodon.android.model.FilterContext.HOME; -import static org.joinmastodon.android.model.FilterContext.NOTIFICATIONS; -import static org.joinmastodon.android.model.FilterContext.PUBLIC; -import static org.joinmastodon.android.model.FilterContext.THREAD; - -import org.joinmastodon.android.GlobalUserPreferences; -import org.joinmastodon.android.api.session.AccountSessionManager; -import org.joinmastodon.android.model.AltTextFilter; -import org.joinmastodon.android.model.LegacyFilter; -import org.joinmastodon.android.model.FilterAction; -import org.joinmastodon.android.model.FilterContext; -import org.joinmastodon.android.model.Status; - -import java.time.Instant; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import java.util.function.Predicate; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -// TODO: This whole class has been ditched upstream. I plan to eventually refactor it to only have the still relevant clientFilters code - -public class StatusFilterPredicate implements Predicate{ - private final List clientFilters; - private final List filters; - private final FilterContext context; - private final FilterAction action; - private LegacyFilter applyingFilter; - - /** - * @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, FilterContext context, FilterAction action){ - this.filters = filters; - this.context = context; - this.action = action; - this.clientFilters = getClientFilters(); - } - - public StatusFilterPredicate(List filters, FilterContext context){ - this(filters, context, 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, FilterContext context, FilterAction action){ - filters=AccountSessionManager.getInstance().getAccount(accountID).wordFilters.stream().filter(f->f.context.contains(context)).collect(Collectors.toList()); - this.context = context; - this.action = action; - this.clientFilters = getClientFilters(); - } - - private List getClientFilters() { - List filters = new ArrayList<>(); - if(!GlobalUserPreferences.showPostsWithoutAlt) { - filters.add(new AltTextFilter(WARN, HOME, PUBLIC, ACCOUNT, THREAD, NOTIFICATIONS)); - } - return filters; - } - - /** - * @param context null makes the predicate pass automatically - */ - public StatusFilterPredicate(String accountID, FilterContext context){ - this(accountID, context, HIDE); - } - - /** - * @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 - */ - @Override - public boolean test(Status status){ - if (context == null) return true; - - 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)); - - 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 - .filter(filter -> filter.filterAction == null ? action == HIDE : filter.filterAction == action) - .findAny(); - - //Apply client filters if no server filter is triggered - if (applyingFilter.isEmpty() && !clientFilters.isEmpty()) { - applyingFilter = clientFilters.stream() - .filter(filter -> filter.context.contains(context)) - .filter(filter -> filter.filterAction == null ? action == HIDE : filter.filterAction == action) - .filter(filter -> filter.matches(status)) - .findAny(); - } - - this.applyingFilter = applyingFilter.orElse(null); - return applyingFilter.isEmpty(); - } - - public LegacyFilter getApplyingFilter() { - return applyingFilter; - } -}