diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/ErrorStatusDisplayItem.java b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/ErrorStatusDisplayItem.java new file mode 100644 index 000000000..7e8fea609 --- /dev/null +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/displayitems/ErrorStatusDisplayItem.java @@ -0,0 +1,50 @@ +package org.joinmastodon.android.ui.displayitems; + +import android.content.Context; +import android.net.Uri; +import android.text.TextUtils; +import android.view.View; +import android.view.ViewGroup; +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.ui.utils.UiUtils; + +public class ErrorStatusDisplayItem extends StatusDisplayItem{ + private final Exception exception; + + public ErrorStatusDisplayItem(String parentID, BaseStatusListFragment parentFragment, Exception exception) { + super(parentID, parentFragment); + this.exception=exception; + } + + @Override + public Type getType() { + return Type.ERROR_ITEM; + } + + 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(ErrorStatusDisplayItem item) { + title.setText(item.exception.getMessage()); +// 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(), getUrl()); + } + } +} + 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 9ac9d86b4..f6b6f5ca4 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 @@ -139,6 +139,7 @@ public abstract class StatusDisplayItem{ case SPOILER, FILTER_SPOILER -> new SpoilerStatusDisplayItem.Holder(activity, parent, type); case SECTION_HEADER -> null; // new SectionHeaderStatusDisplayItem.Holder(activity, parent); case NOTIFICATION_HEADER -> new NotificationHeaderStatusDisplayItem.Holder(activity, parent); + case ERROR_ITEM -> new ErrorStatusDisplayItem.Holder(activity, parent); case DUMMY -> new DummyStatusDisplayItem.Holder(activity); }; } @@ -164,201 +165,192 @@ public abstract class StatusDisplayItem{ Status statusForContent=status.getContentStatus(); Bundle args=new Bundle(); args.putString("account", accountID); - ScheduledStatus scheduledStatus = parentObject instanceof ScheduledStatus s ? s : null; + try{ + ScheduledStatus scheduledStatus=parentObject instanceof ScheduledStatus s ? s : null; - HeaderStatusDisplayItem header=null; - boolean hideCounts=!AccountSessionManager.get(accountID).getLocalPreferences().showInteractionCounts; + HeaderStatusDisplayItem header=null; + boolean hideCounts=!AccountSessionManager.get(accountID).getLocalPreferences().showInteractionCounts; - if((flags & FLAG_NO_HEADER)==0){ - ReblogOrReplyLineStatusDisplayItem replyLine = null; - boolean threadReply = statusForContent.inReplyToAccountId != null && - statusForContent.inReplyToAccountId.equals(statusForContent.account.id); + if((flags&FLAG_NO_HEADER)==0){ + ReblogOrReplyLineStatusDisplayItem replyLine=null; + boolean threadReply=statusForContent.inReplyToAccountId!=null && + statusForContent.inReplyToAccountId.equals(statusForContent.account.id); - if(statusForContent.inReplyToAccountId!=null && !(threadReply && fragment instanceof ThreadFragment)){ - Account account = knownAccounts.get(statusForContent.inReplyToAccountId); - replyLine = buildReplyLine(fragment, status, accountID, parentObject, account, threadReply); + if(statusForContent.inReplyToAccountId!=null && !(threadReply && fragment instanceof ThreadFragment)){ + Account account=knownAccounts.get(statusForContent.inReplyToAccountId); + replyLine=buildReplyLine(fragment, status, accountID, parentObject, account, threadReply); + } + + if(status.reblog!=null){ + boolean isOwnPost=AccountSessionManager.getInstance().isSelf(fragment.getAccountID(), status.account); + + statusForContent.rebloggedBy=status.account; + + String text=fragment.getString(R.string.user_boosted, status.account.getDisplayName()); + items.add(new ReblogOrReplyLineStatusDisplayItem(parentID, fragment, text, status.account.emojis, R.drawable.ic_fluent_arrow_repeat_all_20sp_filled, isOwnPost ? status.visibility : null, i->{ + args.putParcelable("profileAccount", Parcels.wrap(status.account)); + Nav.go(fragment.getActivity(), ProfileFragment.class, args); + }, null, status, status.account)); + }else if(!(status.tags.isEmpty() || + fragment instanceof HashtagTimelineFragment || + fragment instanceof ListTimelineFragment + ) && fragment.getParentFragment() instanceof HomeTabFragment home){ + home.getHashtags().stream() + .filter(followed->status.tags.stream() + .anyMatch(hashtag->followed.name.equalsIgnoreCase(hashtag.name))) + .findAny() + // post contains a hashtag the user is following + .ifPresent(hashtag->items.add(new ReblogOrReplyLineStatusDisplayItem( + parentID, fragment, hashtag.name, List.of(), + R.drawable.ic_fluent_number_symbol_20sp_filled, null, + i->UiUtils.openHashtagTimeline(fragment.getActivity(), accountID, hashtag), + status + ))); + } + + if(replyLine!=null){ + Optional primaryLine=items.stream() + .filter(i->i instanceof ReblogOrReplyLineStatusDisplayItem) + .map(ReblogOrReplyLineStatusDisplayItem.class::cast) + .findFirst(); + + if(primaryLine.isPresent()){ + primaryLine.get().extra=replyLine; + }else{ + items.add(replyLine); + } + } + + if((flags&FLAG_CHECKABLE)!=0) + items.add(header=new CheckableHeaderStatusDisplayItem(parentID, statusForContent.account, statusForContent.createdAt, fragment, accountID, statusForContent, null)); + else + items.add(header=new HeaderStatusDisplayItem(parentID, statusForContent.account, statusForContent.createdAt, fragment, accountID, statusForContent, null, parentObject instanceof Notification n ? n : null, scheduledStatus)); } - if(status.reblog!=null){ - boolean isOwnPost = AccountSessionManager.getInstance().isSelf(fragment.getAccountID(), status.account); + LegacyFilter applyingFilter=null; + if(status.filtered!=null){ + List filters=status.filtered; - statusForContent.rebloggedBy = status.account; + // Only add client filters if there are no pre-existing status filter + if(filters.isEmpty()) + filters.addAll(AccountSessionManager.get(accountID).getClientSideFilters(status)); - String text=fragment.getString(R.string.user_boosted, status.account.getDisplayName()); - items.add(new ReblogOrReplyLineStatusDisplayItem(parentID, fragment, text, status.account.emojis, R.drawable.ic_fluent_arrow_repeat_all_20sp_filled, isOwnPost ? status.visibility : null, i->{ - args.putParcelable("profileAccount", Parcels.wrap(status.account)); - Nav.go(fragment.getActivity(), ProfileFragment.class, args); - }, null, status, status.account)); - } else if (!(status.tags.isEmpty() || - fragment instanceof HashtagTimelineFragment || - fragment instanceof ListTimelineFragment - ) && fragment.getParentFragment() instanceof HomeTabFragment home) { - home.getHashtags().stream() - .filter(followed -> status.tags.stream() - .anyMatch(hashtag -> followed.name.equalsIgnoreCase(hashtag.name))) - .findAny() - // post contains a hashtag the user is following - .ifPresent(hashtag -> items.add(new ReblogOrReplyLineStatusDisplayItem( - parentID, fragment, hashtag.name, List.of(), - R.drawable.ic_fluent_number_symbol_20sp_filled, null, - i->UiUtils.openHashtagTimeline(fragment.getActivity(), accountID, hashtag), - status - ))); - } - - if (replyLine != null) { - Optional primaryLine = items.stream() - .filter(i -> i instanceof ReblogOrReplyLineStatusDisplayItem) - .map(ReblogOrReplyLineStatusDisplayItem.class::cast) - .findFirst(); - - if (primaryLine.isPresent()) { - primaryLine.get().extra = replyLine; - } else { - items.add(replyLine); + for(FilterResult filter : filters){ + LegacyFilter f=filter.filter; + if(f.isActive() && filterContext!=null && f.context.contains(filterContext)){ + applyingFilter=f; + break; + } } } - if((flags & FLAG_CHECKABLE)!=0) - items.add(header=new CheckableHeaderStatusDisplayItem(parentID, statusForContent.account, statusForContent.createdAt, fragment, accountID, statusForContent, null)); - else - items.add(header=new HeaderStatusDisplayItem(parentID, statusForContent.account, statusForContent.createdAt, fragment, accountID, statusForContent, null, parentObject instanceof Notification n ? n : null, scheduledStatus)); - } - - LegacyFilter applyingFilter=null; - if(status.filtered!=null){ - List filters = status.filtered; - - // Only add client filters if there are no pre-existing status filter - if(filters.isEmpty()) - filters.addAll(AccountSessionManager.get(accountID).getClientSideFilters(status)); - - for(FilterResult filter:filters){ - LegacyFilter f=filter.filter; - if(f.isActive() && filterContext != null && f.context.contains(filterContext)){ - applyingFilter=f; - break; + ArrayList contentItems; + if(statusForContent.hasSpoiler()){ + if(AccountSessionManager.get(accountID).getLocalPreferences().revealCWs) statusForContent.spoilerRevealed=true; + SpoilerStatusDisplayItem spoilerItem=new SpoilerStatusDisplayItem(parentID, fragment, null, statusForContent, Type.SPOILER); + if((flags&FLAG_IS_FOR_QUOTE)!=0){ + for(StatusDisplayItem item : spoilerItem.contentItems){ + item.isForQuote=true; + } } + items.add(spoilerItem); + contentItems=spoilerItem.contentItems; + }else{ + contentItems=items; } - } - ArrayList contentItems; - if(statusForContent.hasSpoiler()){ - if (AccountSessionManager.get(accountID).getLocalPreferences().revealCWs) statusForContent.spoilerRevealed = true; - SpoilerStatusDisplayItem spoilerItem=new SpoilerStatusDisplayItem(parentID, fragment, null, statusForContent, Type.SPOILER); - if((flags & FLAG_IS_FOR_QUOTE)!=0){ - for(StatusDisplayItem item:spoilerItem.contentItems){ - item.isForQuote=true; - } + if(statusForContent.quote!=null){ + int quoteInlineIndex=statusForContent.content.lastIndexOf("

RE:"); + if(quoteInlineIndex!=-1) + statusForContent.content=statusForContent.content.substring(0, quoteInlineIndex); } - items.add(spoilerItem); - contentItems=spoilerItem.contentItems; - }else{ - contentItems=items; - } - if(statusForContent.quote!=null) { - int quoteInlineIndex=statusForContent.content.lastIndexOf("

RE:"); - if (quoteInlineIndex!=-1) - statusForContent.content=statusForContent.content.substring(0, quoteInlineIndex); - } - - boolean hasSpoiler=!TextUtils.isEmpty(statusForContent.spoilerText); - if(!TextUtils.isEmpty(statusForContent.content)){ - SpannableStringBuilder parsedText=HtmlParser.parse(statusForContent.content, statusForContent.emojis, statusForContent.mentions, statusForContent.tags, accountID, fragment.getContext()); - if(applyingFilter!=null) - HtmlParser.applyFilterHighlights(fragment.getActivity(), parsedText, status.filtered); - TextStatusDisplayItem text=new TextStatusDisplayItem(parentID, parsedText, fragment, statusForContent, (flags & FLAG_NO_TRANSLATE) != 0); - contentItems.add(text); - }else if(!hasSpoiler && header!=null){ - header.needBottomPadding=true; - }else if(hasSpoiler){ - contentItems.add(new DummyStatusDisplayItem(parentID, fragment)); - } - - List imageAttachments=statusForContent.mediaAttachments.stream().filter(att->att.type.isImage()).collect(Collectors.toList()); - if(!imageAttachments.isEmpty() && (flags & FLAG_NO_MEDIA_PREVIEW)==0){ - int color = UiUtils.getThemeColor(fragment.getContext(), R.attr.colorM3SurfaceVariant); - for (Attachment att : imageAttachments) { - if (att.blurhashPlaceholder == null) { - att.blurhashPlaceholder = new ColorDrawable(color); - } - } - PhotoLayoutHelper.TiledLayoutResult layout=PhotoLayoutHelper.processThumbs(imageAttachments); - MediaGridStatusDisplayItem mediaGrid=new MediaGridStatusDisplayItem(parentID, fragment, layout, imageAttachments, statusForContent); - if((flags & FLAG_MEDIA_FORCE_HIDDEN)!=0){ - mediaGrid.sensitiveTitle=fragment.getString(R.string.media_hidden); - statusForContent.sensitiveRevealed=false; - statusForContent.sensitive=true; - } else if(statusForContent.sensitive && AccountSessionManager.get(accountID).getLocalPreferences().revealCWs && !AccountSessionManager.get(accountID).getLocalPreferences().hideSensitiveMedia) - statusForContent.sensitiveRevealed=true; - contentItems.add(mediaGrid); - } - if((flags & FLAG_NO_MEDIA_PREVIEW)!=0){ - contentItems.add(new PreviewlessMediaGridStatusDisplayItem(parentID, fragment, null, imageAttachments, statusForContent)); - - } - for(Attachment att:statusForContent.mediaAttachments){ - if(att.type==Attachment.Type.AUDIO){ - contentItems.add(new AudioStatusDisplayItem(parentID, fragment, statusForContent, att)); - } - if(att.type==Attachment.Type.UNKNOWN){ - contentItems.add(new FileStatusDisplayItem(parentID, fragment, att)); - } - } - if(statusForContent.poll!=null){ - buildPollItems(parentID, fragment, statusForContent.poll, status, contentItems); - } - if(statusForContent.card!=null && statusForContent.mediaAttachments.isEmpty() && statusForContent.quote==null && !statusForContent.card.isHashtagUrl(statusForContent.url)){ - contentItems.add(new LinkCardStatusDisplayItem(parentID, fragment, statusForContent, (flags & FLAG_NO_MEDIA_PREVIEW)==0)); - } - if(statusForContent.quote!=null && !(parentObject instanceof Notification)){ - if(!statusForContent.mediaAttachments.isEmpty() && statusForContent.poll==null) // add spacing if immediately preceded by attachment + boolean hasSpoiler=!TextUtils.isEmpty(statusForContent.spoilerText); + if(!TextUtils.isEmpty(statusForContent.content)){ + SpannableStringBuilder parsedText=HtmlParser.parse(statusForContent.content, statusForContent.emojis, statusForContent.mentions, statusForContent.tags, accountID, fragment.getContext()); + if(applyingFilter!=null) + HtmlParser.applyFilterHighlights(fragment.getActivity(), parsedText, status.filtered); + TextStatusDisplayItem text=new TextStatusDisplayItem(parentID, parsedText, fragment, statusForContent, (flags&FLAG_NO_TRANSLATE)!=0); + contentItems.add(text); + }else if(!hasSpoiler && header!=null){ + header.needBottomPadding=true; + }else if(hasSpoiler){ contentItems.add(new DummyStatusDisplayItem(parentID, fragment)); - contentItems.addAll(buildItems(fragment, statusForContent.quote, accountID, parentObject, knownAccounts, filterContext, FLAG_NO_FOOTER | FLAG_INSET | FLAG_NO_EMOJI_REACTIONS | FLAG_IS_FOR_QUOTE)); - } - if(contentItems!=items && statusForContent.spoilerRevealed){ - items.addAll(contentItems); - } - AccountLocalPreferences lp=fragment.getLocalPrefs(); - if((flags & FLAG_NO_EMOJI_REACTIONS)==0 && !status.preview && lp.emojiReactionsEnabled && - (lp.showEmojiReactions!=ONLY_OPENED || fragment instanceof ThreadFragment) && - statusForContent.reactions!=null){ - boolean isMainStatus=fragment instanceof ThreadFragment t && t.getMainStatus().id.equals(statusForContent.id); - boolean showAddButton=lp.showEmojiReactions==ALWAYS || isMainStatus; - items.add(new EmojiReactionsStatusDisplayItem(parentID, fragment, statusForContent, accountID, !showAddButton, false)); - } - FooterStatusDisplayItem footer=null; - if((flags & FLAG_NO_FOOTER)==0){ - footer=new FooterStatusDisplayItem(parentID, fragment, statusForContent, accountID); - footer.hideCounts=hideCounts; - items.add(footer); - } - boolean inset=(flags & FLAG_INSET)!=0; - boolean isForQuote=(flags & FLAG_IS_FOR_QUOTE)!=0; - // add inset dummy so last content item doesn't clip out of inset bounds - if((inset || footer==null) && (flags & FLAG_CHECKABLE)==0 && !isForQuote){ - items.add(new DummyStatusDisplayItem(parentID, fragment)); - // in case we ever need the dummy to display a margin for the media grid again: - // (i forgot why we apparently don't need this anymore) - // !contentItems.isEmpty() && contentItems - // .get(contentItems.size() - 1) instanceof MediaGridStatusDisplayItem)); - } - GapStatusDisplayItem gap=null; - if((flags & FLAG_NO_FOOTER)==0 && status.hasGapAfter!=null && !(fragment instanceof ThreadFragment)) - items.add(gap=new GapStatusDisplayItem(parentID, fragment, status)); - int i=1; - for(StatusDisplayItem item:items){ - if(inset) - item.inset=true; - if(isForQuote){ - item.status=statusForContent; - item.isForQuote=true; } - item.index=i++; - } - if(items!=contentItems && !statusForContent.spoilerRevealed){ - for(StatusDisplayItem item:contentItems){ + + List imageAttachments=statusForContent.mediaAttachments.stream().filter(att->att.type.isImage()).collect(Collectors.toList()); + if(!imageAttachments.isEmpty() && (flags&FLAG_NO_MEDIA_PREVIEW)==0){ + int color=UiUtils.getThemeColor(fragment.getContext(), R.attr.colorM3SurfaceVariant); + for(Attachment att : imageAttachments){ + if(att.blurhashPlaceholder==null){ + att.blurhashPlaceholder=new ColorDrawable(color); + } + } + PhotoLayoutHelper.TiledLayoutResult layout=PhotoLayoutHelper.processThumbs(imageAttachments); + MediaGridStatusDisplayItem mediaGrid=new MediaGridStatusDisplayItem(parentID, fragment, layout, imageAttachments, statusForContent); + if((flags&FLAG_MEDIA_FORCE_HIDDEN)!=0){ + mediaGrid.sensitiveTitle=fragment.getString(R.string.media_hidden); + statusForContent.sensitiveRevealed=false; + statusForContent.sensitive=true; + }else if(statusForContent.sensitive && AccountSessionManager.get(accountID).getLocalPreferences().revealCWs && !AccountSessionManager.get(accountID).getLocalPreferences().hideSensitiveMedia) + statusForContent.sensitiveRevealed=true; + contentItems.add(mediaGrid); + } + if((flags&FLAG_NO_MEDIA_PREVIEW)!=0){ + contentItems.add(new PreviewlessMediaGridStatusDisplayItem(parentID, fragment, null, imageAttachments, statusForContent)); + + } + for(Attachment att : statusForContent.mediaAttachments){ + if(att.type==Attachment.Type.AUDIO){ + contentItems.add(new AudioStatusDisplayItem(parentID, fragment, statusForContent, att)); + } + if(att.type==Attachment.Type.UNKNOWN){ + contentItems.add(new FileStatusDisplayItem(parentID, fragment, att)); + } + } + if(statusForContent.poll!=null){ + buildPollItems(parentID, fragment, statusForContent.poll, status, contentItems); + } + if(statusForContent.card!=null && statusForContent.mediaAttachments.isEmpty() && statusForContent.quote==null && !statusForContent.card.isHashtagUrl(statusForContent.url)){ + contentItems.add(new LinkCardStatusDisplayItem(parentID, fragment, statusForContent, (flags&FLAG_NO_MEDIA_PREVIEW)==0)); + } + if(statusForContent.quote!=null && !(parentObject instanceof Notification)){ + if(!statusForContent.mediaAttachments.isEmpty() && statusForContent.poll==null) // add spacing if immediately preceded by attachment + contentItems.add(new DummyStatusDisplayItem(parentID, fragment)); + contentItems.addAll(buildItems(fragment, statusForContent.quote, accountID, parentObject, knownAccounts, filterContext, FLAG_NO_FOOTER|FLAG_INSET|FLAG_NO_EMOJI_REACTIONS|FLAG_IS_FOR_QUOTE)); + } + if(contentItems!=items && statusForContent.spoilerRevealed){ + items.addAll(contentItems); + } + AccountLocalPreferences lp=fragment.getLocalPrefs(); + if((flags&FLAG_NO_EMOJI_REACTIONS)==0 && !status.preview && lp.emojiReactionsEnabled && + (lp.showEmojiReactions!=ONLY_OPENED || fragment instanceof ThreadFragment) && + statusForContent.reactions!=null){ + boolean isMainStatus=fragment instanceof ThreadFragment t && t.getMainStatus().id.equals(statusForContent.id); + boolean showAddButton=lp.showEmojiReactions==ALWAYS || isMainStatus; + items.add(new EmojiReactionsStatusDisplayItem(parentID, fragment, statusForContent, accountID, !showAddButton, false)); + } + FooterStatusDisplayItem footer=null; + if((flags&FLAG_NO_FOOTER)==0){ + footer=new FooterStatusDisplayItem(parentID, fragment, statusForContent, accountID); + footer.hideCounts=hideCounts; + items.add(footer); + } + boolean inset=(flags&FLAG_INSET)!=0; + boolean isForQuote=(flags&FLAG_IS_FOR_QUOTE)!=0; + // add inset dummy so last content item doesn't clip out of inset bounds + if((inset || footer==null) && (flags&FLAG_CHECKABLE)==0 && !isForQuote){ + items.add(new DummyStatusDisplayItem(parentID, fragment)); + // in case we ever need the dummy to display a margin for the media grid again: + // (i forgot why we apparently don't need this anymore) + // !contentItems.isEmpty() && contentItems + // .get(contentItems.size() - 1) instanceof MediaGridStatusDisplayItem)); + } + GapStatusDisplayItem gap=null; + if((flags&FLAG_NO_FOOTER)==0 && status.hasGapAfter!=null && !(fragment instanceof ThreadFragment)) + items.add(gap=new GapStatusDisplayItem(parentID, fragment, status)); + int i=1; + for(StatusDisplayItem item : items){ if(inset) item.inset=true; if(isForQuote){ @@ -367,15 +359,28 @@ public abstract class StatusDisplayItem{ } item.index=i++; } - } + if(items!=contentItems && !statusForContent.spoilerRevealed){ + for(StatusDisplayItem item : contentItems){ + if(inset) + item.inset=true; + if(isForQuote){ + item.status=statusForContent; + item.isForQuote=true; + } + item.index=i++; + } + } - List nonGapItems=gap!=null ? items.subList(0, items.size()-1) : items; - WarningFilteredStatusDisplayItem warning=applyingFilter==null ? null : - new WarningFilteredStatusDisplayItem(parentID, fragment, statusForContent, nonGapItems, applyingFilter); - return applyingFilter==null ? items : new ArrayList<>(gap!=null - ? List.of(warning, gap) - : Collections.singletonList(warning) - ); + List nonGapItems=gap!=null ? items.subList(0, items.size()-1) : items; + WarningFilteredStatusDisplayItem warning=applyingFilter==null ? null : + new WarningFilteredStatusDisplayItem(parentID, fragment, statusForContent, nonGapItems, applyingFilter); + return applyingFilter==null ? items : new ArrayList<>(gap!=null + ? List.of(warning, gap) + : Collections.singletonList(warning) + ); + } catch(Exception e) { + return new ArrayList<>(Collections.singletonList(new ErrorStatusDisplayItem(parentID, fragment, e))); + } } public static void buildPollItems(String parentID, BaseStatusListFragment fragment, Poll poll, Status status, List items){ @@ -411,6 +416,7 @@ public abstract class StatusDisplayItem{ SECTION_HEADER, HEADER_CHECKABLE, NOTIFICATION_HEADER, + ERROR_ITEM, FILTER_SPOILER, DUMMY }