mirror of https://github.com/tuskyapp/Tusky.git
15 changed files with 1187 additions and 386 deletions
@ -0,0 +1,84 @@
|
||||
package com.keylesspalace.tusky.util; |
||||
|
||||
import android.arch.core.util.Function; |
||||
|
||||
import java.util.AbstractList; |
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
|
||||
|
||||
/** |
||||
* This list implementation can help to keep two lists in sync - like real models and view models. |
||||
* Every operation on the main list triggers update of the supplementary list (but not vice versa). |
||||
* This makes sure that the main list is always the source of truth. |
||||
* Main list is projected to the supplementary list by the passed mapper function. |
||||
* Paired list is newer actually exposed and clients are provided with {@code getPairedCopy()}, |
||||
* {@code getPairedItem()} and {@code setPairedItem()}. This prevents modifications of the |
||||
* supplementary list size so lists are always have the same length. |
||||
* This implementation will not try to recover from exceptional cases so lists may be out of sync |
||||
* after the exception. |
||||
* |
||||
* It is most useful with immutable data because we cannot track changes inside stored objects. |
||||
* @param <T> type of elements in the main list |
||||
* @param <V> type of elements in supplementary list |
||||
*/ |
||||
public final class PairedList<T, V> extends AbstractList<T> { |
||||
private final List<T> main = new ArrayList<>(); |
||||
private final List<V> synced = new ArrayList<>(); |
||||
private final Function<T, ? extends V> mapper; |
||||
|
||||
/** |
||||
* Construct new paired list. Main and supplementary lists will be empty. |
||||
* @param mapper Function, which will be used to translate items from the main list to the |
||||
* supplementary one. |
||||
*/ |
||||
public PairedList(Function<T, ? extends V> mapper) { |
||||
this.mapper = mapper; |
||||
} |
||||
|
||||
public List<V> getPairedCopy() { |
||||
return new ArrayList<>(synced); |
||||
} |
||||
|
||||
public V getPairedItem(int index) { |
||||
return synced.get(index); |
||||
} |
||||
|
||||
public void setPairedItem(int index, V element) { |
||||
synced.set(index, element); |
||||
} |
||||
|
||||
@Override |
||||
public T get(int index) { |
||||
return main.get(index); |
||||
} |
||||
|
||||
@Override |
||||
public T set(int index, T element) { |
||||
synced.set(index, mapper.apply(element)); |
||||
return main.set(index, element); |
||||
} |
||||
|
||||
@Override |
||||
public boolean add(T t) { |
||||
synced.add(mapper.apply(t)); |
||||
return main.add(t); |
||||
} |
||||
|
||||
@Override |
||||
public void add(int index, T element) { |
||||
synced.add(index, mapper.apply(element)); |
||||
main.add(element); |
||||
} |
||||
|
||||
@Override |
||||
public T remove(int index) { |
||||
synced.remove(index); |
||||
return main.remove(index); |
||||
} |
||||
|
||||
@Override |
||||
public int size() { |
||||
return main.size(); |
||||
} |
||||
} |
||||
@ -0,0 +1,78 @@
|
||||
package com.keylesspalace.tusky.util; |
||||
|
||||
import android.arch.core.util.Function; |
||||
import android.support.annotation.Nullable; |
||||
|
||||
import com.keylesspalace.tusky.entity.Notification; |
||||
import com.keylesspalace.tusky.entity.Status; |
||||
import com.keylesspalace.tusky.viewdata.NotificationViewData; |
||||
import com.keylesspalace.tusky.viewdata.StatusViewData; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
|
||||
/** |
||||
* Created by charlag on 12/07/2017. |
||||
*/ |
||||
|
||||
public final class ViewDataUtils { |
||||
@Nullable |
||||
public static StatusViewData statusToViewData(@Nullable Status status) { |
||||
if (status == null) return null; |
||||
Status visibleStatus = status.reblog == null ? status : status.reblog; |
||||
return new StatusViewData.Builder() |
||||
.setId(status.id) |
||||
.setAttachments(status.attachments) |
||||
.setAvatar(visibleStatus.account.avatar) |
||||
.setContent(visibleStatus.content) |
||||
.setCreatedAt(visibleStatus.createdAt) |
||||
.setFavourited(visibleStatus.favourited) |
||||
.setReblogged(visibleStatus.reblogged) |
||||
.setIsExpanded(false) |
||||
.setIsShowingSensitiveContent(false) |
||||
.setMentions(visibleStatus.mentions) |
||||
.setNickname(visibleStatus.account.username) |
||||
.setRebloggedAvatar(visibleStatus.account.avatar) |
||||
.setSensitive(visibleStatus.sensitive) |
||||
.setSpoilerText(visibleStatus.spoilerText) |
||||
.setRebloggedByUsername(status.reblog == null ? null : status.account.username) |
||||
.setUserFullName(visibleStatus.account.getDisplayName()) |
||||
.setSenderId(status.account.id) |
||||
.setRebloggingEnabled(visibleStatus.rebloggingAllowed()) |
||||
.createStatusViewData(); |
||||
} |
||||
|
||||
public static List<StatusViewData> statusListToViewDataList(List<Status> statuses) { |
||||
List<StatusViewData> viewDatas = new ArrayList<>(statuses.size()); |
||||
for (Status s : statuses) { |
||||
viewDatas.add(statusToViewData(s)); |
||||
} |
||||
return viewDatas; |
||||
} |
||||
|
||||
public static Function<Status, StatusViewData> statusMapper() { |
||||
return statusMapper; |
||||
} |
||||
|
||||
public static NotificationViewData notificationToViewData(Notification notification) { |
||||
return new NotificationViewData(notification.type, notification.id, notification.account, |
||||
statusToViewData(notification.status)); |
||||
} |
||||
|
||||
public static List<NotificationViewData> |
||||
notificationListToViewDataList(List<Notification> notifications) { |
||||
List<NotificationViewData> viewDatas = new ArrayList<>(notifications.size()); |
||||
for (Notification n : notifications) { |
||||
viewDatas.add(notificationToViewData(n)); |
||||
} |
||||
return viewDatas; |
||||
} |
||||
|
||||
private static final Function<Status, StatusViewData> statusMapper = |
||||
new Function<Status, StatusViewData>() { |
||||
@Override |
||||
public StatusViewData apply(Status input) { |
||||
return ViewDataUtils.statusToViewData(input); |
||||
} |
||||
}; |
||||
} |
||||
@ -0,0 +1,39 @@
|
||||
package com.keylesspalace.tusky.viewdata; |
||||
|
||||
import com.keylesspalace.tusky.entity.Account; |
||||
import com.keylesspalace.tusky.entity.Notification; |
||||
|
||||
/** |
||||
* Created by charlag on 12/07/2017. |
||||
*/ |
||||
|
||||
public final class NotificationViewData { |
||||
private final Notification.Type type; |
||||
private final String id; |
||||
private final Account account; |
||||
private final StatusViewData statusViewData; |
||||
|
||||
public NotificationViewData(Notification.Type type, String id, Account account, |
||||
StatusViewData statusViewData) { |
||||
this.type = type; |
||||
this.id = id; |
||||
this.account = account; |
||||
this.statusViewData = statusViewData; |
||||
} |
||||
|
||||
public Notification.Type getType() { |
||||
return type; |
||||
} |
||||
|
||||
public String getId() { |
||||
return id; |
||||
} |
||||
|
||||
public Account getAccount() { |
||||
return account; |
||||
} |
||||
|
||||
public StatusViewData getStatusViewData() { |
||||
return statusViewData; |
||||
} |
||||
} |
||||
@ -0,0 +1,296 @@
|
||||
package com.keylesspalace.tusky.viewdata; |
||||
|
||||
import android.support.annotation.Nullable; |
||||
import android.text.Spanned; |
||||
|
||||
import com.keylesspalace.tusky.entity.Status; |
||||
|
||||
import java.util.Date; |
||||
|
||||
/** |
||||
* Created by charlag on 11/07/2017. |
||||
*/ |
||||
|
||||
public final class StatusViewData { |
||||
private final String id; |
||||
private final Spanned content; |
||||
private final boolean reblogged; |
||||
private final boolean favourited; |
||||
@Nullable |
||||
private final String spoilerText; |
||||
private final Status.Visibility visibility; |
||||
private final Status.MediaAttachment[] attachments; |
||||
@Nullable |
||||
private final String rebloggedByUsername; |
||||
@Nullable |
||||
private final String rebloggedAvatar; |
||||
private final boolean isSensitive; |
||||
private final boolean isExpanded; |
||||
private final boolean isShowingSensitiveContent; |
||||
private final String userFullName; |
||||
private final String nickname; |
||||
private final String avatar; |
||||
private final Date createdAt; |
||||
// I would rather have something else but it would be too much of a rewrite
|
||||
@Nullable |
||||
private final Status.Mention[] mentions; |
||||
private final String senderId; |
||||
private final boolean rebloggingEnabled; |
||||
|
||||
public StatusViewData(String id, Spanned contnet, boolean reblogged, boolean favourited, |
||||
String spoilerText, Status.Visibility visibility, |
||||
Status.MediaAttachment[] attachments, String rebloggedByUsername, |
||||
String rebloggedAvatar, boolean sensitive, boolean isExpanded, |
||||
boolean isShowingSensitiveWarning, String userFullName, String nickname, |
||||
String avatar, Date createdAt, Status.Mention[] mentions, |
||||
String senderId, boolean rebloggingEnabled) { |
||||
this.id = id; |
||||
this.content = contnet; |
||||
this.reblogged = reblogged; |
||||
this.favourited = favourited; |
||||
this.spoilerText = spoilerText; |
||||
this.visibility = visibility; |
||||
this.attachments = attachments; |
||||
this.rebloggedByUsername = rebloggedByUsername; |
||||
this.rebloggedAvatar = rebloggedAvatar; |
||||
this.isSensitive = sensitive; |
||||
this.isExpanded = isExpanded; |
||||
this.isShowingSensitiveContent = isShowingSensitiveWarning; |
||||
this.userFullName = userFullName; |
||||
this.nickname = nickname; |
||||
this.avatar = avatar; |
||||
this.createdAt = createdAt; |
||||
this.mentions = mentions; |
||||
this.senderId = senderId; |
||||
this.rebloggingEnabled = rebloggingEnabled; |
||||
} |
||||
|
||||
public String getId() { |
||||
return id; |
||||
} |
||||
|
||||
public Spanned getContent() { |
||||
return content; |
||||
} |
||||
|
||||
public boolean isReblogged() { |
||||
return reblogged; |
||||
} |
||||
|
||||
public boolean isFavourited() { |
||||
return favourited; |
||||
} |
||||
|
||||
@Nullable |
||||
public String getSpoilerText() { |
||||
return spoilerText; |
||||
} |
||||
|
||||
public Status.Visibility getVisibility() { |
||||
return visibility; |
||||
} |
||||
|
||||
public Status.MediaAttachment[] getAttachments() { |
||||
return attachments; |
||||
} |
||||
|
||||
@Nullable |
||||
public String getRebloggedByUsername() { |
||||
return rebloggedByUsername; |
||||
} |
||||
|
||||
public boolean isSensitive() { |
||||
return isSensitive; |
||||
} |
||||
|
||||
public boolean isExpanded() { |
||||
return isExpanded; |
||||
} |
||||
|
||||
public boolean isShowingSensitiveContent() { |
||||
return isShowingSensitiveContent; |
||||
} |
||||
|
||||
@Nullable |
||||
public String getRebloggedAvatar() { |
||||
return rebloggedAvatar; |
||||
} |
||||
|
||||
public String getUserFullName() { |
||||
return userFullName; |
||||
} |
||||
|
||||
public String getNickname() { |
||||
return nickname; |
||||
} |
||||
|
||||
public String getAvatar() { |
||||
return avatar; |
||||
} |
||||
|
||||
public Date getCreatedAt() { |
||||
return createdAt; |
||||
} |
||||
|
||||
public String getSenderId() { |
||||
return senderId; |
||||
} |
||||
|
||||
public Boolean getRebloggingEnabled() { |
||||
return rebloggingEnabled; |
||||
} |
||||
|
||||
@Nullable |
||||
public Status.Mention[] getMentions() { |
||||
return mentions; |
||||
} |
||||
|
||||
public static class Builder { |
||||
private String id; |
||||
private Spanned contnet; |
||||
private boolean reblogged; |
||||
private boolean favourited; |
||||
private String spoilerText; |
||||
private Status.Visibility visibility; |
||||
private Status.MediaAttachment[] attachments; |
||||
private String rebloggedByUsername; |
||||
private String rebloggedAvatar; |
||||
private boolean isSensitive; |
||||
private boolean isExpanded; |
||||
private boolean isShowingSensitiveContent; |
||||
private String userFullName; |
||||
private String nickname; |
||||
private String avatar; |
||||
private Date createdAt; |
||||
private Status.Mention[] mentions; |
||||
private String senderId; |
||||
private boolean rebloggingEnabled; |
||||
|
||||
public Builder() { |
||||
} |
||||
|
||||
public Builder(final StatusViewData viewData) { |
||||
id = viewData.id; |
||||
contnet = viewData.content; |
||||
reblogged = viewData.reblogged; |
||||
favourited = viewData.favourited; |
||||
spoilerText = viewData.spoilerText; |
||||
visibility = viewData.visibility; |
||||
attachments = viewData.attachments == null ? null : viewData.attachments.clone(); |
||||
rebloggedByUsername = viewData.rebloggedByUsername; |
||||
rebloggedAvatar = viewData.rebloggedAvatar; |
||||
isSensitive = viewData.isSensitive; |
||||
isExpanded = viewData.isExpanded; |
||||
isShowingSensitiveContent = viewData.isShowingSensitiveContent; |
||||
userFullName = viewData.userFullName; |
||||
nickname = viewData.nickname; |
||||
avatar = viewData.avatar; |
||||
createdAt = new Date(viewData.createdAt.getTime()); |
||||
mentions = viewData.mentions == null ? null : viewData.mentions.clone(); |
||||
senderId = viewData.senderId; |
||||
rebloggingEnabled = viewData.rebloggingEnabled; |
||||
} |
||||
|
||||
public Builder setId(String id) { |
||||
this.id = id; |
||||
return this; |
||||
} |
||||
|
||||
public Builder setContent(Spanned content) { |
||||
this.contnet = content; |
||||
return this; |
||||
} |
||||
|
||||
public Builder setReblogged(boolean reblogged) { |
||||
this.reblogged = reblogged; |
||||
return this; |
||||
} |
||||
|
||||
public Builder setFavourited(boolean favourited) { |
||||
this.favourited = favourited; |
||||
return this; |
||||
} |
||||
|
||||
public Builder setSpoilerText(String spoilerText) { |
||||
this.spoilerText = spoilerText; |
||||
return this; |
||||
} |
||||
|
||||
public Builder setVisibility(Status.Visibility visibility) { |
||||
this.visibility = visibility; |
||||
return this; |
||||
} |
||||
|
||||
public Builder setAttachments(Status.MediaAttachment[] attachments) { |
||||
this.attachments = attachments; |
||||
return this; |
||||
} |
||||
|
||||
public Builder setRebloggedByUsername(String rebloggedByUsername) { |
||||
this.rebloggedByUsername = rebloggedByUsername; |
||||
return this; |
||||
} |
||||
|
||||
public Builder setRebloggedAvatar(String rebloggedAvatar) { |
||||
this.rebloggedAvatar = rebloggedAvatar; |
||||
return this; |
||||
} |
||||
|
||||
public Builder setSensitive(boolean sensitive) { |
||||
this.isSensitive = sensitive; |
||||
return this; |
||||
} |
||||
|
||||
public Builder setIsExpanded(boolean isExpanded) { |
||||
this.isExpanded = isExpanded; |
||||
return this; |
||||
} |
||||
|
||||
public Builder setIsShowingSensitiveContent(boolean isShowingSensitiveContent) { |
||||
this.isShowingSensitiveContent = isShowingSensitiveContent; |
||||
return this; |
||||
} |
||||
|
||||
public Builder setUserFullName(String userFullName) { |
||||
this.userFullName = userFullName; |
||||
return this; |
||||
} |
||||
|
||||
public Builder setNickname(String nickname) { |
||||
this.nickname = nickname; |
||||
return this; |
||||
} |
||||
|
||||
public Builder setAvatar(String avatar) { |
||||
this.avatar = avatar; |
||||
return this; |
||||
} |
||||
|
||||
public Builder setCreatedAt(Date createdAt) { |
||||
this.createdAt = createdAt; |
||||
return this; |
||||
} |
||||
|
||||
public Builder setMentions(Status.Mention[] mentions) { |
||||
this.mentions = mentions; |
||||
return this; |
||||
} |
||||
|
||||
public Builder setSenderId(String senderId) { |
||||
this.senderId = senderId; |
||||
return this; |
||||
} |
||||
|
||||
public Builder setRebloggingEnabled(boolean rebloggingEnabled) { |
||||
this.rebloggingEnabled = rebloggingEnabled; |
||||
return this; |
||||
} |
||||
|
||||
public StatusViewData createStatusViewData() { |
||||
return new StatusViewData(id, contnet, reblogged, favourited, spoilerText, visibility, |
||||
attachments, rebloggedByUsername, rebloggedAvatar, isSensitive, isExpanded, |
||||
isShowingSensitiveContent, userFullName, nickname, avatar, createdAt, mentions, |
||||
senderId, rebloggingEnabled); |
||||
} |
||||
} |
||||
} |
||||
Loading…
Reference in new issue