Better char counter and custom emoji in compose

This commit is contained in:
Grishka
2022-02-02 09:40:29 +03:00
parent b9bdf7caec
commit c885a5fc28
19 changed files with 927 additions and 16 deletions

View File

@@ -163,4 +163,8 @@ public class MastodonAPIController{
}
}, 0);
}
public static void runInBackground(Runnable action){
thread.postRunnable(action, 0);
}
}

View File

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

View File

@@ -11,6 +11,7 @@ public class AccountSession{
public String domain;
public int tootCharLimit;
public Application app;
public long infoLastUpdated;
private transient MastodonAPIController apiController;
AccountSession(Token token, Account self, Application app, String domain, int tootCharLimit){
@@ -19,6 +20,7 @@ public class AccountSession{
this.domain=domain;
this.app=app;
this.tootCharLimit=tootCharLimit;
infoLastUpdated=System.currentTimeMillis();
}
AccountSession(){}

View File

@@ -2,21 +2,22 @@ package org.joinmastodon.android.api.session;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.util.Log;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonParseException;
import org.joinmastodon.android.MastodonApp;
import org.joinmastodon.android.OAuthActivity;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.MastodonAPIController;
import org.joinmastodon.android.api.requests.GetCustomEmojis;
import org.joinmastodon.android.api.requests.accounts.GetOwnAccount;
import org.joinmastodon.android.api.requests.oauth.CreateOAuthApp;
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.Instance;
import org.joinmastodon.android.model.Token;
@@ -28,8 +29,13 @@ import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import androidx.annotation.NonNull;
@@ -46,11 +52,14 @@ public class AccountSessionManager{
private static final AccountSessionManager instance=new AccountSessionManager();
private HashMap<String, AccountSession> sessions=new HashMap<>();
private HashMap<String, List<EmojiCategory>> customEmojis=new HashMap<>();
private HashMap<String, Long> customEmojisLastUpdated=new HashMap<>();
private MastodonAPIController unauthenticatedApiController=new MastodonAPIController(null);
private Instance authenticatingInstance;
private Application authenticatingApp;
private String lastActiveAccountID;
private SharedPreferences prefs;
private boolean loadedCustomEmojis;
public static AccountSessionManager getInstance(){
return instance;
@@ -61,15 +70,18 @@ public class AccountSessionManager{
File file=new File(MastodonApp.context.getFilesDir(), "accounts.json");
if(!file.exists())
return;
HashSet<String> domains=new HashSet<>();
try(FileInputStream in=new FileInputStream(file)){
SessionsStorageWrapper w=MastodonAPIController.gson.fromJson(new InputStreamReader(in, StandardCharsets.UTF_8), SessionsStorageWrapper.class);
for(AccountSession session:w.accounts){
domains.add(session.domain.toLowerCase());
sessions.put(session.getID(), session);
}
}catch(IOException x){
}catch(IOException|JsonParseException x){
Log.e(TAG, "Error loading accounts", x);
}
lastActiveAccountID=prefs.getString("lastActiveAccount", null);
MastodonAPIController.runInBackground(()->readCustomEmojis(domains));
}
public void addAccount(Instance instance, Token token, Account self, Application app){
@@ -129,6 +141,10 @@ public class AccountSessionManager{
lastActiveAccountID=getLoggedInAccounts().get(0).getID();
}
writeAccountsFile();
String domain=session.domain.toLowerCase();
if(sessions.isEmpty() || !sessions.values().stream().map(s->s.domain.toLowerCase()).collect(Collectors.toSet()).contains(domain)){
getCustomEmojisFile(domain).delete();
}
}
@NonNull
@@ -181,7 +197,122 @@ public class AccountSessionManager{
return authenticatingApp;
}
public void maybeUpdateLocalInfo(){
long now=System.currentTimeMillis();
HashSet<String> domains=new HashSet<>();
for(AccountSession session:sessions.values()){
domains.add(session.domain.toLowerCase());
if(now-session.infoLastUpdated>24L*3600_000L){
updateSessionLocalInfo(session);
}
}
if(loadedCustomEmojis){
maybeUpdateCustomEmojis(domains);
}
}
private void maybeUpdateCustomEmojis(Set<String> domains){
long now=System.currentTimeMillis();
for(String domain:domains){
Long lastUpdated=customEmojisLastUpdated.get(domain);
if(lastUpdated==null || now-lastUpdated>24L*3600_000L){
updateCustomEmojis(domain);
}
}
}
private void updateSessionLocalInfo(AccountSession session){
new GetOwnAccount()
.setCallback(new Callback<>(){
@Override
public void onSuccess(Account result){
session.self=result;
session.infoLastUpdated=System.currentTimeMillis();
writeAccountsFile();
}
@Override
public void onError(ErrorResponse error){
}
})
.exec(session.getID());
}
private void updateCustomEmojis(String domain){
new GetCustomEmojis()
.setCallback(new Callback<>(){
@Override
public void onSuccess(List<Emoji> result){
CustomEmojisStorageWrapper emojis=new CustomEmojisStorageWrapper();
emojis.lastUpdated=System.currentTimeMillis();
emojis.emojis=result;
customEmojis.put(domain, groupCustomEmojis(emojis));
customEmojisLastUpdated.put(domain, emojis.lastUpdated);
MastodonAPIController.runInBackground(()->writeCustomEmojisFile(emojis, domain));
}
@Override
public void onError(ErrorResponse error){
}
})
.execNoAuth(domain);
}
private File getCustomEmojisFile(String domain){
return new File(MastodonApp.context.getFilesDir(), "emojis_"+domain.replace('.', '_')+".json");
}
private void writeCustomEmojisFile(CustomEmojisStorageWrapper emojis, String domain){
try(FileOutputStream out=new FileOutputStream(getCustomEmojisFile(domain))){
OutputStreamWriter writer=new OutputStreamWriter(out, StandardCharsets.UTF_8);
MastodonAPIController.gson.toJson(emojis, writer);
writer.flush();
}catch(IOException x){
Log.w(TAG, "Error writing emojis file for "+domain, x);
}
}
private void readCustomEmojis(Set<String> domains){
for(String domain:domains){
try(FileInputStream in=new FileInputStream(getCustomEmojisFile(domain))){
InputStreamReader reader=new InputStreamReader(in, StandardCharsets.UTF_8);
CustomEmojisStorageWrapper emojis=MastodonAPIController.gson.fromJson(reader, CustomEmojisStorageWrapper.class);
customEmojis.put(domain, groupCustomEmojis(emojis));
customEmojisLastUpdated.put(domain, emojis.lastUpdated);
}catch(IOException|JsonParseException x){
Log.w(TAG, "Error reading emojis file for "+domain, x);
}
}
if(!loadedCustomEmojis){
loadedCustomEmojis=true;
maybeUpdateCustomEmojis(domains);
}
}
private List<EmojiCategory> groupCustomEmojis(CustomEmojisStorageWrapper emojis){
return emojis.emojis.stream()
.filter(e->e.visibleInPicker)
.collect(Collectors.groupingBy(e->e.category==null ? "" : e.category))
.entrySet()
.stream()
.map(e->new EmojiCategory(e.getKey(), e.getValue()))
.sorted(Comparator.comparing(c->c.title))
.collect(Collectors.toList());
}
public List<EmojiCategory> getCustomEmojis(String domain){
List<EmojiCategory> r=customEmojis.get(domain.toLowerCase());
return r==null ? Collections.emptyList() : r;
}
private static class SessionsStorageWrapper{
public List<AccountSession> accounts;
}
private static class CustomEmojisStorageWrapper{
public List<Emoji> emojis;
public long lastUpdated;
}
}