Settings M3 redesign wip

This commit is contained in:
Grishka
2023-06-04 02:04:55 +03:00
parent 7c6ec2e3d7
commit 31c8665653
139 changed files with 4520 additions and 1145 deletions

View File

@@ -15,7 +15,8 @@ import org.joinmastodon.android.api.requests.notifications.GetNotifications;
import org.joinmastodon.android.api.requests.timelines.GetHomeTimeline;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.model.CacheablePaginatedResponse;
import org.joinmastodon.android.model.Filter;
import org.joinmastodon.android.model.FilterContext;
import org.joinmastodon.android.model.LegacyFilter;
import org.joinmastodon.android.model.Notification;
import org.joinmastodon.android.model.PaginatedResponse;
import org.joinmastodon.android.model.SearchResult;
@@ -59,7 +60,7 @@ public class CacheController{
cancelDelayedClose();
databaseThread.postRunnable(()->{
try{
List<Filter> filters=AccountSessionManager.getInstance().getAccount(accountID).wordFilters.stream().filter(f->f.context.contains(Filter.FilterContext.HOME)).collect(Collectors.toList());
List<LegacyFilter> filters=AccountSessionManager.getInstance().getAccount(accountID).wordFilters.stream().filter(f->f.context.contains(FilterContext.HOME)).collect(Collectors.toList());
if(!forceReload){
SQLiteDatabase db=getOrOpenDatabase();
try(Cursor cursor=db.query("home_timeline", new String[]{"json", "flags"}, maxID==null ? null : "`id`<?", maxID==null ? null : new String[]{maxID}, null, null, "`time` DESC", count+"")){
@@ -74,7 +75,7 @@ public class CacheController{
int flags=cursor.getInt(1);
status.hasGapAfter=((flags & POST_FLAG_GAP_AFTER)!=0);
newMaxID=status.id;
for(Filter filter:filters){
for(LegacyFilter filter:filters){
if(filter.matches(status))
continue outer;
}
@@ -139,7 +140,7 @@ public class CacheController{
}
return;
}
List<Filter> filters=AccountSessionManager.getInstance().getAccount(accountID).wordFilters.stream().filter(f->f.context.contains(Filter.FilterContext.NOTIFICATIONS)).collect(Collectors.toList());
List<LegacyFilter> filters=AccountSessionManager.getInstance().getAccount(accountID).wordFilters.stream().filter(f->f.context.contains(FilterContext.NOTIFICATIONS)).collect(Collectors.toList());
if(!forceReload){
SQLiteDatabase db=getOrOpenDatabase();
try(Cursor cursor=db.query(onlyMentions ? "notifications_mentions" : "notifications_all", new String[]{"json"}, maxID==null ? null : "`id`<?", maxID==null ? null : new String[]{maxID}, null, null, "`time` DESC", count+"")){
@@ -153,7 +154,7 @@ public class CacheController{
ntf.postprocess();
newMaxID=ntf.id;
if(ntf.status!=null){
for(Filter filter:filters){
for(LegacyFilter filter:filters){
if(filter.matches(ntf.status))
continue outer;
}
@@ -176,7 +177,7 @@ public class CacheController{
public void onSuccess(List<Notification> result){
PaginatedResponse<List<Notification>> res=new PaginatedResponse<>(result.stream().filter(ntf->{
if(ntf.status!=null){
for(Filter filter:filters){
for(LegacyFilter filter:filters){
if(filter.matches(ntf.status)){
return false;
}

View File

@@ -122,13 +122,17 @@ public class MastodonAPIController{
Log.d(TAG, "["+(session==null ? "no-auth" : session.getID())+"] response body: "+respJson);
if(req.respTypeToken!=null)
respObj=gson.fromJson(respJson, req.respTypeToken.getType());
else
else if(req.respClass!=null)
respObj=gson.fromJson(respJson, req.respClass);
else
respObj=null;
}else{
if(req.respTypeToken!=null)
respObj=gson.fromJson(reader, req.respTypeToken.getType());
else
else if(req.respClass!=null)
respObj=gson.fromJson(reader, req.respClass);
else
respObj=null;
}
}catch(JsonIOException|JsonSyntaxException x){
if(BuildConfig.DEBUG)

View File

@@ -0,0 +1,9 @@
package org.joinmastodon.android.api;
import com.google.gson.reflect.TypeToken;
public abstract class ResultlessMastodonAPIRequest extends MastodonAPIRequest<Void>{
public ResultlessMastodonAPIRequest(HttpMethod method, String path){
super(method, path, (Class<Void>)null);
}
}

View File

@@ -0,0 +1,34 @@
package org.joinmastodon.android.api.requests.accounts;
import org.joinmastodon.android.api.MastodonAPIRequest;
import org.joinmastodon.android.model.Account;
import org.joinmastodon.android.model.Preferences;
import org.joinmastodon.android.model.StatusPrivacy;
public class UpdateAccountCredentialsPreferences extends MastodonAPIRequest<Account>{
public UpdateAccountCredentialsPreferences(Preferences preferences, Boolean locked, Boolean discoverable){
super(HttpMethod.PATCH, "/accounts/update_credentials", Account.class);
setRequestBody(new Request(locked, discoverable, new RequestSource(preferences.postingDefaultVisibility, preferences.postingDefaultLanguage)));
}
private static class Request{
public Boolean locked, discoverable;
public RequestSource source;
public Request(Boolean locked, Boolean discoverable, RequestSource source){
this.locked=locked;
this.discoverable=discoverable;
this.source=source;
}
}
private static class RequestSource{
public StatusPrivacy privacy;
public String language;
public RequestSource(StatusPrivacy privacy, String language){
this.privacy=privacy;
this.language=language;
}
}
}

View File

@@ -0,0 +1,23 @@
package org.joinmastodon.android.api.requests.filters;
import org.joinmastodon.android.api.MastodonAPIRequest;
import org.joinmastodon.android.model.Filter;
import org.joinmastodon.android.model.FilterAction;
import org.joinmastodon.android.model.FilterContext;
import org.joinmastodon.android.model.FilterKeyword;
import java.util.EnumSet;
import java.util.List;
import java.util.stream.Collectors;
public class CreateFilter extends MastodonAPIRequest<Filter>{
public CreateFilter(String title, EnumSet<FilterContext> context, FilterAction action, int expiresIn, List<FilterKeyword> words){
super(HttpMethod.POST, "/filters", Filter.class);
setRequestBody(new FilterRequest(title, context, action, expiresIn==0 ? null : expiresIn, words.stream().map(w->new KeywordAttribute(null, null, w.keyword, w.wholeWord)).collect(Collectors.toList())));
}
@Override
protected String getPathPrefix(){
return "/api/v2";
}
}

View File

@@ -0,0 +1,14 @@
package org.joinmastodon.android.api.requests.filters;
import org.joinmastodon.android.api.ResultlessMastodonAPIRequest;
public class DeleteFilter extends ResultlessMastodonAPIRequest{
public DeleteFilter(String id){
super(HttpMethod.DELETE, "/filters/"+id);
}
@Override
protected String getPathPrefix(){
return "/api/v2";
}
}

View File

@@ -0,0 +1,23 @@
package org.joinmastodon.android.api.requests.filters;
import org.joinmastodon.android.model.FilterAction;
import org.joinmastodon.android.model.FilterContext;
import java.util.EnumSet;
import java.util.List;
class FilterRequest{
public String title;
public EnumSet<FilterContext> context;
public FilterAction filterAction;
public Integer expiresIn;
public List<KeywordAttribute> keywordsAttributes;
public FilterRequest(String title, EnumSet<FilterContext> context, FilterAction filterAction, Integer expiresIn, List<KeywordAttribute> keywordsAttributes){
this.title=title;
this.context=context;
this.filterAction=filterAction;
this.expiresIn=expiresIn;
this.keywordsAttributes=keywordsAttributes;
}
}

View File

@@ -1,4 +1,4 @@
package org.joinmastodon.android.api.requests.accounts;
package org.joinmastodon.android.api.requests.filters;
import com.google.gson.reflect.TypeToken;
@@ -7,8 +7,13 @@ import org.joinmastodon.android.model.Filter;
import java.util.List;
public class GetWordFilters extends MastodonAPIRequest<List<Filter>>{
public GetWordFilters(){
public class GetFilters extends MastodonAPIRequest<List<Filter>>{
public GetFilters(){
super(HttpMethod.GET, "/filters", new TypeToken<>(){});
}
@Override
protected String getPathPrefix(){
return "/api/v2";
}
}

View File

@@ -0,0 +1,14 @@
package org.joinmastodon.android.api.requests.filters;
import com.google.gson.reflect.TypeToken;
import org.joinmastodon.android.api.MastodonAPIRequest;
import org.joinmastodon.android.model.LegacyFilter;
import java.util.List;
public class GetLegacyFilters extends MastodonAPIRequest<List<LegacyFilter>>{
public GetLegacyFilters(){
super(HttpMethod.GET, "/filters", new TypeToken<>(){});
}
}

View File

@@ -0,0 +1,18 @@
package org.joinmastodon.android.api.requests.filters;
import com.google.gson.annotations.SerializedName;
class KeywordAttribute{
public String id;
@SerializedName("_destroy")
public Boolean delete;
public String keyword;
public Boolean wholeWord;
public KeywordAttribute(String id, Boolean delete, String keyword, Boolean wholeWord){
this.id=id;
this.delete=delete;
this.keyword=keyword;
this.wholeWord=wholeWord;
}
}

View File

@@ -0,0 +1,30 @@
package org.joinmastodon.android.api.requests.filters;
import org.joinmastodon.android.api.MastodonAPIRequest;
import org.joinmastodon.android.model.Filter;
import org.joinmastodon.android.model.FilterAction;
import org.joinmastodon.android.model.FilterContext;
import org.joinmastodon.android.model.FilterKeyword;
import java.util.EnumSet;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class UpdateFilter extends MastodonAPIRequest<Filter>{
public UpdateFilter(String id, String title, EnumSet<FilterContext> context, FilterAction action, int expiresIn, List<FilterKeyword> words, List<String> deletedWords){
super(HttpMethod.PUT, "/filters/"+id, Filter.class);
List<KeywordAttribute> attrs=Stream.of(
words.stream().map(w->new KeywordAttribute(w.id, null, w.keyword, w.wholeWord)),
deletedWords.stream().map(wid->new KeywordAttribute(wid, true, null, null))
).flatMap(Function.identity()).collect(Collectors.toList());
setRequestBody(new FilterRequest(title, context, action, expiresIn==0 ? null : expiresIn, attrs));
}
@Override
protected String getPathPrefix(){
return "/api/v2";
}
}

View File

@@ -0,0 +1,16 @@
package org.joinmastodon.android.api.requests.instance;
import org.joinmastodon.android.api.MastodonAPIRequest;
import java.time.Instant;
public class GetInstanceExtendedDescription extends MastodonAPIRequest<GetInstanceExtendedDescription.Response>{
public GetInstanceExtendedDescription(){
super(HttpMethod.GET, "/instance/extended_description", Response.class);
}
public static class Response{
public Instant updatedAt;
public String content;
}
}

View File

@@ -0,0 +1,37 @@
package org.joinmastodon.android.api.session;
import android.content.SharedPreferences;
public class AccountLocalPreferences{
private final SharedPreferences prefs;
public boolean showInteractionCounts;
public boolean customEmojiInNames;
public boolean showCWs;
public boolean hideSensitiveMedia;
public AccountLocalPreferences(SharedPreferences prefs){
this.prefs=prefs;
showInteractionCounts=prefs.getBoolean("interactionCounts", true);
customEmojiInNames=prefs.getBoolean("emojiInNames", true);
showCWs=prefs.getBoolean("showCWs", true);
hideSensitiveMedia=prefs.getBoolean("hideSensitive", true);
}
public long getNotificationsPauseEndTime(){
return prefs.getLong("notificationsPauseTime", 0L);
}
public void setNotificationsPauseEndTime(long time){
prefs.edit().putLong("notificationsPauseTime", time).apply();
}
public void save(){
prefs.edit()
.putBoolean("interactionCounts", showInteractionCounts)
.putBoolean("emojiInNames", customEmojiInNames)
.putBoolean("showCWs", showCWs)
.putBoolean("hideSensitive", hideSensitiveMedia)
.apply();
}
}

View File

@@ -1,5 +1,6 @@
package org.joinmastodon.android.api.session;
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.text.TextUtils;
@@ -7,17 +8,20 @@ import android.util.Log;
import org.joinmastodon.android.E;
import org.joinmastodon.android.MastodonApp;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.CacheController;
import org.joinmastodon.android.api.MastodonAPIController;
import org.joinmastodon.android.api.PushSubscriptionManager;
import org.joinmastodon.android.api.StatusInteractionController;
import org.joinmastodon.android.api.requests.accounts.GetPreferences;
import org.joinmastodon.android.api.requests.accounts.UpdateAccountCredentialsPreferences;
import org.joinmastodon.android.api.requests.markers.GetMarkers;
import org.joinmastodon.android.api.requests.markers.SaveMarkers;
import org.joinmastodon.android.api.requests.oauth.RevokeOauthToken;
import org.joinmastodon.android.events.NotificationsMarkerUpdatedEvent;
import org.joinmastodon.android.model.Account;
import org.joinmastodon.android.model.Application;
import org.joinmastodon.android.model.Filter;
import org.joinmastodon.android.model.LegacyFilter;
import org.joinmastodon.android.model.Preferences;
import org.joinmastodon.android.model.PushSubscription;
import org.joinmastodon.android.model.TimelineMarkers;
@@ -46,7 +50,7 @@ public class AccountSession{
public PushSubscription pushSubscription;
public boolean needUpdatePushSettings;
public long filtersLastUpdated;
public List<Filter> wordFilters=new ArrayList<>();
public List<LegacyFilter> wordFilters=new ArrayList<>();
public String pushAccountID;
public AccountActivationInfo activationInfo;
public Preferences preferences;
@@ -55,6 +59,8 @@ public class AccountSession{
private transient CacheController cacheController;
private transient PushSubscriptionManager pushSubscriptionManager;
private transient SharedPreferences prefs;
private transient boolean preferencesNeedSaving;
private transient AccountLocalPreferences localPreferences;
AccountSession(Token token, Account self, Application app, String domain, boolean activated, AccountActivationInfo activationInfo){
this.token=token;
@@ -106,7 +112,8 @@ public class AccountSession{
@Override
public void onSuccess(Preferences result){
preferences=result;
callback.accept(result);
if(callback!=null)
callback.accept(result);
AccountSessionManager.getInstance().writeAccountsFile();
}
@@ -118,7 +125,7 @@ public class AccountSession{
.exec(getID());
}
public SharedPreferences getLocalPreferences(){
public SharedPreferences getRawLocalPreferences(){
if(prefs==null)
prefs=MastodonApp.context.getSharedPreferences(getID(), Context.MODE_PRIVATE);
return prefs;
@@ -150,11 +157,60 @@ public class AccountSession{
}
public String getLastKnownNotificationsMarker(){
return getLocalPreferences().getString("notificationsMarker", null);
return getRawLocalPreferences().getString("notificationsMarker", null);
}
public void setNotificationsMarker(String id, boolean clearUnread){
getLocalPreferences().edit().putString("notificationsMarker", id).apply();
getRawLocalPreferences().edit().putString("notificationsMarker", id).apply();
E.post(new NotificationsMarkerUpdatedEvent(getID(), id, clearUnread));
}
public void logOut(Activity activity, Runnable onDone){
new RevokeOauthToken(app.clientId, app.clientSecret, token.accessToken)
.setCallback(new Callback<>(){
@Override
public void onSuccess(Object result){
AccountSessionManager.getInstance().removeAccount(getID());
onDone.run();
}
@Override
public void onError(ErrorResponse error){
AccountSessionManager.getInstance().removeAccount(getID());
onDone.run();
}
})
.wrapProgress(activity, R.string.loading, false)
.exec(getID());
}
public void savePreferencesLater(){
preferencesNeedSaving=true;
}
public void savePreferencesIfPending(){
if(preferencesNeedSaving){
new UpdateAccountCredentialsPreferences(preferences, null, null)
.setCallback(new Callback<>(){
@Override
public void onSuccess(Account result){
preferencesNeedSaving=false;
self=result;
AccountSessionManager.getInstance().writeAccountsFile();
}
@Override
public void onError(ErrorResponse error){
Log.e(TAG, "failed to save preferences: "+error);
}
})
.exec(getID());
}
}
public AccountLocalPreferences getLocalPreferences(){
if(localPreferences==null)
localPreferences=new AccountLocalPreferences(getRawLocalPreferences());
return localPreferences;
}
}

View File

@@ -13,8 +13,6 @@ import android.net.Uri;
import android.os.Build;
import android.util.Log;
import com.google.gson.JsonParseException;
import org.joinmastodon.android.BuildConfig;
import org.joinmastodon.android.E;
import org.joinmastodon.android.MainActivity;
@@ -22,7 +20,7 @@ import org.joinmastodon.android.MastodonApp;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.MastodonAPIController;
import org.joinmastodon.android.api.PushSubscriptionManager;
import org.joinmastodon.android.api.requests.accounts.GetWordFilters;
import org.joinmastodon.android.api.requests.filters.GetLegacyFilters;
import org.joinmastodon.android.api.requests.instance.GetCustomEmojis;
import org.joinmastodon.android.api.requests.accounts.GetOwnAccount;
import org.joinmastodon.android.api.requests.instance.GetInstance;
@@ -32,7 +30,7 @@ import org.joinmastodon.android.model.Account;
import org.joinmastodon.android.model.Application;
import org.joinmastodon.android.model.Emoji;
import org.joinmastodon.android.model.EmojiCategory;
import org.joinmastodon.android.model.Filter;
import org.joinmastodon.android.model.LegacyFilter;
import org.joinmastodon.android.model.Instance;
import org.joinmastodon.android.model.Token;
@@ -190,6 +188,7 @@ public class AccountSessionManager{
lastActiveAccountID=null;
else
lastActiveAccountID=getLoggedInAccounts().get(0).getID();
prefs.edit().putString("lastActiveAccount", lastActiveAccountID).apply();
}
writeAccountsFile();
String domain=session.domain.toLowerCase();
@@ -299,10 +298,10 @@ public class AccountSessionManager{
}
private void updateSessionWordFilters(AccountSession session){
new GetWordFilters()
new GetLegacyFilters()
.setCallback(new Callback<>(){
@Override
public void onSuccess(List<Filter> result){
public void onSuccess(List<LegacyFilter> result){
session.wordFilters=result;
session.filtersLastUpdated=System.currentTimeMillis();
writeAccountsFile();