chore(merging-upstream): bunch of conflicts to solve
This commit is contained in:
@@ -1,41 +1,44 @@
|
||||
package org.joinmastodon.android.model;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.joinmastodon.android.api.ObjectValidationException;
|
||||
import org.joinmastodon.android.api.RequiredField;
|
||||
import org.parceler.Parcel;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents a user of Mastodon and their associated profile.
|
||||
*/
|
||||
@Parcel
|
||||
public class Account extends BaseModel{
|
||||
public class Account extends BaseModel implements Searchable{
|
||||
// Base attributes
|
||||
|
||||
/**
|
||||
* The account id
|
||||
*/
|
||||
@RequiredField
|
||||
// @RequiredField
|
||||
public String id;
|
||||
/**
|
||||
* The username of the account, not including domain.
|
||||
*/
|
||||
@RequiredField
|
||||
// @RequiredField
|
||||
public String username;
|
||||
/**
|
||||
* The Webfinger account URI. Equal to username for local users, or username@domain for remote users.
|
||||
*/
|
||||
@RequiredField
|
||||
// @RequiredField
|
||||
public String acct;
|
||||
/**
|
||||
* The location of the user's profile page.
|
||||
*/
|
||||
@RequiredField
|
||||
// @RequiredField
|
||||
public String url;
|
||||
|
||||
// Display attributes
|
||||
@@ -43,17 +46,17 @@ public class Account extends BaseModel{
|
||||
/**
|
||||
* The profile's display name.
|
||||
*/
|
||||
@RequiredField
|
||||
// @RequiredField
|
||||
public String displayName;
|
||||
/**
|
||||
* The profile's bio / description.
|
||||
*/
|
||||
@RequiredField
|
||||
// @RequiredField
|
||||
public String note;
|
||||
/**
|
||||
* An image icon that is shown next to statuses and in the profile.
|
||||
*/
|
||||
@RequiredField
|
||||
// @RequiredField
|
||||
public String avatar;
|
||||
/**
|
||||
* A static version of the avatar. Equal to avatar if its value is a static image; different if avatar is an animated GIF.
|
||||
@@ -62,7 +65,6 @@ public class Account extends BaseModel{
|
||||
/**
|
||||
* An image banner that is shown above the profile and in profile cards.
|
||||
*/
|
||||
@RequiredField
|
||||
public String header;
|
||||
/**
|
||||
* A static version of the header. Equal to header if its value is a static image; different if header is an animated GIF.
|
||||
@@ -86,7 +88,7 @@ public class Account extends BaseModel{
|
||||
/**
|
||||
* When the account was created.
|
||||
*/
|
||||
@RequiredField
|
||||
// @RequiredField
|
||||
public Instant createdAt;
|
||||
/**
|
||||
* When the most recent status was posted.
|
||||
@@ -134,6 +136,21 @@ public class Account extends BaseModel{
|
||||
public Instant muteExpiresAt;
|
||||
public boolean noindex;
|
||||
|
||||
public List<Role> roles;
|
||||
|
||||
public @Nullable String fqn; // akkoma has this, mastodon't
|
||||
|
||||
@Override
|
||||
public String getQuery() {
|
||||
return url;
|
||||
}
|
||||
|
||||
@Parcel
|
||||
public static class Role {
|
||||
public String name;
|
||||
/** #rrggbb */
|
||||
public String color;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postprocess() throws ObjectValidationException{
|
||||
@@ -141,15 +158,26 @@ public class Account extends BaseModel{
|
||||
if(fields!=null){
|
||||
for(AccountField f:fields)
|
||||
f.postprocess();
|
||||
}else{
|
||||
fields=Collections.emptyList();
|
||||
}
|
||||
if(emojis!=null){
|
||||
for(Emoji e:emojis)
|
||||
e.postprocess();
|
||||
}else{
|
||||
emojis=Collections.emptyList();
|
||||
}
|
||||
if(moved!=null)
|
||||
moved.postprocess();
|
||||
if(fqn==null) fqn=getFullyQualifiedName();
|
||||
if(id==null) id="";
|
||||
if(username==null) username="";
|
||||
if(TextUtils.isEmpty(displayName))
|
||||
displayName=username;
|
||||
displayName=!TextUtils.isEmpty(username) ? username : "";
|
||||
if(acct==null) acct="";
|
||||
if(url==null) url="";
|
||||
if(note==null) note="";
|
||||
if(avatar==null) avatar="";
|
||||
}
|
||||
|
||||
public boolean isLocal(){
|
||||
@@ -161,10 +189,28 @@ public class Account extends BaseModel{
|
||||
return parts.length==1 ? null : parts[1];
|
||||
}
|
||||
|
||||
public String getDomainFromURL() {
|
||||
return Uri.parse(url).getHost();
|
||||
}
|
||||
|
||||
public String getDisplayUsername(){
|
||||
return '@'+acct;
|
||||
}
|
||||
|
||||
public String getShortUsername() {
|
||||
return '@'+acct.split("@")[0];
|
||||
}
|
||||
|
||||
public String getFullyQualifiedName() {
|
||||
if (TextUtils.isEmpty(acct))
|
||||
return "";
|
||||
return fqn != null ? fqn : acct.split("@")[0] + "@" + getDomainFromURL();
|
||||
}
|
||||
|
||||
public String getDisplayName(){
|
||||
return '\u2068'+displayName+'\u2069';
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
return "Account{"+
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package org.joinmastodon.android.model;
|
||||
|
||||
public class AkkomaTranslation extends BaseModel{
|
||||
public String text;
|
||||
public String detectedLanguage;
|
||||
|
||||
public Translation toTranslation() {
|
||||
Translation translation=new Translation();
|
||||
translation.content=text;
|
||||
translation.detectedSourceLanguage=detectedLanguage;
|
||||
translation.provider="Akkoma";
|
||||
return translation;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package org.joinmastodon.android.model;
|
||||
|
||||
import org.jsoup.internal.StringUtil;
|
||||
|
||||
import java.util.EnumSet;
|
||||
|
||||
public class AltTextFilter extends LegacyFilter {
|
||||
|
||||
public AltTextFilter(FilterAction filterAction, FilterContext firstContext, FilterContext... restContexts) {
|
||||
this.filterAction = filterAction;
|
||||
isRemote = false;
|
||||
context = EnumSet.of(firstContext, restContexts);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(Status status) {
|
||||
return status.getContentStatus().mediaAttachments.stream().map(attachment -> attachment.description).anyMatch(StringUtil::isBlank);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package org.joinmastodon.android.model;
|
||||
|
||||
import org.joinmastodon.android.api.ObjectValidationException;
|
||||
import org.joinmastodon.android.api.RequiredField;
|
||||
import org.parceler.Parcel;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Parcel
|
||||
public class Announcement extends BaseModel implements DisplayItemsParent {
|
||||
@RequiredField
|
||||
public String id;
|
||||
@RequiredField
|
||||
public String content;
|
||||
public Instant startsAt;
|
||||
public Instant endsAt;
|
||||
public boolean published;
|
||||
public boolean allDay;
|
||||
public Instant publishedAt;
|
||||
public Instant updatedAt;
|
||||
public boolean read;
|
||||
public List<Emoji> emojis;
|
||||
public List<EmojiReaction> reactions;
|
||||
public List<Mention> mentions;
|
||||
public List<Hashtag> tags;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Announcement{" +
|
||||
"id='" + id + '\'' +
|
||||
", content='" + content + '\'' +
|
||||
", startsAt=" + startsAt +
|
||||
", endsAt=" + endsAt +
|
||||
", published=" + published +
|
||||
", allDay=" + allDay +
|
||||
", publishedAt=" + publishedAt +
|
||||
", updatedAt=" + updatedAt +
|
||||
", read=" + read +
|
||||
", emojis=" + emojis +
|
||||
", mentions=" + mentions +
|
||||
", tags=" + tags +
|
||||
'}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postprocess() throws ObjectValidationException{
|
||||
super.postprocess();
|
||||
if(reactions==null) reactions=new ArrayList<>();
|
||||
}
|
||||
|
||||
public Status toStatus() {
|
||||
Status s=Status.ofFake(id, content, publishedAt);
|
||||
s.createdAt=startsAt != null ? startsAt : publishedAt;
|
||||
s.reactions=reactions;
|
||||
if(updatedAt != null) s.editedAt=updatedAt;
|
||||
return s;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getID() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package org.joinmastodon.android.model;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.PointF;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.media.MediaMetadataRetriever;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
@@ -16,7 +17,7 @@ import org.parceler.ParcelProperty;
|
||||
|
||||
@Parcel
|
||||
public class Attachment extends BaseModel{
|
||||
@RequiredField
|
||||
// @RequiredField
|
||||
public String id;
|
||||
@RequiredField
|
||||
public Type type;
|
||||
@@ -85,6 +86,13 @@ public class Attachment extends BaseModel{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public boolean hasSound() {
|
||||
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
|
||||
retriever.setDataSource(url);
|
||||
String hasAudioStr = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_HAS_AUDIO);
|
||||
return "yes".equals(hasAudioStr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postprocess() throws ObjectValidationException{
|
||||
super.postprocess();
|
||||
@@ -93,6 +101,12 @@ public class Attachment extends BaseModel{
|
||||
if(placeholder!=null)
|
||||
blurhashPlaceholder=new BlurHashDrawable(placeholder, getWidth(), getHeight());
|
||||
}
|
||||
|
||||
if (id == null) {
|
||||
// akkoma servers doesn't provide IDs for attachments,
|
||||
// but IDs are needed by the AudioPlayerService
|
||||
id = "" + this.hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -11,6 +11,14 @@ import androidx.annotation.CallSuper;
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
public abstract class BaseModel implements Cloneable{
|
||||
|
||||
/**
|
||||
* indicates the profile has been fetched from a foreign instance.
|
||||
*
|
||||
* @see org.joinmastodon.android.api.MastodonAPIRequest#execRemote
|
||||
*/
|
||||
public transient boolean isRemote;
|
||||
|
||||
@CallSuper
|
||||
public void postprocess() throws ObjectValidationException{
|
||||
try{
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.joinmastodon.android.model;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
@@ -51,6 +52,13 @@ public class Card extends BaseModel{
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isHashtagUrl(String statusUrl){
|
||||
Uri parsedUrl=Uri.parse(url);
|
||||
Uri parsedStatusUrl=Uri.parse(statusUrl);
|
||||
if(parsedUrl.getHost()==null || parsedUrl.getPath()==null || parsedStatusUrl.getHost()==null) return false;
|
||||
return title.equals("Akkoma") && parsedUrl.getHost().equals(parsedStatusUrl.getHost()) && parsedUrl.getPath().startsWith("/tag/");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
return "Card{"+
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package org.joinmastodon.android.model;
|
||||
|
||||
import android.view.Menu;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import org.joinmastodon.android.R;
|
||||
|
||||
public enum ContentType {
|
||||
@SerializedName("text/plain")
|
||||
PLAIN,
|
||||
@SerializedName("text/html")
|
||||
HTML,
|
||||
@SerializedName("text/markdown")
|
||||
MARKDOWN,
|
||||
@SerializedName("text/bbcode")
|
||||
BBCODE, // akkoma
|
||||
@SerializedName("text/x.misskeymarkdown")
|
||||
MISSKEY_MARKDOWN, // akkoma/*key
|
||||
@SerializedName("")
|
||||
UNSPECIFIED;
|
||||
|
||||
public int getName() {
|
||||
return switch(this) {
|
||||
case PLAIN -> R.string.sk_content_type_plain;
|
||||
case HTML -> R.string.sk_content_type_html;
|
||||
case MARKDOWN -> R.string.sk_content_type_markdown;
|
||||
case BBCODE -> R.string.sk_content_type_bbcode;
|
||||
case MISSKEY_MARKDOWN -> R.string.sk_content_type_mfm;
|
||||
case UNSPECIFIED -> R.string.sk_content_type_unspecified;
|
||||
};
|
||||
}
|
||||
|
||||
public boolean supportedByInstance(Instance i) {
|
||||
return i.isAkkoma() || (this!=BBCODE && this!=MISSKEY_MARKDOWN);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package org.joinmastodon.android.model;
|
||||
|
||||
import org.joinmastodon.android.api.RequiredField;
|
||||
import org.parceler.Parcel;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Parcel
|
||||
public class CustomLocalTimeline extends BaseModel{
|
||||
@RequiredField
|
||||
public String domain;
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
return "Hashtag{"+
|
||||
", url='"+domain+'\''+
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package org.joinmastodon.android.model;
|
||||
|
||||
import org.joinmastodon.android.api.RequiredField;
|
||||
import org.parceler.Parcel;
|
||||
|
||||
@Parcel
|
||||
public class DomainBlock extends BaseModel {
|
||||
@RequiredField
|
||||
public String domain;
|
||||
@RequiredField
|
||||
public String digest;
|
||||
@RequiredField
|
||||
public Severity severity;
|
||||
public String comment;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DomainBlock{" +
|
||||
"domain='" + domain + '\'' +
|
||||
", digest='" + digest + '\'' +
|
||||
", severity='" + severity + '\'' +
|
||||
", comment='" + comment + '\'' +
|
||||
'}';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -33,6 +33,20 @@ public class Emoji extends BaseModel{
|
||||
*/
|
||||
public String category;
|
||||
|
||||
public Emoji() {}
|
||||
|
||||
public Emoji(String shortcode, String url, String staticUrl) {
|
||||
this.shortcode = shortcode.replaceAll(":", "");
|
||||
this.url = url;
|
||||
this.staticUrl = staticUrl;
|
||||
}
|
||||
|
||||
public String getUrl(boolean playGifs){
|
||||
String idealUrl=playGifs ? url : staticUrl;
|
||||
if(idealUrl==null) return url==null ? staticUrl : url;
|
||||
return idealUrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
return "Emoji{"+
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.joinmastodon.android.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class EmojiCategory{
|
||||
@@ -10,4 +11,8 @@ public class EmojiCategory{
|
||||
this.title=title;
|
||||
this.emojis=emojis;
|
||||
}
|
||||
public EmojiCategory(EmojiCategory category){
|
||||
this.title = category.title;
|
||||
this.emojis = new ArrayList<>(category.emojis);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
package org.joinmastodon.android.model;
|
||||
|
||||
import org.joinmastodon.android.GlobalUserPreferences;
|
||||
import org.parceler.Parcel;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
|
||||
import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest;
|
||||
import me.grishka.appkit.utils.V;
|
||||
|
||||
@Parcel
|
||||
public class EmojiReaction {
|
||||
public List<Account> accounts;
|
||||
public List<String> accountIds;
|
||||
public int count;
|
||||
public boolean me;
|
||||
public String name;
|
||||
public String url;
|
||||
public String staticUrl;
|
||||
|
||||
public transient ImageLoaderRequest request;
|
||||
|
||||
public String getUrl(boolean playGifs){
|
||||
String idealUrl=playGifs ? url : staticUrl;
|
||||
if(idealUrl==null) return url==null ? staticUrl : url;
|
||||
return idealUrl;
|
||||
}
|
||||
|
||||
public static EmojiReaction of(Emoji info, Account me){
|
||||
EmojiReaction reaction=new EmojiReaction();
|
||||
reaction.me=true;
|
||||
reaction.count=1;
|
||||
reaction.name=info.shortcode;
|
||||
reaction.url=info.url;
|
||||
reaction.staticUrl=info.staticUrl;
|
||||
reaction.accounts=new ArrayList<>(Collections.singleton(me));
|
||||
reaction.accountIds=new ArrayList<>(Collections.singleton(me.id));
|
||||
reaction.request=new UrlImageLoaderRequest(info.url, V.sp(24), V.sp(24));
|
||||
return reaction;
|
||||
}
|
||||
|
||||
public static EmojiReaction of(String emoji, Account me){
|
||||
EmojiReaction reaction=new EmojiReaction();
|
||||
reaction.me=true;
|
||||
reaction.count=1;
|
||||
reaction.name=emoji;
|
||||
reaction.accounts=new ArrayList<>(Collections.singleton(me));
|
||||
reaction.accountIds=new ArrayList<>(Collections.singleton(me.id));
|
||||
return reaction;
|
||||
}
|
||||
|
||||
public void add(Account self){
|
||||
if(accounts==null) accounts=new ArrayList<>();
|
||||
if(accountIds==null) accountIds=new ArrayList<>();
|
||||
count++;
|
||||
me=true;
|
||||
accounts.add(self);
|
||||
accountIds.add(self.id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package org.joinmastodon.android.model;
|
||||
|
||||
import org.joinmastodon.android.api.RequiredField;
|
||||
import org.parceler.Parcel;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Parcel
|
||||
public class ExtendedDescription extends BaseModel{
|
||||
@RequiredField
|
||||
public String content;
|
||||
public String updatedAt;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ExtendedDescription{" +
|
||||
"content='" + content + '\'' +
|
||||
", updatedAt='" + updatedAt + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,20 @@
|
||||
package org.joinmastodon.android.model;
|
||||
|
||||
import org.joinmastodon.android.api.ObjectValidationException;
|
||||
import org.joinmastodon.android.api.RequiredField;
|
||||
import org.parceler.Parcel;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Parcel
|
||||
public class FilterResult extends BaseModel{
|
||||
@RequiredField
|
||||
public Filter filter;
|
||||
public class FilterResult extends BaseModel {
|
||||
public LegacyFilter filter;
|
||||
|
||||
public List<String> keywordMatches;
|
||||
public List<String> keywordMatches;
|
||||
|
||||
@Override
|
||||
public void postprocess() throws ObjectValidationException{
|
||||
super.postprocess();
|
||||
filter.postprocess();
|
||||
}
|
||||
@Override
|
||||
public void postprocess() throws ObjectValidationException {
|
||||
super.postprocess();
|
||||
if(filter!=null) filter.postprocess();
|
||||
if(keywordMatches==null) keywordMatches=List.of();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,15 +11,16 @@ public class Hashtag extends BaseModel implements DisplayItemsParent{
|
||||
public String name;
|
||||
@RequiredField
|
||||
public String url;
|
||||
public boolean following;
|
||||
public List<History> history;
|
||||
public int statusesCount;
|
||||
public boolean following;
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
return "Hashtag{"+
|
||||
"name='"+name+'\''+
|
||||
", url='"+url+'\''+
|
||||
", following="+following+
|
||||
", history="+history+
|
||||
", statusesCount="+statusesCount+
|
||||
", following="+following+
|
||||
|
||||
@@ -11,6 +11,7 @@ import java.net.IDN;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
@Parcel
|
||||
public class Instance extends BaseModel{
|
||||
@@ -27,7 +28,7 @@ public class Instance extends BaseModel{
|
||||
/**
|
||||
* Admin-defined description of the Mastodon site.
|
||||
*/
|
||||
@RequiredField
|
||||
// @RequiredField
|
||||
public String description;
|
||||
/**
|
||||
* A shorter description defined by the admin.
|
||||
@@ -37,7 +38,7 @@ public class Instance extends BaseModel{
|
||||
/**
|
||||
* An email that may be contacted for any inquiries.
|
||||
*/
|
||||
@RequiredField
|
||||
// @RequiredField
|
||||
public String email;
|
||||
/**
|
||||
* The version of Mastodon installed on the instance.
|
||||
@@ -82,6 +83,15 @@ public class Instance extends BaseModel{
|
||||
// non-standard field in some Mastodon forks
|
||||
public int maxTootChars;
|
||||
|
||||
public V2 v2;
|
||||
|
||||
public Pleroma pleroma;
|
||||
|
||||
public PleromaPollLimits pollLimits;
|
||||
|
||||
/** like uri, but always without scheme and trailing slash */
|
||||
public transient String normalizedUri;
|
||||
|
||||
@Override
|
||||
public void postprocess() throws ObjectValidationException{
|
||||
super.postprocess();
|
||||
@@ -91,6 +101,10 @@ public class Instance extends BaseModel{
|
||||
rules=Collections.emptyList();
|
||||
if(shortDescription==null)
|
||||
shortDescription="";
|
||||
// akkoma says uri is "https://example.social" while just "example.social" on mastodon
|
||||
normalizedUri = uri
|
||||
.replaceFirst("^https://", "")
|
||||
.replaceFirst("/$", "");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -130,6 +144,34 @@ public class Instance extends BaseModel{
|
||||
return ci;
|
||||
}
|
||||
|
||||
public boolean isAkkoma() {
|
||||
return pleroma != null;
|
||||
}
|
||||
|
||||
public boolean isPixelfed() {
|
||||
return version.contains("compatible; Pixelfed");
|
||||
}
|
||||
|
||||
public boolean hasFeature(Feature feature) {
|
||||
Optional<List<String>> pleromaFeatures = Optional.ofNullable(pleroma)
|
||||
.map(p -> p.metadata)
|
||||
.map(m -> m.features);
|
||||
|
||||
return switch (feature) {
|
||||
case BUBBLE_TIMELINE -> pleromaFeatures
|
||||
.map(f -> f.contains("bubble_timeline"))
|
||||
.orElse(false);
|
||||
case MACHINE_TRANSLATION -> pleromaFeatures
|
||||
.map(f -> f.contains("akkoma:machine_translation"))
|
||||
.orElse(false);
|
||||
};
|
||||
}
|
||||
|
||||
public enum Feature {
|
||||
BUBBLE_TIMELINE,
|
||||
MACHINE_TRANSLATION
|
||||
}
|
||||
|
||||
@Parcel
|
||||
public static class Rule{
|
||||
public String id;
|
||||
@@ -176,4 +218,46 @@ public class Instance extends BaseModel{
|
||||
public int minExpiration;
|
||||
public int maxExpiration;
|
||||
}
|
||||
|
||||
@Parcel
|
||||
public static class V2 extends BaseModel {
|
||||
public V2.Configuration configuration;
|
||||
|
||||
@Parcel
|
||||
public static class Configuration {
|
||||
public TranslationConfiguration translation;
|
||||
}
|
||||
|
||||
@Parcel
|
||||
public static class TranslationConfiguration{
|
||||
public boolean enabled;
|
||||
}
|
||||
}
|
||||
|
||||
@Parcel
|
||||
public static class Pleroma extends BaseModel {
|
||||
public Pleroma.Metadata metadata;
|
||||
|
||||
@Parcel
|
||||
public static class Metadata {
|
||||
public List<String> features;
|
||||
public Pleroma.Metadata.FieldsLimits fieldsLimits;
|
||||
|
||||
@Parcel
|
||||
public static class FieldsLimits {
|
||||
public long maxFields;
|
||||
public long maxRemoteFields;
|
||||
public long nameLength;
|
||||
public long valueLength;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Parcel
|
||||
public static class PleromaPollLimits {
|
||||
public long maxExpiration;
|
||||
public long maxOptionChars;
|
||||
public long maxOptions;
|
||||
public long minExpiration;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,24 +6,32 @@ import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import org.joinmastodon.android.api.ObjectValidationException;
|
||||
import org.joinmastodon.android.api.RequiredField;
|
||||
import org.parceler.Parcel;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@Parcel
|
||||
public class LegacyFilter extends BaseModel{
|
||||
@RequiredField
|
||||
public String id;
|
||||
@RequiredField
|
||||
public String phrase;
|
||||
public String title;
|
||||
public transient EnumSet<FilterContext> context=EnumSet.noneOf(FilterContext.class);
|
||||
public Instant expiresAt;
|
||||
public boolean irreversible;
|
||||
public boolean wholeWord;
|
||||
|
||||
@SerializedName("context")
|
||||
private List<FilterContext> _context;
|
||||
protected List<FilterContext> _context;
|
||||
|
||||
public FilterAction filterAction;
|
||||
|
||||
public List<FilterKeyword> keywords=new ArrayList<>();
|
||||
|
||||
public List<FilterStatus> statuses=new ArrayList<>();
|
||||
|
||||
private transient Pattern pattern;
|
||||
|
||||
@@ -36,6 +44,10 @@ public class LegacyFilter extends BaseModel{
|
||||
if(c!=null)
|
||||
context.add(c);
|
||||
}
|
||||
for(FilterKeyword keyword:keywords)
|
||||
keyword.postprocess();
|
||||
for(FilterStatus status:statuses)
|
||||
status.postprocess();
|
||||
}
|
||||
|
||||
public boolean matches(CharSequence text){
|
||||
@@ -47,6 +59,7 @@ public class LegacyFilter extends BaseModel{
|
||||
else
|
||||
pattern=Pattern.compile(Pattern.quote(phrase), Pattern.CASE_INSENSITIVE);
|
||||
}
|
||||
if (title == null) title = phrase;
|
||||
return pattern.matcher(text).find();
|
||||
}
|
||||
|
||||
@@ -62,12 +75,15 @@ public class LegacyFilter extends BaseModel{
|
||||
public String toString(){
|
||||
return "Filter{"+
|
||||
"id='"+id+'\''+
|
||||
", title='"+title+'\''+
|
||||
", phrase='"+phrase+'\''+
|
||||
", context="+context+
|
||||
", expiresAt="+expiresAt+
|
||||
", irreversible="+irreversible+
|
||||
", wholeWord="+wholeWord+
|
||||
", filterAction="+filterAction+
|
||||
", keywords="+keywords+
|
||||
", statuses="+statuses+
|
||||
'}';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package org.joinmastodon.android.model;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import org.joinmastodon.android.api.RequiredField;
|
||||
import org.parceler.Parcel;
|
||||
|
||||
@Parcel
|
||||
public class ListTimeline extends BaseModel {
|
||||
@RequiredField
|
||||
public String id;
|
||||
@RequiredField
|
||||
public String title;
|
||||
public RepliesPolicy repliesPolicy;
|
||||
public boolean exclusive;
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "List{" +
|
||||
"id='" + id + '\'' +
|
||||
", title='" + title + '\'' +
|
||||
", repliesPolicy=" + repliesPolicy +
|
||||
", exclusive=" + exclusive +
|
||||
'}';
|
||||
}
|
||||
|
||||
public enum RepliesPolicy{
|
||||
@SerializedName("followed")
|
||||
FOLLOWED,
|
||||
@SerializedName("list")
|
||||
LIST,
|
||||
@SerializedName("none")
|
||||
NONE
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
package org.joinmastodon.android.model;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import org.joinmastodon.android.api.AllFieldsAreRequired;
|
||||
|
||||
import java.time.Instant;
|
||||
@@ -18,4 +20,11 @@ public class Marker extends BaseModel{
|
||||
", updatedAt="+updatedAt+
|
||||
'}';
|
||||
}
|
||||
|
||||
public enum Type {
|
||||
@SerializedName("home")
|
||||
HOME,
|
||||
@SerializedName("notifications")
|
||||
NOTIFICATIONS
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package org.joinmastodon.android.model;
|
||||
|
||||
public class Markers {
|
||||
public Marker notifications;
|
||||
public Marker home;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Markers{" +
|
||||
"notifications=" + notifications +
|
||||
", home=" + home +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -18,8 +18,10 @@ public class Notification extends BaseModel implements DisplayItemsParent{
|
||||
public Instant createdAt;
|
||||
@RequiredField
|
||||
public Account account;
|
||||
|
||||
public Status status;
|
||||
public Report report;
|
||||
public String emoji;
|
||||
public String emojiUrl;
|
||||
|
||||
@Override
|
||||
public void postprocess() throws ObjectValidationException{
|
||||
@@ -53,6 +55,23 @@ public class Notification extends BaseModel implements DisplayItemsParent{
|
||||
@SerializedName("poll")
|
||||
POLL,
|
||||
@SerializedName("status")
|
||||
STATUS
|
||||
STATUS,
|
||||
@SerializedName("update")
|
||||
UPDATE,
|
||||
@SerializedName("reaction")
|
||||
REACTION,
|
||||
@SerializedName("pleroma:emoji_reaction")
|
||||
PLEROMA_EMOJI_REACTION,
|
||||
@SerializedName("admin.sign_up")
|
||||
SIGN_UP,
|
||||
@SerializedName("admin.report")
|
||||
REPORT
|
||||
}
|
||||
|
||||
@Parcel
|
||||
public static class Report {
|
||||
public String id;
|
||||
public String comment;
|
||||
public Account targetAccount;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package org.joinmastodon.android.model;
|
||||
|
||||
public enum NotificationAction {
|
||||
FAVORITE,
|
||||
BOOST,
|
||||
UNBOOST,
|
||||
BOOKMARK,
|
||||
REPLY,
|
||||
FOLLOW_BACK
|
||||
}
|
||||
@@ -18,11 +18,11 @@ public class Poll extends BaseModel{
|
||||
public int votersCount;
|
||||
public int votesCount;
|
||||
public boolean voted;
|
||||
@RequiredField
|
||||
// @RequiredField
|
||||
public List<Integer> ownVotes;
|
||||
@RequiredField
|
||||
public List<Option> options;
|
||||
@RequiredField
|
||||
// @RequiredField
|
||||
public List<Emoji> emojis;
|
||||
|
||||
public transient ArrayList<Option> selectedOptions;
|
||||
@@ -30,6 +30,8 @@ public class Poll extends BaseModel{
|
||||
@Override
|
||||
public void postprocess() throws ObjectValidationException{
|
||||
super.postprocess();
|
||||
if (emojis == null) emojis = List.of();
|
||||
if (ownVotes == null) ownVotes = List.of();
|
||||
for(Emoji e:emojis)
|
||||
e.postprocess();
|
||||
}
|
||||
@@ -60,6 +62,11 @@ public class Poll extends BaseModel{
|
||||
public String title;
|
||||
public Integer votesCount;
|
||||
|
||||
public Option() {}
|
||||
public Option(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
return "Option{"+
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
package org.joinmastodon.android.model;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.api.RequiredField;
|
||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||
|
||||
import androidx.annotation.StringRes;
|
||||
|
||||
@@ -20,6 +23,40 @@ public class PushNotification extends BaseModel{
|
||||
@RequiredField
|
||||
public String body;
|
||||
|
||||
public static PushNotification fromNotification(Context context, Notification notification){
|
||||
PushNotification pushNotification = new PushNotification();
|
||||
pushNotification.notificationType = switch(notification.type) {
|
||||
case FOLLOW -> PushNotification.Type.FOLLOW;
|
||||
case MENTION -> PushNotification.Type.MENTION;
|
||||
case REBLOG -> PushNotification.Type.REBLOG;
|
||||
case FAVORITE -> PushNotification.Type.FAVORITE;
|
||||
case POLL -> PushNotification.Type.POLL;
|
||||
case STATUS -> PushNotification.Type.STATUS;
|
||||
case UPDATE -> PushNotification.Type.UPDATE;
|
||||
case SIGN_UP -> PushNotification.Type.SIGN_UP;
|
||||
case REPORT -> PushNotification.Type.REPORT;
|
||||
//Follow request, and reactions are not supported by the API
|
||||
default -> throw new IllegalStateException("Unexpected value: "+notification.type);
|
||||
};
|
||||
|
||||
String notificationTitle = context.getString(switch(notification.type){
|
||||
case FOLLOW -> R.string.user_followed_you;
|
||||
case MENTION -> R.string.sk_notification_mention;
|
||||
case REBLOG -> R.string.notification_boosted;
|
||||
case FAVORITE -> R.string.user_favorited;
|
||||
case POLL -> R.string.poll_ended;
|
||||
case UPDATE -> R.string.sk_post_edited;
|
||||
case SIGN_UP -> R.string.sk_signed_up;
|
||||
case REPORT -> R.string.sk_reported;
|
||||
default -> throw new IllegalStateException("Unexpected value: "+notification.type);
|
||||
});
|
||||
|
||||
pushNotification.title = UiUtils.generateFormattedString(notificationTitle, notification.account.displayName).toString();
|
||||
pushNotification.icon = notification.status.account.avatarStatic;
|
||||
pushNotification.body = notification.status.getStrippedText();
|
||||
return pushNotification;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
return "PushNotification{"+
|
||||
@@ -43,7 +80,15 @@ public class PushNotification extends BaseModel{
|
||||
@SerializedName("follow")
|
||||
FOLLOW(R.string.notification_type_follow),
|
||||
@SerializedName("poll")
|
||||
POLL(R.string.notification_type_poll);
|
||||
POLL(R.string.notification_type_poll),
|
||||
@SerializedName("status")
|
||||
STATUS(R.string.sk_notification_type_status),
|
||||
@SerializedName("update")
|
||||
UPDATE(R.string.sk_notification_type_update),
|
||||
@SerializedName("admin.sign_up")
|
||||
SIGN_UP(R.string.sk_sign_ups),
|
||||
@SerializedName("admin.report")
|
||||
REPORT(R.string.sk_new_reports);
|
||||
|
||||
@StringRes
|
||||
public final int localizedName;
|
||||
|
||||
@@ -41,10 +41,20 @@ public class PushSubscription extends BaseModel implements Cloneable{
|
||||
public boolean reblog;
|
||||
public boolean mention;
|
||||
public boolean poll;
|
||||
public boolean status;
|
||||
public boolean update;
|
||||
|
||||
// set to true here because i didn't add any items for those to the settings
|
||||
// (so i don't have to determine whether the user is an admin to show the items or not, and
|
||||
// admins can still disable those through the android notifications settings)
|
||||
@SerializedName("admin.sign_up")
|
||||
public boolean adminSignUp = true;
|
||||
@SerializedName("admin.report")
|
||||
public boolean adminReport = true;
|
||||
|
||||
public static Alerts ofAll(){
|
||||
Alerts alerts=new Alerts();
|
||||
alerts.follow=alerts.favourite=alerts.reblog=alerts.mention=alerts.poll=true;
|
||||
alerts.follow=alerts.favourite=alerts.reblog=alerts.mention=alerts.poll=alerts.status=alerts.update=true;
|
||||
return alerts;
|
||||
}
|
||||
|
||||
@@ -56,6 +66,10 @@ public class PushSubscription extends BaseModel implements Cloneable{
|
||||
", reblog="+reblog+
|
||||
", mention="+mention+
|
||||
", poll="+poll+
|
||||
", status="+status+
|
||||
", update="+update+
|
||||
", adminSignUp="+adminSignUp+
|
||||
", adminReport="+adminReport+
|
||||
'}';
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
package org.joinmastodon.android.model;
|
||||
|
||||
import org.joinmastodon.android.api.ObjectValidationException;
|
||||
import org.joinmastodon.android.api.RequiredField;
|
||||
import org.joinmastodon.android.model.Poll.Option;
|
||||
import org.parceler.Parcel;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Parcel
|
||||
public class ScheduledStatus extends BaseModel implements DisplayItemsParent{
|
||||
@RequiredField
|
||||
public String id;
|
||||
@RequiredField
|
||||
public Instant scheduledAt;
|
||||
@RequiredField
|
||||
public Params params;
|
||||
public List<Attachment> mediaAttachments;
|
||||
|
||||
@Override
|
||||
public String getID() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postprocess() throws ObjectValidationException {
|
||||
super.postprocess();
|
||||
if(mediaAttachments==null) mediaAttachments=List.of();
|
||||
for(Attachment a:mediaAttachments)
|
||||
a.postprocess();
|
||||
if(params!=null) params.postprocess();
|
||||
}
|
||||
|
||||
@Parcel
|
||||
public static class Params extends BaseModel {
|
||||
@RequiredField
|
||||
public String text;
|
||||
public String spoilerText;
|
||||
@RequiredField
|
||||
public StatusPrivacy visibility;
|
||||
public long inReplyToId;
|
||||
public ScheduledPoll poll;
|
||||
public boolean sensitive;
|
||||
public boolean withRateLimit;
|
||||
public String language;
|
||||
public String idempotency;
|
||||
public String applicationId;
|
||||
public List<String> mediaIds;
|
||||
public ContentType contentType;
|
||||
|
||||
@Override
|
||||
public void postprocess() throws ObjectValidationException {
|
||||
super.postprocess();
|
||||
if(poll!=null) poll.postprocess();
|
||||
}
|
||||
}
|
||||
|
||||
@Parcel
|
||||
public static class ScheduledPoll extends BaseModel {
|
||||
@RequiredField
|
||||
public String expiresIn;
|
||||
@RequiredField
|
||||
public List<String> options;
|
||||
public boolean multiple;
|
||||
public boolean hideTotals;
|
||||
|
||||
public Poll toPoll() {
|
||||
Poll p=new Poll();
|
||||
p.voted=true;
|
||||
p.emojis=List.of();
|
||||
p.ownVotes=List.of();
|
||||
p.multiple=multiple;
|
||||
p.options=options.stream().map(Option::new).collect(Collectors.toList());
|
||||
p.expiresAt=Instant.now().plus(Integer.parseInt(expiresIn)+1, ChronoUnit.SECONDS);
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
public Status toStatus() {
|
||||
Status s=Status.ofFake(id, params.text, scheduledAt);
|
||||
s.mediaAttachments=mediaAttachments;
|
||||
s.inReplyToId=params.inReplyToId>0 ? ""+params.inReplyToId : null;
|
||||
s.spoilerText=params.spoilerText;
|
||||
s.visibility=params.visibility;
|
||||
s.language=params.language;
|
||||
s.sensitive=params.sensitive;
|
||||
if(params.poll!=null) s.poll=params.poll.toPoll();
|
||||
return s;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package org.joinmastodon.android.model;
|
||||
|
||||
public interface Searchable {
|
||||
String getQuery();
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package org.joinmastodon.android.model;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import org.parceler.Parcel;
|
||||
|
||||
public enum Severity {
|
||||
@SerializedName("silence")
|
||||
SILENCE,
|
||||
@SerializedName("suspend")
|
||||
SUSPEND
|
||||
}
|
||||
@@ -1,10 +1,17 @@
|
||||
package org.joinmastodon.android.model;
|
||||
|
||||
import static org.joinmastodon.android.api.MastodonAPIController.gson;
|
||||
import static org.joinmastodon.android.api.MastodonAPIController.gsonWithoutDeserializer;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import android.util.Pair;
|
||||
|
||||
import org.joinmastodon.android.api.ObjectValidationException;
|
||||
import org.joinmastodon.android.api.RequiredField;
|
||||
import org.joinmastodon.android.events.StatusCountersUpdatedEvent;
|
||||
import org.joinmastodon.android.events.StatusMuteChangedEvent;
|
||||
import org.joinmastodon.android.ui.text.HtmlParser;
|
||||
import org.parceler.Parcel;
|
||||
|
||||
@@ -15,13 +22,30 @@ import java.util.Objects;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.github.bottomSoftwareFoundation.bottom.Bottom;
|
||||
import com.github.bottomSoftwareFoundation.bottom.TranslationError;
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
|
||||
import org.joinmastodon.android.api.session.AccountSession;
|
||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||
import org.joinmastodon.android.events.EmojiReactionsUpdatedEvent;
|
||||
import org.joinmastodon.android.utils.StatusTextEncoder;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@Parcel
|
||||
public class Status extends BaseModel implements DisplayItemsParent{
|
||||
public class Status extends BaseModel implements DisplayItemsParent, Searchable{
|
||||
@RequiredField
|
||||
public String id;
|
||||
@RequiredField
|
||||
public String uri;
|
||||
@RequiredField
|
||||
// @RequiredField // sometimes null on calckey
|
||||
public Instant createdAt;
|
||||
@RequiredField
|
||||
public Account account;
|
||||
@@ -32,7 +56,6 @@ public class Status extends BaseModel implements DisplayItemsParent{
|
||||
public boolean sensitive;
|
||||
@RequiredField
|
||||
public String spoilerText="";
|
||||
@RequiredField
|
||||
public List<Attachment> mediaAttachments;
|
||||
public Application application;
|
||||
@RequiredField
|
||||
@@ -45,6 +68,7 @@ public class Status extends BaseModel implements DisplayItemsParent{
|
||||
public long favouritesCount;
|
||||
public long repliesCount;
|
||||
public Instant editedAt;
|
||||
public List<FilterResult> filtered;
|
||||
|
||||
public String url;
|
||||
public String inReplyToId;
|
||||
@@ -54,7 +78,9 @@ public class Status extends BaseModel implements DisplayItemsParent{
|
||||
public Card card;
|
||||
public String language;
|
||||
public String text;
|
||||
public List<FilterResult> filtered;
|
||||
@Nullable
|
||||
public Account rebloggedBy;
|
||||
public boolean localOnly;
|
||||
|
||||
public boolean favourited;
|
||||
public boolean reblogged;
|
||||
@@ -62,11 +88,21 @@ public class Status extends BaseModel implements DisplayItemsParent{
|
||||
public boolean bookmarked;
|
||||
public boolean pinned;
|
||||
|
||||
public Status quote; // can be boolean in calckey
|
||||
|
||||
public List<EmojiReaction> reactions;
|
||||
protected List<EmojiReaction> emojiReactions; // akkoma
|
||||
|
||||
public transient boolean filterRevealed;
|
||||
public transient boolean spoilerRevealed;
|
||||
public transient boolean hasGapAfter;
|
||||
public transient boolean sensitiveRevealed;
|
||||
public transient boolean textExpanded, textExpandable;
|
||||
public transient String hasGapAfter;
|
||||
private transient String strippedText;
|
||||
public transient TranslationState translationState=TranslationState.HIDDEN;
|
||||
public transient Translation translation;
|
||||
public transient boolean fromStatusCreated;
|
||||
public transient boolean preview;
|
||||
|
||||
public Status(){}
|
||||
|
||||
@@ -81,6 +117,7 @@ public class Status extends BaseModel implements DisplayItemsParent{
|
||||
t.postprocess();
|
||||
for(Emoji e:emojis)
|
||||
e.postprocess();
|
||||
if (mediaAttachments == null) mediaAttachments=List.of();
|
||||
for(Attachment a:mediaAttachments)
|
||||
a.postprocess();
|
||||
account.postprocess();
|
||||
@@ -90,12 +127,18 @@ public class Status extends BaseModel implements DisplayItemsParent{
|
||||
card.postprocess();
|
||||
if(reblog!=null)
|
||||
reblog.postprocess();
|
||||
if(filtered!=null){
|
||||
if(filtered!=null)
|
||||
for(FilterResult fr:filtered)
|
||||
fr.postprocess();
|
||||
}
|
||||
if(quote!=null)
|
||||
quote.postprocess();
|
||||
|
||||
spoilerRevealed=!sensitive;
|
||||
spoilerRevealed=!hasSpoiler();
|
||||
if(!spoilerRevealed) sensitive=true;
|
||||
sensitiveRevealed=!sensitive;
|
||||
if(visibility.equals(StatusPrivacy.LOCAL)) localOnly=true;
|
||||
if(emojiReactions!=null) reactions=emojiReactions;
|
||||
if(reactions==null) reactions=new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -155,18 +198,35 @@ public class Status extends BaseModel implements DisplayItemsParent{
|
||||
favourited=ev.favorited;
|
||||
reblogged=ev.reblogged;
|
||||
bookmarked=ev.bookmarked;
|
||||
pinned=ev.pinned;
|
||||
}
|
||||
|
||||
public void update(StatusMuteChangedEvent ev) {
|
||||
muted=ev.muted;
|
||||
}
|
||||
|
||||
public void update(EmojiReactionsUpdatedEvent ev){
|
||||
reactions=ev.reactions;
|
||||
}
|
||||
|
||||
public Status getContentStatus(){
|
||||
return reblog!=null ? reblog : this;
|
||||
}
|
||||
|
||||
public String getContentStatusID(){
|
||||
return getContentStatus().id;
|
||||
}
|
||||
|
||||
public String getStrippedText(){
|
||||
if(strippedText==null)
|
||||
strippedText=HtmlParser.strip(content);
|
||||
return strippedText;
|
||||
}
|
||||
|
||||
public boolean hasSpoiler(){
|
||||
return !TextUtils.isEmpty(spoilerText);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Status clone(){
|
||||
@@ -176,8 +236,30 @@ public class Status extends BaseModel implements DisplayItemsParent{
|
||||
return copy;
|
||||
}
|
||||
|
||||
public boolean isEligibleForTranslation(){
|
||||
return !TextUtils.isEmpty(content) && !TextUtils.isEmpty(language) && !Objects.equals(Locale.getDefault().getLanguage(), language)
|
||||
public static final Pattern BOTTOM_TEXT_PATTERN = Pattern.compile("(?:[\uD83E\uDEC2\uD83D\uDC96✨\uD83E\uDD7A,]+|❤️)(?:\uD83D\uDC49\uD83D\uDC48(?:[\uD83E\uDEC2\uD83D\uDC96✨\uD83E\uDD7A,]+|❤️))*\uD83D\uDC49\uD83D\uDC48");
|
||||
public boolean isEligibleForTranslation(AccountSession session){
|
||||
Instance instanceInfo=AccountSessionManager.getInstance().getInstanceInfo(session.domain);
|
||||
boolean translateEnabled=instanceInfo!=null && (
|
||||
(instanceInfo.v2!=null && instanceInfo.v2.configuration.translation!=null && instanceInfo.v2.configuration.translation.enabled) ||
|
||||
(instanceInfo.isAkkoma() && instanceInfo.hasFeature(Instance.Feature.MACHINE_TRANSLATION))
|
||||
);
|
||||
|
||||
try {
|
||||
Pair<String, List<String>> decoded=BOTTOM_TEXT_PATTERN.matcher(getStrippedText()).find()
|
||||
? new StatusTextEncoder(Bottom::decode).decode(getStrippedText(), BOTTOM_TEXT_PATTERN)
|
||||
: null;
|
||||
String bottomText=decoded==null || decoded.second.stream().allMatch(s->s.trim().isEmpty()) ? null : decoded.first;
|
||||
if(bottomText!=null){
|
||||
translation=new Translation();
|
||||
translation.content=bottomText;
|
||||
translation.detectedSourceLanguage="\uD83E\uDD7A\uD83D\uDC49\uD83D\uDC48";
|
||||
translation.provider="bottom-java";
|
||||
return true;
|
||||
}
|
||||
} catch (TranslationError ignored) {}
|
||||
|
||||
return translateEnabled && !TextUtils.isEmpty(content) && !TextUtils.isEmpty(language)
|
||||
&& !Objects.equals(Locale.getDefault().getLanguage(), language)
|
||||
&& (visibility==StatusPrivacy.PUBLIC || visibility==StatusPrivacy.UNLISTED);
|
||||
}
|
||||
|
||||
@@ -186,4 +268,54 @@ public class Status extends BaseModel implements DisplayItemsParent{
|
||||
SHOWN,
|
||||
LOADING
|
||||
}
|
||||
|
||||
public boolean isReblogPermitted(String accountID){
|
||||
return visibility.isReblogPermitted(account.id.equals(
|
||||
AccountSessionManager.getInstance().getAccount(accountID).self.id
|
||||
));
|
||||
}
|
||||
|
||||
public static Status ofFake(String id, String text, Instant createdAt) {
|
||||
Status s=new Status();
|
||||
s.id=id;
|
||||
s.mediaAttachments=List.of();
|
||||
s.createdAt=createdAt;
|
||||
s.content=s.text=text;
|
||||
s.spoilerText="";
|
||||
s.visibility=StatusPrivacy.PUBLIC;
|
||||
s.reactions=List.of();
|
||||
s.mentions=List.of();
|
||||
s.tags=List.of();
|
||||
s.emojis=List.of();
|
||||
s.filtered=List.of();
|
||||
return s;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getQuery() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public static class StatusDeserializer implements JsonDeserializer<Status> {
|
||||
@Override
|
||||
public Status deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
|
||||
JsonObject obj=json.getAsJsonObject();
|
||||
|
||||
Status quote=null;
|
||||
if (obj.has("quote") && obj.get("quote").isJsonObject())
|
||||
quote=gson.fromJson(obj.get("quote"), Status.class);
|
||||
obj.remove("quote");
|
||||
|
||||
Status reblog=null;
|
||||
if (obj.has("reblog"))
|
||||
reblog=gson.fromJson(obj.get("reblog"), Status.class);
|
||||
obj.remove("reblog");
|
||||
|
||||
Status status=gsonWithoutDeserializer.fromJson(json, Status.class);
|
||||
status.quote=quote;
|
||||
status.reblog=reblog;
|
||||
|
||||
return status;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,9 +10,11 @@ public enum StatusPrivacy{
|
||||
@SerializedName("private")
|
||||
PRIVATE(2),
|
||||
@SerializedName("direct")
|
||||
DIRECT(3);
|
||||
DIRECT(3),
|
||||
@SerializedName("local")
|
||||
LOCAL(4); // akkoma
|
||||
|
||||
private int privacy;
|
||||
private final int privacy;
|
||||
|
||||
StatusPrivacy(int privacy) {
|
||||
this.privacy = privacy;
|
||||
@@ -22,6 +24,13 @@ public enum StatusPrivacy{
|
||||
return privacy > other.getPrivacy();
|
||||
}
|
||||
|
||||
public boolean isReblogPermitted(boolean isOwnStatus){
|
||||
return (this == StatusPrivacy.PUBLIC ||
|
||||
this == StatusPrivacy.UNLISTED ||
|
||||
this == StatusPrivacy.LOCAL ||
|
||||
(this == StatusPrivacy.PRIVATE && isOwnStatus));
|
||||
}
|
||||
|
||||
public int getPrivacy() {
|
||||
return privacy;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,421 @@
|
||||
package org.joinmastodon.android.model;
|
||||
|
||||
import android.app.Fragment;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StringRes;
|
||||
|
||||
import org.joinmastodon.android.BuildConfig;
|
||||
import org.joinmastodon.android.R;
|
||||
import org.joinmastodon.android.fragments.CustomLocalTimelineFragment;
|
||||
import org.joinmastodon.android.api.session.AccountSession;
|
||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||
import org.joinmastodon.android.fragments.BookmarkedStatusListFragment;
|
||||
import org.joinmastodon.android.fragments.FavoritedStatusListFragment;
|
||||
import org.joinmastodon.android.fragments.HashtagTimelineFragment;
|
||||
import org.joinmastodon.android.fragments.HomeTimelineFragment;
|
||||
import org.joinmastodon.android.fragments.ListTimelineFragment;
|
||||
import org.joinmastodon.android.fragments.NotificationsListFragment;
|
||||
import org.joinmastodon.android.fragments.discover.BubbleTimelineFragment;
|
||||
import org.joinmastodon.android.fragments.discover.FederatedTimelineFragment;
|
||||
import org.joinmastodon.android.fragments.discover.LocalTimelineFragment;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class TimelineDefinition {
|
||||
private TimelineType type;
|
||||
private String title;
|
||||
private @Nullable Icon icon;
|
||||
|
||||
private @Nullable String listId;
|
||||
private @Nullable String listTitle;
|
||||
private boolean listIsExclusive;
|
||||
|
||||
private @Nullable String domain;
|
||||
private @Nullable String hashtagName;
|
||||
private @Nullable List<String> hashtagAny;
|
||||
private @Nullable List<String> hashtagAll;
|
||||
private @Nullable List<String> hashtagNone;
|
||||
private boolean hashtagLocalOnly;
|
||||
|
||||
public static TimelineDefinition ofList(String listId, String listTitle, boolean listIsExclusive) {
|
||||
TimelineDefinition def = new TimelineDefinition(TimelineType.LIST);
|
||||
def.listId = listId;
|
||||
def.listTitle = listTitle;
|
||||
def.listIsExclusive = listIsExclusive;
|
||||
return def;
|
||||
}
|
||||
|
||||
public static TimelineDefinition ofList(ListTimeline list) {
|
||||
return ofList(list.id, list.title, list.exclusive);
|
||||
}
|
||||
|
||||
public static TimelineDefinition ofHashtag(String hashtag) {
|
||||
TimelineDefinition def = new TimelineDefinition(TimelineType.HASHTAG);
|
||||
def.hashtagName = hashtag;
|
||||
return def;
|
||||
}
|
||||
|
||||
public static TimelineDefinition ofCustomLocalTimeline(String domain) {
|
||||
TimelineDefinition def = new TimelineDefinition(TimelineType.CUSTOM_LOCAL_TIMELINE);
|
||||
def.domain = domain;
|
||||
return def;
|
||||
}
|
||||
|
||||
public static TimelineDefinition ofHashtag(Hashtag hashtag) {
|
||||
return ofHashtag(hashtag.name);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public TimelineDefinition() {}
|
||||
|
||||
public TimelineDefinition(TimelineType type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public boolean isCompatible(AccountSession session) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean wantsDefault(AccountSession session) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public String getTitle(Context ctx) {
|
||||
return title != null ? title : getDefaultTitle(ctx);
|
||||
}
|
||||
|
||||
public String getCustomTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getHashtagName() {
|
||||
return hashtagName;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public List<String> getHashtagAny() {
|
||||
return hashtagAny;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public List<String> getHashtagAll() {
|
||||
return hashtagAll;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public List<String> getHashtagNone() {
|
||||
return hashtagNone;
|
||||
}
|
||||
|
||||
public boolean isHashtagLocalOnly() {
|
||||
return hashtagLocalOnly;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title == null || title.isBlank() ? null : title;
|
||||
}
|
||||
|
||||
private List<String> sanitizeTagList(List<String> tags) {
|
||||
return tags.stream()
|
||||
.map(String::trim)
|
||||
.filter(str -> !TextUtils.isEmpty(str))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public void setTagOptions(String main, List<String> any, List<String> all, List<String> none, boolean localOnly) {
|
||||
this.hashtagName = main;
|
||||
this.hashtagAny = sanitizeTagList(any);
|
||||
this.hashtagAll = sanitizeTagList(all);
|
||||
this.hashtagNone = sanitizeTagList(none);
|
||||
this.hashtagLocalOnly = localOnly;
|
||||
}
|
||||
|
||||
|
||||
public String getDefaultTitle(Context ctx) {
|
||||
return switch (type) {
|
||||
case HOME -> ctx.getString(R.string.sk_timeline_home);
|
||||
case LOCAL -> ctx.getString(R.string.sk_timeline_local);
|
||||
case FEDERATED -> ctx.getString(R.string.sk_timeline_federated);
|
||||
case POST_NOTIFICATIONS -> ctx.getString(R.string.sk_timeline_posts);
|
||||
case LIST -> listTitle;
|
||||
case HASHTAG -> hashtagName;
|
||||
case BUBBLE -> ctx.getString(R.string.sk_timeline_bubble);
|
||||
case BOOKMARKS -> ctx.getString(R.string.bookmarks);
|
||||
case FAVORITES -> ctx.getString(R.string.your_favorites);
|
||||
case CUSTOM_LOCAL_TIMELINE -> domain;
|
||||
};
|
||||
}
|
||||
|
||||
public Icon getDefaultIcon() {
|
||||
return switch (type) {
|
||||
case HOME -> Icon.HOME;
|
||||
case LOCAL -> Icon.LOCAL;
|
||||
case FEDERATED -> Icon.FEDERATED;
|
||||
case POST_NOTIFICATIONS -> Icon.POST_NOTIFICATIONS;
|
||||
case LIST -> listIsExclusive ? Icon.EXCLUSIVE_LIST : Icon.LIST;
|
||||
case HASHTAG -> Icon.HASHTAG;
|
||||
case CUSTOM_LOCAL_TIMELINE -> Icon.CUSTOM_LOCAL_TIMELINE;
|
||||
case BUBBLE -> Icon.BUBBLE;
|
||||
case BOOKMARKS -> Icon.BOOKMARKS;
|
||||
case FAVORITES -> Icon.FAVORITES;
|
||||
};
|
||||
}
|
||||
|
||||
public Fragment getFragment() {
|
||||
return switch (type) {
|
||||
case HOME -> new HomeTimelineFragment();
|
||||
case LOCAL -> new LocalTimelineFragment();
|
||||
case FEDERATED -> new FederatedTimelineFragment();
|
||||
case LIST -> new ListTimelineFragment();
|
||||
case HASHTAG -> new HashtagTimelineFragment();
|
||||
case POST_NOTIFICATIONS -> new NotificationsListFragment();
|
||||
case BUBBLE -> new BubbleTimelineFragment();
|
||||
case CUSTOM_LOCAL_TIMELINE -> new CustomLocalTimelineFragment();
|
||||
case BOOKMARKS -> new BookmarkedStatusListFragment();
|
||||
case FAVORITES -> new FavoritedStatusListFragment();
|
||||
};
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Icon getIcon() {
|
||||
return icon == null ? getDefaultIcon() : icon;
|
||||
}
|
||||
|
||||
public void setIcon(@Nullable Icon icon) {
|
||||
this.icon = icon;
|
||||
}
|
||||
|
||||
public TimelineType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
TimelineDefinition that = (TimelineDefinition) o;
|
||||
if (type != that.type) return false;
|
||||
if (type == TimelineType.LIST) return Objects.equals(listId, that.listId);
|
||||
if (type == TimelineType.CUSTOM_LOCAL_TIMELINE) return Objects.equals(domain.toLowerCase(), that.domain.toLowerCase());
|
||||
if (type == TimelineType.HASHTAG) {
|
||||
if (hashtagName == null && that.hashtagName == null) return true;
|
||||
if (hashtagName == null || that.hashtagName == null) return false;
|
||||
return Objects.equals(hashtagName.toLowerCase(), that.hashtagName.toLowerCase());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(type, title, listId, hashtagName, hashtagAny, hashtagAll, hashtagNone);
|
||||
}
|
||||
|
||||
public TimelineDefinition copy() {
|
||||
TimelineDefinition def = new TimelineDefinition(type);
|
||||
def.title = title;
|
||||
def.listId = listId;
|
||||
def.listTitle = listTitle;
|
||||
def.listIsExclusive = listIsExclusive;
|
||||
def.hashtagName = hashtagName;
|
||||
def.domain = domain;
|
||||
def.hashtagAny = hashtagAny;
|
||||
def.hashtagAll = hashtagAll;
|
||||
def.hashtagNone = hashtagNone;
|
||||
def.icon = icon == null ? null : Icon.values()[icon.ordinal()];
|
||||
return def;
|
||||
}
|
||||
|
||||
public Bundle populateArguments(Bundle args) {
|
||||
if (type == TimelineType.LIST) {
|
||||
args.putString("listTitle", title);
|
||||
args.putString("listID", listId);
|
||||
args.putBoolean("listIsExclusive", listIsExclusive);
|
||||
} else if (type == TimelineType.HASHTAG) {
|
||||
args.putString("hashtagName", hashtagName);
|
||||
args.putBoolean("localOnly", hashtagLocalOnly);
|
||||
args.putStringArrayList("any", hashtagAny == null ? new ArrayList<>() : new ArrayList<>(hashtagAny));
|
||||
args.putStringArrayList("all", hashtagAll == null ? new ArrayList<>() : new ArrayList<>(hashtagAll));
|
||||
args.putStringArrayList("none", hashtagNone == null ? new ArrayList<>() : new ArrayList<>(hashtagNone));
|
||||
} else if (type == TimelineType.CUSTOM_LOCAL_TIMELINE) {
|
||||
args.putString("domain", domain);
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
public enum TimelineType {
|
||||
HOME,
|
||||
LOCAL,
|
||||
FEDERATED,
|
||||
POST_NOTIFICATIONS,
|
||||
LIST,
|
||||
HASHTAG,
|
||||
BUBBLE,
|
||||
CUSTOM_LOCAL_TIMELINE,
|
||||
|
||||
// not really timelines, but some people want it, so,,
|
||||
BOOKMARKS,
|
||||
FAVORITES
|
||||
}
|
||||
|
||||
public enum Icon {
|
||||
HEART(R.drawable.ic_fluent_heart_24_regular, R.string.sk_icon_heart),
|
||||
STAR(R.drawable.ic_fluent_star_24_regular, R.string.sk_icon_star),
|
||||
PEOPLE(R.drawable.ic_fluent_people_24_regular, R.string.sk_icon_people),
|
||||
CITY(R.drawable.ic_fluent_city_24_regular, R.string.sk_icon_city),
|
||||
IMAGE(R.drawable.ic_fluent_image_24_regular, R.string.sk_icon_image),
|
||||
NEWS(R.drawable.ic_fluent_news_24_regular, R.string.sk_icon_news),
|
||||
FEED(R.drawable.ic_fluent_rss_24_regular, R.string.sk_icon_feed),
|
||||
COLOR_PALETTE(R.drawable.ic_fluent_color_24_regular, R.string.sk_icon_color_palette),
|
||||
CAT(R.drawable.ic_fluent_animal_cat_24_regular, R.string.sk_icon_cat),
|
||||
DOG(R.drawable.ic_fluent_animal_dog_24_regular, R.string.sk_icon_dog),
|
||||
RABBIT(R.drawable.ic_fluent_animal_rabbit_24_regular, R.string.sk_icon_rabbit),
|
||||
TURTLE(R.drawable.ic_fluent_animal_turtle_24_regular, R.string.sk_icon_turtle),
|
||||
ACADEMIC_CAP(R.drawable.ic_fluent_hat_graduation_24_regular, R.string.sk_icon_academic_cap),
|
||||
BOT(R.drawable.ic_fluent_bot_24_regular, R.string.sk_icon_bot),
|
||||
IMPORTANT(R.drawable.ic_fluent_important_24_regular, R.string.sk_icon_important),
|
||||
PIN(R.drawable.ic_fluent_pin_24_regular, R.string.sk_icon_pin),
|
||||
SHIELD(R.drawable.ic_fluent_shield_24_regular, R.string.sk_icon_shield),
|
||||
CHAT(R.drawable.ic_fluent_chat_multiple_24_regular, R.string.sk_icon_chat),
|
||||
TAG(R.drawable.ic_fluent_tag_24_regular, R.string.sk_icon_tag),
|
||||
TRAIN(R.drawable.ic_fluent_vehicle_subway_24_regular, R.string.sk_icon_train),
|
||||
BICYCLE(R.drawable.ic_fluent_vehicle_bicycle_24_regular, R.string.sk_icon_bicycle),
|
||||
MAP(R.drawable.ic_fluent_map_24_regular, R.string.sk_icon_map),
|
||||
BACKPACK(R.drawable.ic_fluent_backpack_24_regular, R.string.sk_icon_backpack),
|
||||
BRIEFCASE(R.drawable.ic_fluent_briefcase_24_regular, R.string.sk_icon_briefcase),
|
||||
BOOK(R.drawable.ic_fluent_book_open_24_regular, R.string.sk_icon_book),
|
||||
LANGUAGE(R.drawable.ic_fluent_local_language_24_regular, R.string.sk_icon_language),
|
||||
WEATHER(R.drawable.ic_fluent_weather_rain_showers_day_24_regular, R.string.sk_icon_weather),
|
||||
APERTURE(R.drawable.ic_fluent_scan_24_regular, R.string.sk_icon_aperture),
|
||||
MUSIC(R.drawable.ic_fluent_music_note_2_24_regular, R.string.sk_icon_music),
|
||||
LOCATION(R.drawable.ic_fluent_location_24_regular, R.string.sk_icon_location),
|
||||
GLOBE(R.drawable.ic_fluent_globe_24_regular, R.string.sk_icon_globe),
|
||||
MEGAPHONE(R.drawable.ic_fluent_megaphone_loud_24_regular, R.string.sk_icon_megaphone),
|
||||
MICROPHONE(R.drawable.ic_fluent_mic_24_regular, R.string.sk_icon_microphone),
|
||||
MICROSCOPE(R.drawable.ic_fluent_microscope_24_regular, R.string.sk_icon_microscope),
|
||||
STETHOSCOPE(R.drawable.ic_fluent_stethoscope_24_regular, R.string.sk_icon_stethoscope),
|
||||
KEYBOARD(R.drawable.ic_fluent_midi_24_regular, R.string.sk_icon_keyboard),
|
||||
COFFEE(R.drawable.ic_fluent_drink_coffee_24_regular, R.string.sk_icon_coffee),
|
||||
CLAPPER_BOARD(R.drawable.ic_fluent_movies_and_tv_24_regular, R.string.sk_icon_clapper_board),
|
||||
LAUGH(R.drawable.ic_fluent_emoji_laugh_24_regular, R.string.sk_icon_laugh),
|
||||
BALLOON(R.drawable.ic_fluent_balloon_24_regular, R.string.sk_icon_balloon),
|
||||
PI(R.drawable.ic_fluent_pi_24_regular, R.string.sk_icon_pi),
|
||||
MATH_FORMULA(R.drawable.ic_fluent_math_formula_24_regular, R.string.sk_icon_math_formula),
|
||||
GAMES(R.drawable.ic_fluent_games_24_regular, R.string.sk_icon_games),
|
||||
CODE(R.drawable.ic_fluent_code_24_regular, R.string.sk_icon_code),
|
||||
BUG(R.drawable.ic_fluent_bug_24_regular, R.string.sk_icon_bug),
|
||||
LIGHT_BULB(R.drawable.ic_fluent_lightbulb_24_regular, R.string.sk_icon_light_bulb),
|
||||
FIRE(R.drawable.ic_fluent_fire_24_regular, R.string.sk_icon_fire),
|
||||
LEAVES(R.drawable.ic_fluent_leaf_three_24_regular, R.string.sk_icon_leaves),
|
||||
SPORT(R.drawable.ic_fluent_sport_24_regular, R.string.sk_icon_sport),
|
||||
HEALTH(R.drawable.ic_fluent_heart_pulse_24_regular, R.string.sk_icon_health),
|
||||
PIZZA(R.drawable.ic_fluent_food_pizza_24_regular, R.string.sk_icon_pizza),
|
||||
GAVEL(R.drawable.ic_fluent_gavel_24_regular, R.string.sk_icon_gavel),
|
||||
GAUGE(R.drawable.ic_fluent_gauge_24_regular, R.string.sk_icon_gauge),
|
||||
HEADPHONES(R.drawable.ic_fluent_headphones_sound_wave_24_regular, R.string.sk_icon_headphones),
|
||||
HUMAN(R.drawable.ic_fluent_accessibility_24_regular, R.string.sk_icon_human),
|
||||
BEAKER(R.drawable.ic_fluent_beaker_24_regular, R.string.sk_icon_beaker),
|
||||
BED(R.drawable.ic_fluent_bed_24_regular, R.string.sk_icon_bed),
|
||||
RECYCLE_BIN(R.drawable.ic_fluent_bin_recycle_24_regular, R.string.sk_icon_recycle_bin),
|
||||
VERIFIED(R.drawable.ic_fluent_checkmark_starburst_24_regular, R.string.sk_icon_verified),
|
||||
DOCTOR(R.drawable.ic_fluent_doctor_24_regular, R.string.sk_icon_doctor),
|
||||
DIAMOND(R.drawable.ic_fluent_premium_24_regular, R.string.sk_icon_diamond),
|
||||
UMBRELLA(R.drawable.ic_fluent_umbrella_24_regular, R.string.sk_icon_umbrella),
|
||||
WATER(R.drawable.ic_fluent_water_24_regular, R.string.sk_icon_water),
|
||||
SUN(R.drawable.ic_fluent_weather_sunny_24_regular, R.string.sk_icon_sun),
|
||||
SUNSET(R.drawable.ic_fluent_weather_sunny_low_24_regular, R.string.sk_icon_sunset),
|
||||
CLOUD(R.drawable.ic_fluent_cloud_24_regular, R.string.sk_icon_cloud),
|
||||
THUNDERSTORM(R.drawable.ic_fluent_weather_thunderstorm_24_regular, R.string.sk_icon_thunderstorm),
|
||||
RAIN(R.drawable.ic_fluent_weather_rain_24_regular, R.string.sk_icon_rain),
|
||||
SNOWFLAKE(R.drawable.ic_fluent_weather_snowflake_24_regular, R.string.sk_icon_snowflake),
|
||||
|
||||
HOME(R.drawable.ic_fluent_home_24_regular, R.string.sk_timeline_home, true),
|
||||
LOCAL(R.drawable.ic_fluent_people_community_24_regular, R.string.sk_timeline_local, true),
|
||||
FEDERATED(R.drawable.ic_fluent_earth_24_regular, R.string.sk_timeline_federated, true),
|
||||
POST_NOTIFICATIONS(R.drawable.ic_fluent_chat_24_regular, R.string.sk_timeline_posts, true),
|
||||
LIST(R.drawable.ic_fluent_people_24_regular, R.string.sk_list, true),
|
||||
EXCLUSIVE_LIST(R.drawable.ic_fluent_rss_24_regular, R.string.sk_exclusive_list, true),
|
||||
HASHTAG(R.drawable.ic_fluent_number_symbol_24_regular, R.string.sk_hashtag, true),
|
||||
CUSTOM_LOCAL_TIMELINE(R.drawable.ic_fluent_people_community_24_regular, R.string.sk_timeline_local, true),
|
||||
BUBBLE(R.drawable.ic_fluent_circle_24_regular, R.string.sk_timeline_bubble, true),
|
||||
BOOKMARKS(R.drawable.ic_fluent_bookmark_multiple_24_regular, R.string.bookmarks, true),
|
||||
FAVORITES(R.drawable.ic_fluent_star_24_regular, R.string.your_favorites, true);
|
||||
|
||||
public final int iconRes, nameRes;
|
||||
public final boolean hidden;
|
||||
|
||||
Icon(@DrawableRes int iconRes, @StringRes int nameRes) {
|
||||
this(iconRes, nameRes, false);
|
||||
}
|
||||
|
||||
Icon(@DrawableRes int iconRes, @StringRes int nameRes, boolean hidden) {
|
||||
this.iconRes = iconRes;
|
||||
this.nameRes = nameRes;
|
||||
this.hidden = hidden;
|
||||
}
|
||||
}
|
||||
|
||||
public static final TimelineDefinition HOME_TIMELINE = new TimelineDefinition(TimelineType.HOME);
|
||||
public static final TimelineDefinition LOCAL_TIMELINE = new TimelineDefinition(TimelineType.LOCAL);
|
||||
public static final TimelineDefinition FEDERATED_TIMELINE = new TimelineDefinition(TimelineType.FEDERATED);
|
||||
public static final TimelineDefinition POSTS_TIMELINE = new TimelineDefinition(TimelineType.POST_NOTIFICATIONS);
|
||||
public static final TimelineDefinition BOOKMARKS_TIMELINE = new TimelineDefinition(TimelineType.BOOKMARKS);
|
||||
public static final TimelineDefinition FAVORITES_TIMELINE = new TimelineDefinition(TimelineType.FAVORITES);
|
||||
public static final TimelineDefinition BUBBLE_TIMELINE = new TimelineDefinition(TimelineType.BUBBLE) {
|
||||
@Override
|
||||
public boolean isCompatible(AccountSession session) {
|
||||
// still enabling the bubble timeline for all pleroma/akkoma instances since i know of
|
||||
// at least one instance that supports it, but doesn't list "bubble_timeline"
|
||||
return session.getInstance().map(Instance::isAkkoma).orElse(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean wantsDefault(AccountSession session) {
|
||||
return session.getInstance()
|
||||
.map(i -> i.hasFeature(Instance.Feature.BUBBLE_TIMELINE))
|
||||
.orElse(false);
|
||||
}
|
||||
};
|
||||
|
||||
public static ArrayList<TimelineDefinition> getDefaultTimelines(String accountId) {
|
||||
AccountSession session = AccountSessionManager.getInstance().getAccount(accountId);
|
||||
return DEFAULT_TIMELINES.stream()
|
||||
.filter(tl -> tl.isCompatible(session) && tl.wantsDefault(session))
|
||||
.map(TimelineDefinition::copy)
|
||||
.collect(Collectors.toCollection(ArrayList::new));
|
||||
}
|
||||
|
||||
public static ArrayList<TimelineDefinition> getAllTimelines(String accountId) {
|
||||
AccountSession session = AccountSessionManager.getInstance().getAccount(accountId);
|
||||
return ALL_TIMELINES.stream()
|
||||
.filter(tl -> tl.isCompatible(session))
|
||||
.map(TimelineDefinition::copy)
|
||||
.collect(Collectors.toCollection(ArrayList::new));
|
||||
}
|
||||
|
||||
private static final List<TimelineDefinition> DEFAULT_TIMELINES = List.of(
|
||||
HOME_TIMELINE,
|
||||
LOCAL_TIMELINE,
|
||||
BUBBLE_TIMELINE,
|
||||
FEDERATED_TIMELINE
|
||||
);
|
||||
|
||||
private static final List<TimelineDefinition> ALL_TIMELINES = List.of(
|
||||
HOME_TIMELINE,
|
||||
LOCAL_TIMELINE,
|
||||
FEDERATED_TIMELINE,
|
||||
POSTS_TIMELINE,
|
||||
BUBBLE_TIMELINE,
|
||||
BOOKMARKS_TIMELINE,
|
||||
FAVORITES_TIMELINE
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package org.joinmastodon.android.model;
|
||||
|
||||
import org.joinmastodon.android.api.RequiredField;
|
||||
import org.parceler.Parcel;
|
||||
|
||||
@Parcel
|
||||
public class WeeklyActivity extends BaseModel {
|
||||
@RequiredField
|
||||
public String week;
|
||||
@RequiredField
|
||||
public int statuses;
|
||||
@RequiredField
|
||||
public int logins;
|
||||
@RequiredField
|
||||
public int registrations;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "WeeklyActivity{" +
|
||||
"week=" + week +
|
||||
", statuses=" + statuses +
|
||||
", logins=" + logins +
|
||||
", registrations=" + registrations +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,10 @@ package org.joinmastodon.android.model.viewmodel;
|
||||
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import org.joinmastodon.android.GlobalUserPreferences;
|
||||
import org.joinmastodon.android.api.session.AccountSession;
|
||||
import org.joinmastodon.android.api.session.AccountSessionManager;
|
||||
import org.joinmastodon.android.model.Account;
|
||||
import org.joinmastodon.android.model.AccountField;
|
||||
@@ -26,12 +28,16 @@ public class AccountViewModel{
|
||||
|
||||
public AccountViewModel(Account account, String accountID){
|
||||
this.account=account;
|
||||
avaRequest=new UrlImageLoaderRequest(GlobalUserPreferences.playGifs ? account.avatar : account.avatarStatic, V.dp(50), V.dp(50));
|
||||
AccountSession session = AccountSessionManager.get(accountID);
|
||||
avaRequest=new UrlImageLoaderRequest(
|
||||
TextUtils.isEmpty(account.avatar) ? session.getDefaultAvatarUrl() :
|
||||
GlobalUserPreferences.playGifs ? account.avatar : account.avatarStatic,
|
||||
V.dp(50), V.dp(50));
|
||||
emojiHelper=new CustomEmojiHelper();
|
||||
if(AccountSessionManager.get(accountID).getLocalPreferences().customEmojiInNames)
|
||||
parsedName=HtmlParser.parseCustomEmoji(account.displayName, account.emojis);
|
||||
if(session.getLocalPreferences().customEmojiInNames)
|
||||
parsedName=HtmlParser.parseCustomEmoji(account.getDisplayName(), account.emojis);
|
||||
else
|
||||
parsedName=account.displayName;
|
||||
parsedName=account.getDisplayName();
|
||||
parsedBio=HtmlParser.parse(account.note, account.emojis, Collections.emptyList(), Collections.emptyList(), accountID, account);
|
||||
SpannableStringBuilder ssb=new SpannableStringBuilder(parsedName);
|
||||
ssb.append(parsedBio);
|
||||
|
||||
@@ -16,7 +16,7 @@ public class SearchResultViewModel{
|
||||
switch(result.type){
|
||||
case ACCOUNT -> account=new AccountViewModel(result.account, accountID);
|
||||
case HASHTAG -> {
|
||||
hashtagItem=new ListItem<>((isRecents ? "#" : "")+result.hashtag.name, null, isRecents ? R.drawable.ic_history_24px : R.drawable.ic_tag_24px, null, result.hashtag);
|
||||
hashtagItem=new ListItem<>((isRecents ? "#" : "")+result.hashtag.name, null, isRecents ? R.drawable.ic_fluent_history_24_regular : R.drawable.ic_fluent_number_symbol_24_regular, null, result.hashtag);
|
||||
hashtagItem.isEnabled=true;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user