mirror of https://github.com/sk22/megalodon.git
468 lines
20 KiB
Java
468 lines
20 KiB
Java
package org.joinmastodon.android.ui.displayitems;
|
|
|
|
import static org.joinmastodon.android.api.session.AccountLocalPreferences.ShowEmojiReactions.ALWAYS;
|
|
import static org.joinmastodon.android.api.session.AccountLocalPreferences.ShowEmojiReactions.ONLY_OPENED;
|
|
|
|
import android.app.Activity;
|
|
import android.app.Fragment;
|
|
import android.content.Context;
|
|
import android.graphics.drawable.ColorDrawable;
|
|
import android.os.Bundle;
|
|
import android.text.SpannableStringBuilder;
|
|
import android.text.TextUtils;
|
|
import android.view.View;
|
|
import android.view.ViewGroup;
|
|
|
|
import androidx.annotation.NonNull;
|
|
|
|
import org.joinmastodon.android.R;
|
|
import org.joinmastodon.android.api.session.AccountLocalPreferences;
|
|
import org.joinmastodon.android.api.session.AccountSessionManager;
|
|
import org.joinmastodon.android.fragments.BaseStatusListFragment;
|
|
import org.joinmastodon.android.fragments.HashtagTimelineFragment;
|
|
import org.joinmastodon.android.fragments.HomeTabFragment;
|
|
import org.joinmastodon.android.fragments.ListTimelineFragment;
|
|
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.Attachment;
|
|
import org.joinmastodon.android.model.DisplayItemsParent;
|
|
import org.joinmastodon.android.model.LegacyFilter;
|
|
import org.joinmastodon.android.model.FilterContext;
|
|
import org.joinmastodon.android.model.FilterResult;
|
|
import org.joinmastodon.android.model.Notification;
|
|
import org.joinmastodon.android.model.Poll;
|
|
import org.joinmastodon.android.model.ScheduledStatus;
|
|
import org.joinmastodon.android.model.Status;
|
|
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.parceler.Parcels;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.Collections;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Optional;
|
|
import java.util.function.Predicate;
|
|
import java.util.stream.Collectors;
|
|
|
|
import me.grishka.appkit.Nav;
|
|
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
|
|
import me.grishka.appkit.utils.BindableViewHolder;
|
|
import me.grishka.appkit.views.UsableRecyclerView;
|
|
|
|
public abstract class StatusDisplayItem{
|
|
public final String parentID;
|
|
public final BaseStatusListFragment<?> parentFragment;
|
|
public Status status;
|
|
public boolean inset;
|
|
public int index;
|
|
public boolean
|
|
hasDescendantNeighbor=false,
|
|
hasAncestoringNeighbor=false,
|
|
isMainStatus=true,
|
|
isDirectDescendant=false,
|
|
isForQuote=false;
|
|
|
|
public static final int FLAG_INSET=1;
|
|
public static final int FLAG_NO_FOOTER=1 << 1;
|
|
public static final int FLAG_CHECKABLE=1 << 2;
|
|
public static final int FLAG_MEDIA_FORCE_HIDDEN=1 << 3;
|
|
public static final int FLAG_NO_HEADER=1 << 4;
|
|
public static final int FLAG_NO_TRANSLATE=1 << 5;
|
|
public static final int FLAG_NO_EMOJI_REACTIONS=1 << 6;
|
|
public static final int FLAG_IS_FOR_QUOTE=1 << 7;
|
|
|
|
public void setAncestryInfo(
|
|
boolean hasDescendantNeighbor,
|
|
boolean hasAncestoringNeighbor,
|
|
boolean isMainStatus,
|
|
boolean isDirectDescendant
|
|
) {
|
|
this.hasDescendantNeighbor = hasDescendantNeighbor;
|
|
this.hasAncestoringNeighbor = hasAncestoringNeighbor;
|
|
this.isMainStatus = isMainStatus;
|
|
this.isDirectDescendant = isDirectDescendant;
|
|
}
|
|
|
|
public StatusDisplayItem(String parentID, BaseStatusListFragment<?> parentFragment){
|
|
this.parentID=parentID;
|
|
this.parentFragment=parentFragment;
|
|
}
|
|
|
|
@NonNull
|
|
public String getContentStatusID(){
|
|
if(parentFragment instanceof StatusListFragment slf){
|
|
Status s=slf.getContentStatusByID(parentID);
|
|
return s!=null ? s.id : parentID;
|
|
}else{
|
|
return parentID;
|
|
}
|
|
}
|
|
|
|
public abstract Type getType();
|
|
|
|
public int getImageCount(){
|
|
return 0;
|
|
}
|
|
|
|
public ImageLoaderRequest getImageRequest(int index){
|
|
return null;
|
|
}
|
|
|
|
public static BindableViewHolder<? extends StatusDisplayItem> createViewHolder(Type type, Activity activity, ViewGroup parent, Fragment parentFragment){
|
|
return switch(type){
|
|
case HEADER -> new HeaderStatusDisplayItem.Holder(activity, parent);
|
|
case HEADER_CHECKABLE -> new CheckableHeaderStatusDisplayItem.Holder(activity, parent);
|
|
case REBLOG_OR_REPLY_LINE -> new ReblogOrReplyLineStatusDisplayItem.Holder(activity, parent);
|
|
case TEXT -> new TextStatusDisplayItem.Holder(activity, parent);
|
|
case AUDIO -> new AudioStatusDisplayItem.Holder(activity, parent);
|
|
case POLL_OPTION -> new PollOptionStatusDisplayItem.Holder(activity, parent);
|
|
case POLL_FOOTER -> new PollFooterStatusDisplayItem.Holder(activity, parent);
|
|
case CARD -> new LinkCardStatusDisplayItem.Holder(activity, parent);
|
|
case EMOJI_REACTIONS -> new EmojiReactionsStatusDisplayItem.Holder(activity, parent);
|
|
case FOOTER -> new FooterStatusDisplayItem.Holder(activity, parent);
|
|
case ACCOUNT_CARD -> new AccountCardStatusDisplayItem.Holder(activity, parent);
|
|
case ACCOUNT -> new AccountStatusDisplayItem.Holder(new AccountViewHolder(parentFragment, parent, null));
|
|
case HASHTAG -> new HashtagStatusDisplayItem.Holder(activity, parent);
|
|
case GAP -> new GapStatusDisplayItem.Holder(activity, parent);
|
|
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);
|
|
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 DUMMY -> new DummyStatusDisplayItem.Holder(activity);
|
|
};
|
|
}
|
|
|
|
public static ReblogOrReplyLineStatusDisplayItem buildReplyLine(BaseStatusListFragment<?> fragment, Status status, String accountID, DisplayItemsParent parent, Account account, boolean threadReply) {
|
|
String parentID = parent.getID();
|
|
String text = threadReply ? fragment.getString(R.string.sk_show_thread)
|
|
: account == null ? fragment.getString(R.string.sk_in_reply)
|
|
: status.reblog != null ? account.getDisplayName()
|
|
: fragment.getString(R.string.in_reply_to, account.getDisplayName());
|
|
String fullText = threadReply ? fragment.getString(R.string.sk_show_thread)
|
|
: account == null ? fragment.getString(R.string.sk_in_reply)
|
|
: fragment.getString(R.string.in_reply_to, account.getDisplayName());
|
|
return new ReblogOrReplyLineStatusDisplayItem(
|
|
parentID, fragment, text, account == null ? List.of() : account.emojis,
|
|
R.drawable.ic_fluent_arrow_reply_20sp_filled, null, null, fullText, status
|
|
);
|
|
}
|
|
|
|
public static ArrayList<StatusDisplayItem> buildItems(BaseStatusListFragment<?> fragment, Status status, String accountID, DisplayItemsParent parentObject, Map<String, Account> knownAccounts, FilterContext filterContext, int flags){
|
|
String parentID=parentObject.getID();
|
|
ArrayList<StatusDisplayItem> items=new ArrayList<>();
|
|
Status statusForContent=status.getContentStatus();
|
|
Bundle args=new Bundle();
|
|
args.putString("account", accountID);
|
|
ScheduledStatus scheduledStatus = parentObject instanceof ScheduledStatus s ? s : null;
|
|
|
|
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(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);
|
|
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));
|
|
} 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<ReblogOrReplyLineStatusDisplayItem> 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));
|
|
}
|
|
|
|
LegacyFilter applyingFilter=null;
|
|
if(status.filtered!=null){
|
|
for(FilterResult filter:status.filtered){
|
|
LegacyFilter f=filter.filter;
|
|
if(f.isActive() && filterContext != null && f.context.contains(filterContext)){
|
|
applyingFilter=f;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
ArrayList<StatusDisplayItem> 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;
|
|
}
|
|
|
|
if(statusForContent.quote!=null) {
|
|
int quoteInlineIndex=statusForContent.content.lastIndexOf("<span class=\"quote-inline\"><br/><br/>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());
|
|
HtmlParser.applyFilterHighlights(fragment.getActivity(), parsedText, status.filtered);
|
|
TextStatusDisplayItem text=new TextStatusDisplayItem(parentID, HtmlParser.parse(statusForContent.content, statusForContent.emojis, statusForContent.mentions, statusForContent.tags, accountID, fragment.getContext()), 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<Attachment> imageAttachments=statusForContent.mediaAttachments.stream().filter(att->att.type.isImage()).collect(Collectors.toList());
|
|
if(!imageAttachments.isEmpty()){
|
|
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);
|
|
}
|
|
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));
|
|
}
|
|
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){
|
|
item.status=statusForContent;
|
|
item.isForQuote=true;
|
|
}
|
|
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<StatusDisplayItem> 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)
|
|
);
|
|
}
|
|
|
|
public static void buildPollItems(String parentID, BaseStatusListFragment fragment, Poll poll, Status status, List<StatusDisplayItem> items){
|
|
int i=0;
|
|
for(Poll.Option opt:poll.options){
|
|
items.add(new PollOptionStatusDisplayItem(parentID, poll, i, fragment, status));
|
|
i++;
|
|
}
|
|
items.add(new PollFooterStatusDisplayItem(parentID, fragment, poll));
|
|
}
|
|
|
|
public enum Type{
|
|
HEADER,
|
|
REBLOG_OR_REPLY_LINE,
|
|
TEXT,
|
|
AUDIO,
|
|
POLL_OPTION,
|
|
POLL_FOOTER,
|
|
CARD,
|
|
EMOJI_REACTIONS,
|
|
FOOTER,
|
|
ACCOUNT_CARD,
|
|
ACCOUNT,
|
|
HASHTAG,
|
|
GAP,
|
|
EXTENDED_FOOTER,
|
|
MEDIA_GRID,
|
|
WARNING,
|
|
FILE,
|
|
SPOILER,
|
|
SECTION_HEADER,
|
|
HEADER_CHECKABLE,
|
|
NOTIFICATION_HEADER,
|
|
FILTER_SPOILER,
|
|
DUMMY
|
|
}
|
|
|
|
public static abstract class Holder<T extends StatusDisplayItem> extends BindableViewHolder<T> implements UsableRecyclerView.DisableableClickable{
|
|
private Context context;
|
|
|
|
public Holder(View itemView){
|
|
super(itemView);
|
|
}
|
|
|
|
public Holder(Context context, int layout, ViewGroup parent){
|
|
super(context, layout, parent);
|
|
this.context=context;
|
|
}
|
|
|
|
public String getItemID(){
|
|
return item.parentID;
|
|
}
|
|
|
|
@Override
|
|
public void onClick(){
|
|
if(item.isForQuote){
|
|
item.status.filterRevealed=true;
|
|
Bundle args=new Bundle();
|
|
args.putString("account", item.parentFragment.getAccountID());
|
|
args.putParcelable("status", Parcels.wrap(item.status.clone()));
|
|
args.putBoolean("refresh", true);
|
|
Nav.go((Activity) context, ThreadFragment.class, args);
|
|
return;
|
|
}
|
|
|
|
item.parentFragment.onItemClick(item.parentID);
|
|
}
|
|
|
|
public Optional<StatusDisplayItem> getNextVisibleDisplayItem(){
|
|
return getNextVisibleDisplayItem(null);
|
|
}
|
|
public Optional<StatusDisplayItem> getNextVisibleDisplayItem(Predicate<StatusDisplayItem> predicate){
|
|
Optional<StatusDisplayItem> next=getNextDisplayItem();
|
|
for(int offset=1; next.isPresent(); next=getDisplayItemOffset(++offset)){
|
|
boolean isHidden=next.map(n->(n instanceof EmojiReactionsStatusDisplayItem e && e.isHidden())
|
|
|| (n instanceof DummyStatusDisplayItem)).orElse(false);
|
|
if(!isHidden && (predicate==null || predicate.test(next.get()))) return next;
|
|
}
|
|
return Optional.empty();
|
|
}
|
|
|
|
public Optional<StatusDisplayItem> getNextDisplayItem(){
|
|
return getDisplayItemOffset(1);
|
|
}
|
|
|
|
public Optional<StatusDisplayItem> getDisplayItemOffset(int offset){
|
|
List<StatusDisplayItem> displayItems=item.parentFragment.getDisplayItems();
|
|
int thisPos=displayItems.indexOf(item);
|
|
int offsetPos=thisPos + offset;
|
|
return displayItems.size() > offsetPos && thisPos >= 0 && offsetPos >= 0
|
|
? Optional.of(displayItems.get(offsetPos))
|
|
: Optional.empty();
|
|
}
|
|
|
|
public boolean isLastDisplayItemForStatus(){
|
|
return getNextVisibleDisplayItem()
|
|
.map(next->!next.parentID.equals(item.parentID) || item.inset && !next.inset)
|
|
.orElse(true);
|
|
}
|
|
|
|
@Override
|
|
public boolean isEnabled(){
|
|
return item.parentFragment.isItemEnabled(item.parentID) || item.isForQuote;
|
|
}
|
|
}
|
|
}
|