Settings and other things

This commit is contained in:
Grishka
2022-04-06 03:11:15 +03:00
parent 0661ce265a
commit f73669124f
63 changed files with 1318 additions and 134 deletions

View File

@@ -1,7 +1,6 @@
package org.joinmastodon.android.api;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
@@ -14,8 +13,6 @@ import org.joinmastodon.android.BuildConfig;
import org.joinmastodon.android.MastodonApp;
import org.joinmastodon.android.api.requests.notifications.GetNotifications;
import org.joinmastodon.android.api.requests.timelines.GetHomeTimeline;
import org.joinmastodon.android.model.Account;
import org.joinmastodon.android.model.Hashtag;
import org.joinmastodon.android.model.Notification;
import org.joinmastodon.android.model.SearchResult;
import org.joinmastodon.android.model.Status;
@@ -26,7 +23,6 @@ import java.util.EnumSet;
import java.util.List;
import java.util.function.Consumer;
import androidx.annotation.Nullable;
import me.grishka.appkit.api.Callback;
import me.grishka.appkit.api.ErrorResponse;
import me.grishka.appkit.utils.WorkerThread;
@@ -87,7 +83,7 @@ public class CacheController{
.exec(accountID);
}catch(SQLiteException x){
Log.w(TAG, x);
uiHandler.post(()->callback.onError(new MastodonErrorResponse(x.getLocalizedMessage())));
uiHandler.post(()->callback.onError(new MastodonErrorResponse(x.getLocalizedMessage(), 500)));
}finally{
closeDelayed();
}
@@ -145,7 +141,7 @@ public class CacheController{
.exec(accountID);
}catch(SQLiteException x){
Log.w(TAG, x);
uiHandler.post(()->callback.onError(new MastodonErrorResponse(x.getLocalizedMessage())));
uiHandler.post(()->callback.onError(new MastodonErrorResponse(x.getLocalizedMessage(), 500)));
}finally{
closeDelayed();
}

View File

@@ -103,7 +103,7 @@ public class MastodonAPIController{
synchronized(req){
req.okhttpCall=null;
}
req.onError(e.getLocalizedMessage());
req.onError(e.getLocalizedMessage(), 0);
}
@Override
@@ -136,7 +136,7 @@ public class MastodonAPIController{
}catch(JsonIOException|JsonSyntaxException x){
if(BuildConfig.DEBUG)
Log.w(TAG, "["+(session==null ? "no-auth" : session.getID())+"] "+response+" error parsing or reading body", x);
req.onError(x.getLocalizedMessage());
req.onError(x.getLocalizedMessage(), response.code());
return;
}
@@ -145,7 +145,7 @@ public class MastodonAPIController{
}catch(IOException x){
if(BuildConfig.DEBUG)
Log.w(TAG, "["+(session==null ? "no-auth" : session.getID())+"] "+response+" error post-processing or validating response", x);
req.onError(x.getLocalizedMessage());
req.onError(x.getLocalizedMessage(), response.code());
return;
}
@@ -157,11 +157,11 @@ public class MastodonAPIController{
try{
JsonObject error=JsonParser.parseReader(reader).getAsJsonObject();
Log.w(TAG, "["+(session==null ? "no-auth" : session.getID())+"] "+response+" received error: "+error);
req.onError(error.get("error").getAsString());
req.onError(error.get("error").getAsString(), response.code());
}catch(JsonIOException|JsonSyntaxException x){
req.onError(response.code()+" "+response.message());
req.onError(response.code()+" "+response.message(), response.code());
}catch(IllegalStateException x){
req.onError("Error parsing an API error");
req.onError("Error parsing an API error", response.code());
}
}
}
@@ -170,7 +170,7 @@ public class MastodonAPIController{
}catch(Exception x){
if(BuildConfig.DEBUG)
Log.w(TAG, "["+(session==null ? "no-auth" : session.getID())+"] error creating and sending http request", x);
req.onError(x.getLocalizedMessage());
req.onError(x.getLocalizedMessage(), 0);
}
}, 0);
}

View File

