Filtered posts in timelines (AND-8)
This commit is contained in:
@@ -3,6 +3,7 @@ package org.joinmastodon.android.ui.displayitems;
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.LayerDrawable;
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
@@ -12,6 +13,7 @@ import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
||||
import org.joinmastodon.android.model.Status;
|
||||
import org.joinmastodon.android.ui.OutlineProviders;
|
||||
import org.joinmastodon.android.ui.drawables.SpoilerStripesDrawable;
|
||||
import org.joinmastodon.android.ui.drawables.TiledDrawable;
|
||||
import org.joinmastodon.android.ui.text.HtmlParser;
|
||||
import org.joinmastodon.android.ui.utils.CustomEmojiHelper;
|
||||
|
||||
@@ -25,18 +27,25 @@ public class SpoilerStatusDisplayItem extends StatusDisplayItem{
|
||||
public final ArrayList<StatusDisplayItem> contentItems=new ArrayList<>();
|
||||
private final CharSequence parsedTitle;
|
||||
private final CustomEmojiHelper emojiHelper;
|
||||
private final Type type;
|
||||
|
||||
public SpoilerStatusDisplayItem(String parentID, BaseStatusListFragment parentFragment, Status status){
|
||||
public SpoilerStatusDisplayItem(String parentID, BaseStatusListFragment parentFragment, String title, Status status, Type type){
|
||||
super(parentID, parentFragment);
|
||||
this.status=status;
|
||||
parsedTitle=HtmlParser.parseCustomEmoji(status.spoilerText, status.emojis);
|
||||
emojiHelper=new CustomEmojiHelper();
|
||||
emojiHelper.setText(parsedTitle);
|
||||
this.type=type;
|
||||
if(TextUtils.isEmpty(title)){
|
||||
parsedTitle=HtmlParser.parseCustomEmoji(status.spoilerText, status.emojis);
|
||||
emojiHelper=new CustomEmojiHelper();
|
||||
emojiHelper.setText(parsedTitle);
|
||||
}else{
|
||||
parsedTitle=title;
|
||||
emojiHelper=null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getImageCount(){
|
||||
return emojiHelper.getImageCount();
|
||||
return emojiHelper==null ? 0 : emojiHelper.getImageCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -46,14 +55,14 @@ public class SpoilerStatusDisplayItem extends StatusDisplayItem{
|
||||
|
||||
@Override
|
||||
public Type getType(){
|
||||
return Type.SPOILER;
|
||||
return type;
|
||||
}
|
||||
|
||||
public static class Holder extends StatusDisplayItem.Holder<SpoilerStatusDisplayItem> implements ImageLoaderViewHolder{
|
||||
private final TextView title, action;
|
||||
private final View button;
|
||||
|
||||
public Holder(Context context, ViewGroup parent){
|
||||
public Holder(Context context, ViewGroup parent, Type type){
|
||||
super(context, R.layout.display_item_spoiler, parent);
|
||||
title=findViewById(R.id.spoiler_title);
|
||||
action=findViewById(R.id.spoiler_action);
|
||||
@@ -62,8 +71,14 @@ public class SpoilerStatusDisplayItem extends StatusDisplayItem{
|
||||
button.setOutlineProvider(OutlineProviders.roundedRect(8));
|
||||
button.setClipToOutline(true);
|
||||
LayerDrawable spoilerBg=(LayerDrawable) button.getBackground().mutate();
|
||||
spoilerBg.setDrawableByLayerId(R.id.left_drawable, new SpoilerStripesDrawable(true));
|
||||
spoilerBg.setDrawableByLayerId(R.id.right_drawable, new SpoilerStripesDrawable(false));
|
||||
if(type==Type.SPOILER){
|
||||
spoilerBg.setDrawableByLayerId(R.id.left_drawable, new SpoilerStripesDrawable(true));
|
||||
spoilerBg.setDrawableByLayerId(R.id.right_drawable, new SpoilerStripesDrawable(false));
|
||||
}else if(type==Type.FILTER_SPOILER){
|
||||
Drawable texture=context.getDrawable(R.drawable.filter_banner_stripe_texture);
|
||||
spoilerBg.setDrawableByLayerId(R.id.left_drawable, new TiledDrawable(texture));
|
||||
spoilerBg.setDrawableByLayerId(R.id.right_drawable, new TiledDrawable(texture));
|
||||
}
|
||||
button.setBackground(spoilerBg);
|
||||
button.setOnClickListener(v->item.parentFragment.onRevealSpoilerClick(this));
|
||||
}
|
||||
|
||||
@@ -2,11 +2,11 @@ package org.joinmastodon.android.ui.displayitems;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import org.joinmastodon.android.GlobalUserPreferences;
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
||||
@@ -14,6 +14,7 @@ import org.joinmastodon.android.fragments.ThreadFragment;
|
||||
import org.joinmastodon.android.model.Account;
|
||||
import org.joinmastodon.android.model.Attachment;
|
||||
import org.joinmastodon.android.model.DisplayItemsParent;
|
||||
import org.joinmastodon.android.model.FilterResult;
|
||||
import org.joinmastodon.android.model.Poll;
|
||||
import org.joinmastodon.android.model.Status;
|
||||
import org.joinmastodon.android.ui.PhotoLayoutHelper;
|
||||
@@ -72,7 +73,7 @@ public abstract class StatusDisplayItem{
|
||||
case GAP -> new GapStatusDisplayItem.Holder(activity, parent);
|
||||
case EXTENDED_FOOTER -> new ExtendedFooterStatusDisplayItem.Holder(activity, parent);
|
||||
case MEDIA_GRID -> new MediaGridStatusDisplayItem.Holder(activity, parent);
|
||||
case SPOILER -> new SpoilerStatusDisplayItem.Holder(activity, parent);
|
||||
case SPOILER, FILTER_SPOILER -> new SpoilerStatusDisplayItem.Holder(activity, parent, type);
|
||||
case SECTION_HEADER -> new SectionHeaderStatusDisplayItem.Holder(activity, parent);
|
||||
case NOTIFICATION_HEADER -> new NotificationHeaderStatusDisplayItem.Holder(activity, parent);
|
||||
};
|
||||
@@ -106,9 +107,24 @@ public abstract class StatusDisplayItem{
|
||||
items.add(header=new HeaderStatusDisplayItem(parentID, statusForContent.account, statusForContent.createdAt, fragment, accountID, statusForContent, null));
|
||||
}
|
||||
|
||||
boolean filtered=false;
|
||||
if(status.filtered!=null){
|
||||
for(FilterResult filter:status.filtered){
|
||||
if(filter.filter.isActive()){
|
||||
filtered=true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ArrayList<StatusDisplayItem> contentItems;
|
||||
if(!TextUtils.isEmpty(statusForContent.spoilerText) && AccountSessionManager.get(accountID).getLocalPreferences().showCWs){
|
||||
SpoilerStatusDisplayItem spoilerItem=new SpoilerStatusDisplayItem(parentID, fragment, statusForContent);
|
||||
if(filtered){
|
||||
SpoilerStatusDisplayItem spoilerItem=new SpoilerStatusDisplayItem(parentID, fragment, fragment.getString(R.string.post_matches_filter_x, status.filtered.get(0).filter.title), statusForContent, Type.FILTER_SPOILER);
|
||||
items.add(spoilerItem);
|
||||
contentItems=spoilerItem.contentItems;
|
||||
statusForContent.spoilerRevealed=false;
|
||||
}else if(!TextUtils.isEmpty(statusForContent.spoilerText) && AccountSessionManager.get(accountID).getLocalPreferences().showCWs){
|
||||
SpoilerStatusDisplayItem spoilerItem=new SpoilerStatusDisplayItem(parentID, fragment, null, statusForContent, Type.SPOILER);
|
||||
items.add(spoilerItem);
|
||||
contentItems=spoilerItem.contentItems;
|
||||
}else{
|
||||
@@ -116,7 +132,11 @@ public abstract class StatusDisplayItem{
|
||||
}
|
||||
|
||||
if(!TextUtils.isEmpty(statusForContent.content)){
|
||||
TextStatusDisplayItem text=new TextStatusDisplayItem(parentID, HtmlParser.parse(statusForContent.content, statusForContent.emojis, statusForContent.mentions, statusForContent.tags, accountID), fragment, statusForContent);
|
||||
SpannableStringBuilder parsedText=HtmlParser.parse(statusForContent.content, statusForContent.emojis, statusForContent.mentions, statusForContent.tags, accountID);
|
||||
if(filtered){
|
||||
HtmlParser.applyFilterHighlights(fragment.getActivity(), parsedText, status.filtered);
|
||||
}
|
||||
TextStatusDisplayItem text=new TextStatusDisplayItem(parentID, parsedText, fragment, statusForContent);
|
||||
text.reduceTopPadding=header==null;
|
||||
contentItems.add(text);
|
||||
}else if(header!=null){
|
||||
@@ -192,7 +212,8 @@ public abstract class StatusDisplayItem{
|
||||
SPOILER,
|
||||
SECTION_HEADER,
|
||||
HEADER_CHECKABLE,
|
||||
NOTIFICATION_HEADER
|
||||
NOTIFICATION_HEADER,
|
||||
FILTER_SPOILER
|
||||
}
|
||||
|
||||
public static abstract class Holder<T extends StatusDisplayItem> extends BindableViewHolder<T> implements UsableRecyclerView.DisableableClickable{
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
package org.joinmastodon.android.ui.drawables;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.ColorFilter;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
public class TiledDrawable extends Drawable{
|
||||
private final Drawable drawable;
|
||||
|
||||
public TiledDrawable(Drawable drawable){
|
||||
this.drawable=drawable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(@NonNull Canvas canvas){
|
||||
Rect bounds=getBounds();
|
||||
canvas.save();
|
||||
canvas.clipRect(bounds);
|
||||
int w=drawable.getIntrinsicWidth();
|
||||
int h=drawable.getIntrinsicHeight();
|
||||
for(int y=bounds.top;y<bounds.bottom;y+=h){
|
||||
for(int x=bounds.left;x<bounds.right;x+=w){
|
||||
drawable.setBounds(x, y, x+w, y+h);
|
||||
drawable.draw(canvas);
|
||||
}
|
||||
}
|
||||
canvas.restore();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAlpha(int alpha){
|
||||
drawable.setAlpha(alpha);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setColorFilter(@Nullable ColorFilter colorFilter){
|
||||
drawable.setColorFilter(colorFilter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOpacity(){
|
||||
return drawable.getOpacity();
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,18 @@
|
||||
package org.joinmastodon.android.ui.text;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.Spanned;
|
||||
import android.text.TextUtils;
|
||||
import android.text.style.BackgroundColorSpan;
|
||||
import android.text.style.ForegroundColorSpan;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.twitter.twittertext.Regex;
|
||||
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.model.Emoji;
|
||||
import org.joinmastodon.android.model.FilterResult;
|
||||
import org.joinmastodon.android.model.Hashtag;
|
||||
import org.joinmastodon.android.model.Mention;
|
||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||
@@ -212,4 +217,22 @@ public class HtmlParser{
|
||||
}while(matcher.find()); // Find more URLs
|
||||
return ssb;
|
||||
}
|
||||
|
||||
public static void applyFilterHighlights(Context context, SpannableStringBuilder text, List<FilterResult> filters){
|
||||
int fgColor=UiUtils.getThemeColor(context, R.attr.colorM3Error);
|
||||
int bgColor=UiUtils.getThemeColor(context, R.attr.colorM3ErrorContainer);
|
||||
for(FilterResult filter:filters){
|
||||
if(!filter.filter.isActive())
|
||||
continue;;
|
||||
for(String word:filter.keywordMatches){
|
||||
Matcher matcher=Pattern.compile("\\b"+Pattern.quote(word)+"\\b", Pattern.CASE_INSENSITIVE).matcher(text);
|
||||
while(matcher.find()){
|
||||
ForegroundColorSpan fg=new ForegroundColorSpan(fgColor);
|
||||
BackgroundColorSpan bg=new BackgroundColorSpan(bgColor);
|
||||
text.setSpan(bg, matcher.start(), matcher.end(), 0);
|
||||
text.setSpan(fg, matcher.start(), matcher.end(), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user