Merge upstream redesign (#714)

* merge toolbar fragment

* Fix store screenshot generator

* Fix alert color

* Fix #609

* Fix crash

* bigger hitbox for chips

* support mastodon languages

* merge ui utils

* merge stuff

* fix icon

* ensure 48dp touch target

* init local prefs, add helper function for enum values

* update compose action layout

* merge compose-adj files

* update extended footer

* fix poll wrong option checked

closes sk22#641

* no border when disabled

closes sk22#640

* Fix #610

* Minor fixes

* Fix alert color

* Fix #609

* Fix crash

* Fix #610

* Minor fixes

* add resources

* more compatible mastodon language

* fix html parser

* mark as read on refresh

* update tab bar

* tweak m3 buttons

* update compose-adj files

* tweak and update styles

* m3 expand button

* flag icon should be 18dp, actually

* More minor fixes

closes #612

* More minor fixes

closes #612

* Bump version

* fix no create status event when redrafting

* add material 3 assets

* New translations strings.xml (Greek)

* New translations strings.xml (Greek)

* New translations strings.xml (Italian)

* New translations strings.xml (Greek)

* New translations strings.xml (Italian)

* New translations strings.xml (Thai)

* New translations strings.xml (Thai)

* New translations strings.xml (Italian)

* New translations strings.xml (Thai)

* use new buttons for profile fragment

* merge compose fragment

* merge all the styles! oh dear

* New translations full_description.txt (Indonesian)

* New translations full_description.txt (Chinese Simplified)

* New translations strings.xml (Chinese Simplified)

* New translations full_description.txt (Chinese Simplified)

* Fix #615

* Minor fixes

* Fix #611

* A bunch of crash fixes

* New translations strings.xml (Greek)

* Make the default server configurable

* Pass the system timezone to server when signing up

* New translations strings.xml (Chinese Simplified)

* New translations strings.xml (Japanese)

* Fix #615

* Minor fixes

* Fix #611

* A bunch of crash fixes

* Make the default server configurable

* Pass the system timezone to server when signing up

* oops. accidentally pasted the commit message in the code

* Remove unused code that caused a crash for some users ¯\_(ツ)_/¯

* New translations strings.xml (Japanese)

* New translations strings.xml (Japanese)

* Remove unused code that caused a crash for some users ¯\_(ツ)_/¯

* New translations strings.xml (Polish)

* New translations strings.xml (Polish)

* New translations strings.xml (Turkish)

* New translations strings.xml (Belarusian)

* prepare merging profile fragment

* merge profile fragment

* New translations strings.xml (Belarusian)

* New translations strings.xml (Greek)

* fix icon padding

* apply post header changes

* minor margin tweaks

* fix footer buttons

* fix header announcement buttons

* New translations strings.xml (Japanese)

* New translations strings.xml (Japanese)

* New translations strings.xml (Japanese)

* New translations strings.xml (Japanese)

* New translations strings.xml (Japanese)

* New translations strings.xml (Japanese)

* New translations full_description.txt (Japanese)

* New translations strings.xml (Icelandic)

* New translations strings.xml (Icelandic)

* New translations strings.xml (Icelandic)

* fix replying

* New translations strings.xml (Icelandic)

* fix translate button

* fix more button visibility

* fix counts label styling

* fix disabled boost button opacity

* fix tab layouts

* fix notification icon color crash

* New translations strings.xml (Greek)

* implement elevation listener in home tab

* fix elevation and listener in home tab

* add elevation scroll listener to notifications

* New translations strings.xml (Scottish Gaelic)

* Add editorconfig

So that PRs like #625 don't happen again

* Crash fix

* 🤔

* New translations strings.xml (Greek)

* New translations strings.xml (Japanese)

* New translations strings.xml (French)

* New translations strings.xml (French)

* New translations strings.xml (French)

* fix notification elevation and integrate divider

* 🤔

* Crash fix

* Add editorconfig

So that PRs like #625 don't happen again

* New translations strings.xml (Turkish)

* save interactions in cache

* New translations strings.xml (Turkish)

* merge new discover/search

* New translations strings.xml (Bengali)

* New translations strings.xml (Scottish Gaelic)

* New translations strings.xml (Bengali)

* merge new settings fragments

* fix no auth callback always being executed

* allow opening server info from profile

closes sk22#593

* fix hide boosts icon color

closes sk22#676

* New translations strings.xml (Turkish)

* New translations strings.xml (Turkish)

* New translations strings.xml (Turkish)

* New translations strings.xml (Chinese Simplified)

* New translations strings.xml (Turkish)

* New translations strings.xml (Chinese Simplified)

* New translations strings.xml (German)

* New translations strings.xml (German)

* New translations strings.xml (Turkish)

* update fedinuke list

from source; doesn't contain any modifications regarding a recent issue

* New translations strings.xml (Turkish)

* remove unused class

* fix crash

* darken m3 outline color a bit

* use m3 outline again

* fix misalignment

closes sk22#682

* New translations strings.xml (Turkish)

* New translations full_description.txt (Turkish)

* New translations short_description.txt (Turkish)

* fix crash

* fix metadata sorting

* show pronouns in header/account lists

* fix broken divider line

closes sk22#679

* trim pronouns

* improve pronoun display

* New translations strings.xml (French)

* New translations strings.xml (Japanese)

* fix broken federated timeline

closes sk22#685

* fix broken -1 fallback behavior

closes sk22#681

* don't display nothing if server about request fails

closes sk22#678

* New translations strings.xml (Ukrainian)

* migrate global prefs to local prefs

* do confirm unfollow by default

* New translations strings.xml (Ukrainian)

* New translations strings.xml (Ukrainian)

* New translations full_description.txt (Ukrainian)

* New translations strings.xml (Ukrainian)

* New translations strings.xml (Ukrainian)

* New translations strings.xml (Ukrainian)

* New translations strings.xml (Ukrainian)

* New translations strings.xml (Ukrainian)

* New translations strings.xml (Russian)

* New translations strings.xml (Vietnamese)

* New translations strings.xml (Ukrainian)

* New translations strings.xml (Vietnamese)

* New translations full_description.txt (Ukrainian)

* New translations strings.xml (Ukrainian)

* New translations strings.xml (Vietnamese)

* New translations strings.xml (Ukrainian)

* New translations strings.xml (Ukrainian)

* make sure list in prefs are always mutable and nut null

* New translations strings.xml (Ukrainian)

* New translations strings.xml (Ukrainian)

* New translations strings.xml (Russian)

* fix pronouns edge case

* add back fix for stretched images

closes sk22#636

* fix null pointer on missing default posting language

* fix default posting language not being applied

* bigger username hitbox

closes sk22#688

* fix rtl header username alignment

closes sk22#689

* New translations strings.xml (Ukrainian)

* New translations strings.xml (Ukrainian)

* hopefully fix crashes

closes sk22#692

* New translations strings.xml (Ukrainian)

* New translations full_description.txt (Ukrainian)

* fix pronoun crash

* New translations strings.xml (Persian)

* New translations strings.xml (Ukrainian)

* re-add true black mode

* asterisk can be a pronoun

* New translations strings.xml (Persian)

* true black mode fixes and clean-ups

* material 3 button background for switcher

* darker tab bar selected background

* better align follow/following button widths

* restore rainbow refresh colors

* fix search transition

* fix min width issue with switcher button

* fix no elevation when true black is enabled in light theme

* use statusForContent to determine spoilerRevealed

closes sk22#694

* New translations strings.xml (Persian)

* New translations strings.xml (Persian)

* New translations strings.xml (Persian)

* New translations strings.xml (Persian)

* New translations strings.xml (Persian)

* New translations strings.xml (Persian)

* fix profile tab bar in true black theme

* fix m3 default button style

closes sk22#697

* prettier role badges

closes sk22#663

* fix translate button spacing

closes sk22#655

* use m3 switches in dialogs

closes sk22#653

* implement color palette switcher

* fix color palettes being overwritten

* add display and notification settings

* clean up code

* per-account single notification setting

* add missing items to notification types

* add prefix replies setting

* add show replies/boosts and reply visibility

* add load/see new posts settings

* fix spectator mode missing spoiler padding

* add a bunch of display settings

* update fedinuke

* add content type settings

* add settings for local-onlu

* add missing settings items

* fix visibility button icon tint

* hopefully fix some crashes

* normalize padding above edit text

* apparently, some people don't like pills

closes sk22#706

* fix play button color

closes sk22#705
This commit is contained in:
sk22
2023-07-16 18:01:42 +02:00
committed by GitHub
parent 3cfea0e660
commit 7677ad39ca
744 changed files with 24873 additions and 13485 deletions

View File

@@ -14,8 +14,6 @@ import org.parceler.Parcel;
import org.parceler.ParcelConstructor;
import org.parceler.ParcelProperty;
import java.util.UUID;
@Parcel
public class Attachment extends BaseModel{
// @RequiredField
@@ -135,6 +133,7 @@ public class Attachment extends BaseModel{
public PointF focus;
public SizeMetadata original;
public SizeMetadata small;
public ColorsMetadata colors;
@Override
public String toString(){
@@ -146,6 +145,7 @@ public class Attachment extends BaseModel{
", focus="+focus+
", original="+original+
", small="+small+
", colors="+colors+
'}';
}
}
@@ -169,4 +169,20 @@ public class Attachment extends BaseModel{
'}';
}
}
@Parcel
public static class ColorsMetadata{
public String background;
public String foreground;
public String accent;
@Override
public String toString(){
return "ColorsMetadata{"+
"background='"+background+'\''+
", foreground='"+foreground+'\''+
", accent='"+accent+'\''+
'}';
}
}
}

View File

@@ -18,23 +18,22 @@ public enum ContentType {
@SerializedName("text/bbcode")
BBCODE, // akkoma
@SerializedName("text/x.misskeymarkdown")
MISSKEY_MARKDOWN; // akkoma/*key
MISSKEY_MARKDOWN, // akkoma/*key
@SerializedName("")
UNSPECIFIED;
public static int getContentTypeRes(@Nullable ContentType contentType) {
return contentType == null ? R.id.content_type_null : switch(contentType) {
case PLAIN -> R.id.content_type_plain;
case HTML -> R.id.content_type_html;
case MARKDOWN -> R.id.content_type_markdown;
case BBCODE -> R.id.content_type_bbcode;
case MISSKEY_MARKDOWN -> R.id.content_type_misskey_markdown;
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 static void adaptMenuToInstance(Menu m, Instance i) {
if (i.pleroma == null) {
// memo: change this if glitch or another mastodon fork supports bbcode or mfm
m.findItem(R.id.content_type_bbcode).setVisible(false);
m.findItem(R.id.content_type_misskey_markdown).setVisible(false);
}
public boolean supportedByInstance(Instance i) {
return i.isAkkoma() || (this!=BBCODE && this!=MISSKEY_MARKDOWN);
}
}

View File

@@ -33,6 +33,14 @@ 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;
}
@Override
public String toString(){
return "Emoji{"+

View File

@@ -1,62 +1,43 @@
package org.joinmastodon.android.model;
import android.text.TextUtils;
import androidx.annotation.Nullable;
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;
import java.util.stream.Stream;
@Parcel
public class Filter extends BaseModel{
@RequiredField
public String id;
public String phrase;
@RequiredField
public String title;
public transient EnumSet<FilterContext> context=EnumSet.noneOf(FilterContext.class);
@RequiredField
public EnumSet<FilterContext> context;
public Instant expiresAt;
public boolean irreversible;
public boolean wholeWord;
public FilterAction filterAction;
@SerializedName("context")
protected List<FilterContext> _context;
public List<FilterKeyword> keywords=new ArrayList<>();
private transient Pattern pattern;
public List<FilterStatus> statuses=new ArrayList<>();
@Override
public void postprocess() throws ObjectValidationException{
super.postprocess();
if(_context==null)
throw new ObjectValidationException();
for(FilterContext c:_context){
if(c!=null)
context.add(c);
}
for(FilterKeyword keyword:keywords)
keyword.postprocess();
for(FilterStatus status:statuses)
status.postprocess();
}
public boolean matches(CharSequence text){
if(TextUtils.isEmpty(text))
return false;
if(pattern==null){
if(wholeWord)
pattern=Pattern.compile("\\b"+Pattern.quote(phrase)+"\\b", Pattern.CASE_INSENSITIVE);
else
pattern=Pattern.compile(Pattern.quote(phrase), Pattern.CASE_INSENSITIVE);
}
if (title == null) title = phrase;
return pattern.matcher(text).find();
}
public boolean matches(Status status){
return matches(status.getContentStatus().getStrippedText());
public boolean isActive(){
return expiresAt==null || expiresAt.isAfter(Instant.now());
}
@Override
@@ -64,31 +45,11 @@ public class Filter extends BaseModel{
return "Filter{"+
"id='"+id+'\''+
", title='"+title+'\''+
", phrase='"+phrase+'\''+
", context="+context+
", expiresAt="+expiresAt+
", irreversible="+irreversible+
", wholeWord="+wholeWord+
", filterAction="+filterAction+
", keywords="+keywords+
", statuses="+statuses+
'}';
}
public enum FilterContext{
@SerializedName("home")
HOME,
@SerializedName("notifications")
NOTIFICATIONS,
@SerializedName("public")
PUBLIC,
@SerializedName("thread")
THREAD,
@SerializedName("account")
ACCOUNT
}
public enum FilterAction{
@SerializedName("hide")
HIDE,
@SerializedName("warn")
WARN
}
}

View File

@@ -0,0 +1,10 @@
package org.joinmastodon.android.model;
import com.google.gson.annotations.SerializedName;
public enum FilterAction{
@SerializedName("warn")
WARN,
@SerializedName("hide")
HIDE
}

View File

@@ -0,0 +1,31 @@
package org.joinmastodon.android.model;
import com.google.gson.annotations.SerializedName;
import org.joinmastodon.android.R;
import androidx.annotation.StringRes;
public enum FilterContext{
@SerializedName("home")
HOME,
@SerializedName("notifications")
NOTIFICATIONS,
@SerializedName("public")
PUBLIC,
@SerializedName("thread")
THREAD,
@SerializedName("account")
ACCOUNT;
@StringRes
public int getDisplayNameRes(){
return switch(this){
case HOME -> R.string.filter_context_home_lists;
case NOTIFICATIONS -> R.string.filter_context_notifications;
case PUBLIC -> R.string.filter_context_public_timelines;
case THREAD -> R.string.filter_context_threads_replies;
case ACCOUNT -> R.string.filter_context_profiles;
};
}
}

View File

@@ -0,0 +1,21 @@
package org.joinmastodon.android.model;
import org.joinmastodon.android.api.AllFieldsAreRequired;
import org.parceler.Parcel;
@AllFieldsAreRequired
@Parcel
public class FilterKeyword extends BaseModel{
public String id;
public String keyword;
public boolean wholeWord;
@Override
public String toString(){
return "FilterKeyword{"+
"id='"+id+'\''+
", keyword='"+keyword+'\''+
", wholeWord="+wholeWord+
'}';
}
}

View File

@@ -3,9 +3,13 @@ package org.joinmastodon.android.model;
import org.joinmastodon.android.api.ObjectValidationException;
import org.parceler.Parcel;
import java.util.List;
@Parcel
public class FilterResult extends BaseModel {
public Filter filter;
public LegacyFilter filter;
public List<String> keywordMatches;
@Override
public void postprocess() throws ObjectValidationException {

View File

@@ -0,0 +1,11 @@
package org.joinmastodon.android.model;
import org.joinmastodon.android.api.AllFieldsAreRequired;
import org.parceler.Parcel;
@AllFieldsAreRequired
@Parcel
public class FilterStatus extends BaseModel{
public String id;
public String statusId;
}

View File

@@ -6,13 +6,14 @@ import org.parceler.Parcel;
import java.util.List;
@Parcel
public class Hashtag extends BaseModel{
public class Hashtag extends BaseModel implements DisplayItemsParent{
@RequiredField
public String name;
@RequiredField
public String url;
public boolean following;
public List<History> history;
public int statusesCount;
@Override
public String toString(){
@@ -21,6 +22,12 @@ public class Hashtag extends BaseModel{
", url='"+url+'\''+
", following="+following+
", history="+history+
", statusesCount="+statusesCount+
'}';
}
@Override
public String getID(){
return name;
}
}

View File

@@ -0,0 +1,89 @@
package org.joinmastodon.android.model;
import android.text.TextUtils;
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{
public String id;
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")
protected List<FilterContext> _context;
public FilterAction filterAction;
public List<FilterKeyword> keywords=new ArrayList<>();
public List<FilterStatus> statuses=new ArrayList<>();
private transient Pattern pattern;
@Override
public void postprocess() throws ObjectValidationException{
super.postprocess();
if(_context==null)
throw new ObjectValidationException();
for(FilterContext c:_context){
if(c!=null)
context.add(c);
}
for(FilterKeyword keyword:keywords)
keyword.postprocess();
for(FilterStatus status:statuses)
status.postprocess();
}
public boolean matches(CharSequence text){
if(TextUtils.isEmpty(text))
return false;
if(pattern==null){
if(wholeWord)
pattern=Pattern.compile("\\b"+Pattern.quote(phrase)+"\\b", Pattern.CASE_INSENSITIVE);
else
pattern=Pattern.compile(Pattern.quote(phrase), Pattern.CASE_INSENSITIVE);
}
if (title == null) title = phrase;
return pattern.matcher(text).find();
}
public boolean matches(Status status){
return matches(status.getContentStatus().getStrippedText());
}
public boolean isActive(){
return expiresAt==null || expiresAt.isAfter(Instant.now());
}
@Override
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+
'}';
}
}

View File

@@ -1,33 +0,0 @@
package org.joinmastodon.android.model;
import android.text.SpannableStringBuilder;
import org.joinmastodon.android.GlobalUserPreferences;
import org.joinmastodon.android.ui.text.HtmlParser;
import org.joinmastodon.android.ui.utils.CustomEmojiHelper;
import java.util.Collections;
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest;
import me.grishka.appkit.utils.V;
public class ParsedAccount{
public Account account;
public CharSequence parsedName, parsedBio;
public CustomEmojiHelper emojiHelper;
public ImageLoaderRequest avatarRequest;
public ParsedAccount(Account account, String accountID){
this.account=account;
parsedName=HtmlParser.parseCustomEmoji(account.displayName, account.emojis);
parsedBio=HtmlParser.parse(account.note, account.emojis, Collections.emptyList(), Collections.emptyList(), accountID);
emojiHelper=new CustomEmojiHelper();
SpannableStringBuilder ssb=new SpannableStringBuilder(parsedName);
ssb.append(parsedBio);
emojiHelper.setText(ssb);
avatarRequest=new UrlImageLoaderRequest(GlobalUserPreferences.playGifs ? account.avatar : account.avatarStatic, V.dp(40), V.dp(40));
}
}

View File

@@ -13,7 +13,7 @@ public class Poll extends BaseModel{
@RequiredField
public String id;
public Instant expiresAt;
protected boolean expired;
public boolean expired;
public boolean multiple;
public int votersCount;
public int votesCount;

View File

@@ -1,7 +1,9 @@
package org.joinmastodon.android.model;
import org.joinmastodon.android.api.RequiredField;
import org.parceler.Parcel;
@Parcel
public class Relationship extends BaseModel{
@RequiredField
public String id;

View File

@@ -2,6 +2,7 @@ package org.joinmastodon.android.model;
import org.joinmastodon.android.api.ObjectValidationException;
import org.joinmastodon.android.api.RequiredField;
import org.joinmastodon.android.model.viewmodel.AccountViewModel;
public class SearchResult extends BaseModel implements DisplayItemsParent{
public Account account;
@@ -11,6 +12,7 @@ public class SearchResult extends BaseModel implements DisplayItemsParent{
public Type type;
public transient String id;
public transient boolean firstInSection;
public SearchResult(){}

View File

@@ -3,6 +3,10 @@ package org.joinmastodon.android.model;
import static org.joinmastodon.android.api.MastodonAPIController.gson;
import static org.joinmastodon.android.api.MastodonAPIController.gsonWithoutDeserializer;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
@@ -50,8 +54,6 @@ public class Status extends BaseModel implements DisplayItemsParent, Searchable{
public long favouritesCount;
public long repliesCount;
public Instant editedAt;
// might not be provided (by older mastodon servers),
// so megalodon will use the locally cached filters if filtered == null
public List<FilterResult> filtered;
public String url;
@@ -74,17 +76,17 @@ public class Status extends BaseModel implements DisplayItemsParent, Searchable{
public transient boolean filterRevealed;
public transient boolean spoilerRevealed;
public transient boolean sensitiveRevealed;
public transient boolean textExpanded, textExpandable;
public transient boolean hasGapAfter;
public transient TranslatedStatus translation;
public transient boolean translationShown;
private transient String strippedText;
public Status(){}
@Override
public void postprocess() throws ObjectValidationException{
if(spoilerText!=null && !spoilerText.isEmpty() && !sensitive)
sensitive=true;
super.postprocess();
if(application!=null)
application.postprocess();
@@ -105,10 +107,12 @@ public class Status extends BaseModel implements DisplayItemsParent, Searchable{
if(reblog!=null)
reblog.postprocess();
if(filtered!=null)
for(FilterResult fr : filtered)
for(FilterResult fr:filtered)
fr.postprocess();
spoilerRevealed=GlobalUserPreferences.alwaysExpandContentWarnings || !sensitive;
if (!TextUtils.isEmpty(spoilerText)) sensitive = true;
spoilerRevealed=TextUtils.isEmpty(spoilerText);
sensitiveRevealed=!sensitive;
if (visibility.equals(StatusPrivacy.LOCAL)) localOnly = true;
}
@@ -131,6 +135,7 @@ public class Status extends BaseModel implements DisplayItemsParent, Searchable{
", reblogsCount="+reblogsCount+
", favouritesCount="+favouritesCount+
", repliesCount="+repliesCount+
", editedAt="+editedAt+
", url='"+url+'\''+
", inReplyToId='"+inReplyToId+'\''+
", inReplyToAccountId='"+inReplyToAccountId+'\''+
@@ -139,11 +144,15 @@ public class Status extends BaseModel implements DisplayItemsParent, Searchable{
", card="+card+
", language='"+language+'\''+
", text='"+text+'\''+
", filtered="+filtered+
", favourited="+favourited+
", reblogged="+reblogged+
", muted="+muted+
", bookmarked="+bookmarked+
", pinned="+pinned+
", spoilerRevealed="+spoilerRevealed+
", hasGapAfter="+hasGapAfter+
", strippedText='"+strippedText+'\''+
'}';
}
@@ -172,6 +181,12 @@ public class Status extends BaseModel implements DisplayItemsParent, Searchable{
return strippedText;
}
@NonNull
@Override
public Status clone(){
return (Status) super.clone();
}
public boolean isReblogPermitted(String accountID){
return visibility.isReblogPermitted(account.id.equals(
AccountSessionManager.getInstance().getAccount(accountID).self.id
@@ -189,6 +204,7 @@ public class Status extends BaseModel implements DisplayItemsParent, Searchable{
s.mentions = List.of();
s.tags = List.of();
s.emojis = List.of();
s.filtered = List.of();
return s;
}

View File

@@ -337,20 +337,20 @@ public class TimelineDefinition {
}
};
public static List<TimelineDefinition> getDefaultTimelines(String accountId) {
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.toList());
.collect(Collectors.toCollection(ArrayList::new));
}
public static List<TimelineDefinition> getAllTimelines(String accountId) {
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.toList());
.collect(Collectors.toCollection(ArrayList::new));
}
private static final List<TimelineDefinition> DEFAULT_TIMELINES = List.of(

View File

@@ -0,0 +1,13 @@
package org.joinmastodon.android.model;
public class TimelineMarkers{
public Marker home, notifications;
@Override
public String toString(){
return "TimelineMarkers{"+
"home="+home+
", notifications="+notifications+
'}';
}
}

View File

@@ -0,0 +1,10 @@
package org.joinmastodon.android.model.catalog;
import org.joinmastodon.android.api.AllFieldsAreRequired;
import org.joinmastodon.android.model.BaseModel;
@AllFieldsAreRequired
public class CatalogDefaultInstance extends BaseModel{
public String domain;
public float weight;
}

View File

@@ -0,0 +1,46 @@
package org.joinmastodon.android.model.viewmodel;
import android.text.SpannableStringBuilder;
import org.joinmastodon.android.GlobalUserPreferences;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.model.Account;
import org.joinmastodon.android.model.AccountField;
import org.joinmastodon.android.ui.text.HtmlParser;
import org.joinmastodon.android.ui.utils.CustomEmojiHelper;
import java.util.Collections;
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest;
import me.grishka.appkit.utils.V;
public class AccountViewModel{
public final Account account;
public final ImageLoaderRequest avaRequest;
public final CustomEmojiHelper emojiHelper;
public final CharSequence parsedName, parsedBio;
public final String verifiedLink;
public AccountViewModel(Account account, String accountID){
this.account=account;
avaRequest=new UrlImageLoaderRequest(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);
else
parsedName=account.displayName;
parsedBio=HtmlParser.parse(account.note, account.emojis, Collections.emptyList(), Collections.emptyList(), accountID);
SpannableStringBuilder ssb=new SpannableStringBuilder(parsedName);
ssb.append(parsedBio);
emojiHelper.setText(ssb);
String verifiedLink=null;
for(AccountField fld:account.fields){
if(fld.verifiedAt!=null){
verifiedLink=HtmlParser.stripAndRemoveInvisibleSpans(fld.value);
break;
}
}
this.verifiedLink=verifiedLink;
}
}

View File

@@ -0,0 +1,19 @@
package org.joinmastodon.android.model.viewmodel;
import android.text.TextUtils;
import org.joinmastodon.android.model.Card;
import me.grishka.appkit.imageloader.requests.ImageLoaderRequest;
import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest;
import me.grishka.appkit.utils.V;
public class CardViewModel{
public final Card card;
public final ImageLoaderRequest imageRequest;
public CardViewModel(Card card, int width, int height){
this.card=card;
this.imageRequest=TextUtils.isEmpty(card.image) ? null : new UrlImageLoaderRequest(card.image, V.dp(width), V.dp(height));
}
}

View File

@@ -0,0 +1,74 @@
package org.joinmastodon.android.model.viewmodel;
import org.joinmastodon.android.R;
import java.util.function.Consumer;
public class CheckableListItem<T> extends ListItem<T>{
public Style style;
public boolean checked;
public Consumer<Boolean> checkedChangeListener;
public CheckableListItem(String title, String subtitle, Style style, boolean checked, int iconRes, Runnable onClick, T parentObject, boolean dividerAfter){
super(title, subtitle, iconRes, onClick, parentObject, 0, dividerAfter);
this.style=style;
this.checked=checked;
}
public CheckableListItem(String title, String subtitle, Style style, boolean checked, Runnable onClick){
this(title, subtitle, style, checked, 0, onClick, null, false);
}
public CheckableListItem(String title, String subtitle, Style style, boolean checked, Runnable onClick, T parentObject){
this(title, subtitle, style, checked, 0, onClick, parentObject, false);
}
public CheckableListItem(String title, String subtitle, Style style, boolean checked, int iconRes, Runnable onClick){
this(title, subtitle, style, checked, iconRes, onClick, null, false);
}
public CheckableListItem(String title, String subtitle, Style style, boolean checked, int iconRes, Runnable onClick, T parentObject){
this(title, subtitle, style, checked, iconRes, onClick, parentObject, false);
}
public CheckableListItem(int titleRes, int subtitleRes, Style style, boolean checked, Runnable onClick){
this(titleRes, subtitleRes, style, checked, 0, onClick, false);
}
public CheckableListItem(int titleRes, int subtitleRes, Style style, boolean checked, Runnable onClick, boolean dividerAfter){
this(titleRes, subtitleRes, style, checked, 0, onClick, dividerAfter);
}
public CheckableListItem(int titleRes, int subtitleRes, Style style, boolean checked, int iconRes, Runnable onClick){
this(titleRes, subtitleRes, style, checked, iconRes, onClick, false);
}
public CheckableListItem(int titleRes, int subtitleRes, Style style, boolean checked, int iconRes, Runnable onClick, boolean dividerAfter){
super(titleRes, subtitleRes, iconRes, onClick, 0, dividerAfter);
this.style=style;
this.checked=checked;
}
@Override
public int getItemViewType(){
return switch(style){
case CHECKBOX -> R.id.list_item_checkbox;
case RADIO -> R.id.list_item_radio;
case SWITCH -> R.id.list_item_switch;
};
}
public void setChecked(boolean checked){
this.checked=checked;
}
public void toggle(){
checked=!checked;
}
public enum Style{
CHECKBOX,
RADIO,
SWITCH
}
}

View File

@@ -0,0 +1,82 @@
package org.joinmastodon.android.model.viewmodel;
import org.joinmastodon.android.R;
import androidx.annotation.DrawableRes;
import androidx.annotation.StringRes;
public class ListItem<T>{
public String title;
public String subtitle;
@StringRes
public int titleRes;
@StringRes
public int subtitleRes;
@DrawableRes
public int iconRes;
public int colorOverrideAttr;
public boolean dividerAfter;
public Runnable onClick;
public boolean isEnabled=true;
public T parentObject;
public ListItem(String title, String subtitle, int iconRes, Runnable onClick, T parentObject, int colorOverrideAttr, boolean dividerAfter){
this.title=title;
this.subtitle=subtitle;
this.iconRes=iconRes;
this.colorOverrideAttr=colorOverrideAttr;
this.dividerAfter=dividerAfter;
this.onClick=onClick;
this.parentObject=parentObject;
if(onClick==null)
isEnabled=false;
}
public ListItem(String title, String subtitle, Runnable onClick){
this(title, subtitle, 0, onClick, null, 0, false);
}
public ListItem(String title, String subtitle, Runnable onClick, T parentObject){
this(title, subtitle, 0, onClick, parentObject, 0, false);
}
public ListItem(String title, String subtitle, @DrawableRes int iconRes, Runnable onClick){
this(title, subtitle, iconRes, onClick, null, 0, false);
}
public ListItem(String title, String subtitle, @DrawableRes int iconRes, Runnable onClick, boolean dividerAfter){
this(title, subtitle, iconRes, onClick, null, 0, dividerAfter);
}
public ListItem(String title, String subtitle, @DrawableRes int iconRes, Runnable onClick, T parentObject){
this(title, subtitle, iconRes, onClick, parentObject, 0, false);
}
public ListItem(@StringRes int titleRes, @StringRes int subtitleRes, Runnable onClick){
this(null, null, 0, onClick, null, 0, false);
this.titleRes=titleRes;
this.subtitleRes=subtitleRes;
}
public ListItem(@StringRes int titleRes, @StringRes int subtitleRes, Runnable onClick, int colorOverrideAttr, boolean dividerAfter){
this(null, null, 0, onClick, null, colorOverrideAttr, dividerAfter);
this.titleRes=titleRes;
this.subtitleRes=subtitleRes;
}
public ListItem(@StringRes int titleRes, @StringRes int subtitleRes, @DrawableRes int iconRes, Runnable onClick){
this(null, null, iconRes, onClick, null, 0, false);
this.titleRes=titleRes;
this.subtitleRes=subtitleRes;
}
public ListItem(@StringRes int titleRes, @StringRes int subtitleRes, @DrawableRes int iconRes, Runnable onClick, int colorOverrideAttr, boolean dividerAfter){
this(null, null, iconRes, onClick, null, colorOverrideAttr, dividerAfter);
this.titleRes=titleRes;
this.subtitleRes=subtitleRes;
}
public int getItemViewType(){
return colorOverrideAttr==0 ? R.id.list_item_simple : R.id.list_item_simple_tinted;
}
}

View File

@@ -0,0 +1,24 @@
package org.joinmastodon.android.model.viewmodel;
import android.content.Context;
import org.joinmastodon.android.R;
import org.joinmastodon.android.model.Hashtag;
import org.joinmastodon.android.model.SearchResult;
public class SearchResultViewModel{
public SearchResult result;
public AccountViewModel account;
public ListItem<Hashtag> hashtagItem;
public SearchResultViewModel(SearchResult result, String accountID, boolean isRecents){
this.result=result;
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_fluent_history_24_regular : R.drawable.ic_fluent_number_symbol_24_regular, null, result.hashtag);
hashtagItem.isEnabled=true;
}
}
}
}