@@ -2,13 +2,13 @@ package org.joinmastodon.android.api;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.net.Uri;
import android.util.Log;
import android.util.Pair;
import com.google.gson.reflect.TypeToken;
import org.joinmastodon.android.BuildConfig;
import org.joinmastodon.android.api.session.AccountSession;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.model.BaseModel;
@@ -60,6 +60,8 @@ public abstract class MastodonAPIRequest<T> extends APIRequest<T>{
@Override
public synchronized void cancel(){
if(BuildConfig.DEBUG)
Log.d(TAG, "canceling request "+this);
canceled=true;
if(okhttpCall!=null){
okhttpCall.cancel();
@@ -181,8 +183,8 @@ public abstract class MastodonAPIRequest<T> extends APIRequest<T>{
}
}
void onError(String msg){
invokeErrorCallback(new MastodonErrorResponse(msg));
void onError(String msg, int httpStatus){
invokeErrorCallback(new MastodonErrorResponse(msg, httpStatus));
}
void onSuccess(T resp){

View File

@@ -11,9 +11,11 @@ import me.grishka.appkit.api.ErrorResponse;
public class MastodonErrorResponse extends ErrorResponse{
public final String error;
public final int httpStatus;
public MastodonErrorResponse(String error){
public MastodonErrorResponse(String error, int httpStatus){
this.error=error;
this.httpStatus=httpStatus;
}
@Override

View File

@@ -13,6 +13,7 @@ import android.util.Log;
import org.joinmastodon.android.BuildConfig;
import org.joinmastodon.android.MastodonApp;
import org.joinmastodon.android.api.requests.notifications.RegisterForPushNotifications;
import org.joinmastodon.android.api.requests.notifications.UpdatePushSettings;
import org.joinmastodon.android.api.session.AccountSession;
import org.joinmastodon.android.api.session.AccountSessionManager;
import org.joinmastodon.android.model.PushNotification;
@@ -119,6 +120,10 @@ public class PushSubscriptionManager{
}
public void registerAccountForPush(){
registerAccountForPush(null);
}
public void registerAccountForPush(PushSubscription subscription){
if(TextUtils.isEmpty(deviceToken))
throw new IllegalStateException("No device push token available");
MastodonAPIController.runInBackground(()->{
@@ -143,19 +148,22 @@ public class PushSubscriptionManager{
Log.e(TAG, "registerAccountForPush: error generating encryption key", e);
return;
}
new RegisterForPushNotifications(deviceToken, encodedPublicKey, encodedAuthKey, PushSubscription.Alerts.ofAll(), accountID)
new RegisterForPushNotifications(deviceToken,
encodedPublicKey,
encodedAuthKey,
subscription==null ? PushSubscription.Alerts.ofAll() : subscription.alerts,
subscription==null ? PushSubscription.Policy.ALL : subscription.policy,
accountID)
.setCallback(new Callback<>(){
@Override
public void onSuccess(PushSubscription result){
MastodonAPIController.runInBackground(()->{
serverKey=deserializeRawPublicKey(Base64.decode(result.serverKey, Base64.URL_SAFE));
if(serverKey!=null){
AccountSession session=AccountSessionManager.getInstance().getAccount(accountID);
session.pushServerKey=Base64.encodeToString(serverKey.getEncoded(), Base64.URL_SAFE | Base64.NO_WRAP | Base64.NO_PADDING);
AccountSessionManager.getInstance().writeAccountsFile();
Log.d(TAG, "Successfully registered "+accountID+" for push notifications");
}
AccountSession session=AccountSessionManager.getInstance().getAccount(accountID);
session.pushSubscription=result;
AccountSessionManager.getInstance().writeAccountsFile();
Log.d(TAG, "Successfully registered "+accountID+" for push notifications");
});
}
@@ -168,6 +176,34 @@ public class PushSubscriptionManager{
});
}
public void updatePushSettings(PushSubscription subscription){
new UpdatePushSettings(subscription.alerts, subscription.policy)
.setCallback(new Callback<>(){
@Override
public void onSuccess(PushSubscription result){
AccountSession session=AccountSessionManager.getInstance().getAccount(accountID);
if(result.policy!=subscription.policy)
result.policy=subscription.policy;
session.pushSubscription=result;
session.needUpdatePushSettings=false;
AccountSessionManager.getInstance().writeAccountsFile();
}
@Override
public void onError(ErrorResponse error){
if(((MastodonErrorResponse)error).httpStatus==404){ // Not registered for push, register now
registerAccountForPush(subscription);
}else{
AccountSession session=AccountSessionManager.getInstance().getAccount(accountID);
session.needUpdatePushSettings=true;
session.pushSubscription=subscription;
AccountSessionManager.getInstance().writeAccountsFile();
}
}
})
.exec(accountID);
}
private PublicKey deserializeRawPublicKey(byte[] rawBytes){
if(rawBytes.length!=65 && rawBytes.length!=64)
return null;
@@ -320,8 +356,10 @@ public class PushSubscriptionManager{
private static void registerAllAccountsForPush(){
for(AccountSession session:AccountSessionManager.getInstance().getLoggedInAccounts()){
if(TextUtils.isEmpty(session.pushServerKey))
if(session.pushSubscription==null)
session.getPushSubscriptionManager().registerAccountForPush();
else if(session.needUpdatePushSettings)
session.getPushSubscriptionManager().updatePushSettings(session.pushSubscription);
}
}

View File

@@ -4,11 +4,12 @@ import org.joinmastodon.android.api.MastodonAPIRequest;
import org.joinmastodon.android.model.PushSubscription;
public class RegisterForPushNotifications extends MastodonAPIRequest<PushSubscription>{
public RegisterForPushNotifications(String deviceToken, String encryptionKey, String authKey, PushSubscription.Alerts alerts, String accountID){
public RegisterForPushNotifications(String deviceToken, String encryptionKey, String authKey, PushSubscription.Alerts alerts, PushSubscription.Policy policy, String accountID){
super(HttpMethod.POST, "/push/subscription", PushSubscription.class);
Request r=new Request();
r.subscription.endpoint="https://app.joinmastodon.org/relay-to/fcm/"+deviceToken+"/"+accountID;
r.data.alerts=alerts;
r.data.policy=policy;
r.subscription.keys.p256dh=encryptionKey;
r.subscription.keys.auth=authKey;
setRequestBody(r);
@@ -30,6 +31,7 @@ public class RegisterForPushNotifications extends MastodonAPIRequest<PushSubscri
private static class Data{
public PushSubscription.Alerts alerts;
public PushSubscription.Policy policy;
}
}
}

View File

@@ -0,0 +1,25 @@
package org.joinmastodon.android.api.requests.notifications;
import org.joinmastodon.android.api.MastodonAPIRequest;
import org.joinmastodon.android.model.PushSubscription;
public class UpdatePushSettings extends MastodonAPIRequest<PushSubscription>{
public UpdatePushSettings(PushSubscription.Alerts alerts, PushSubscription.Policy policy){
super(HttpMethod.PUT, "/push/subscription", PushSubscription.class);
setRequestBody(new Request(alerts, policy));
}
private static class Request{
public Data data=new Data();
public Request(PushSubscription.Alerts alerts, PushSubscription.Policy policy){
this.data.alerts=alerts;
this.data.policy=policy;
}
private static class Data{
public PushSubscription.Alerts alerts;
public PushSubscription.Policy policy;
}
}
}

View File

@@ -6,7 +6,7 @@ import org.joinmastodon.android.api.PushSubscriptionManager;
import org.joinmastodon.android.api.StatusInteractionController;
import org.joinmastodon.android.model.Account;
import org.joinmastodon.android.model.Application;
import org.joinmastodon.android.model.Instance;
import org.joinmastodon.android.model.PushSubscription;
import org.joinmastodon.android.model.Token;
public class AccountSession{
@@ -19,7 +19,8 @@ public class AccountSession{
public String pushPrivateKey;
public String pushPublicKey;
public String pushAuthKey;
public String pushServerKey;
public PushSubscription pushSubscription;
public boolean needUpdatePushSettings;
private transient MastodonAPIController apiController;
private transient StatusInteractionController statusInteractionController;
private transient CacheController cacheController;