mirror of
https://github.com/ArcaneChat/android.git
synced 2026-07-03 14:05:24 +02:00
Merge branch 'adb/new-rpc-bindings' into adb/integrated-videocalls
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
package com.b44t.messenger;
|
||||
|
||||
import chat.delta.rpc.BaseTransport;
|
||||
|
||||
/* RPC transport over C FFI */
|
||||
public class FFITransport extends BaseTransport {
|
||||
private final DcJsonrpcInstance dcJsonrpcInstance;
|
||||
|
||||
public FFITransport(DcJsonrpcInstance dcJsonrpcInstance) {
|
||||
this.dcJsonrpcInstance = dcJsonrpcInstance;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void sendRequest(String jsonRequest) {
|
||||
dcJsonrpcInstance.request(jsonRequest);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getResponse() {
|
||||
return dcJsonrpcInstance.getNextResponse();
|
||||
}
|
||||
}
|
||||
@@ -1,171 +0,0 @@
|
||||
package com.b44t.messenger.rpc;
|
||||
|
||||
public class EnteredLoginParam {
|
||||
// Email address.
|
||||
private final String addr;
|
||||
|
||||
// Password.
|
||||
private final String password;
|
||||
|
||||
// ============ IMAP settings ============
|
||||
|
||||
// Server hostname or IP address.
|
||||
private final String imapServer;
|
||||
|
||||
// Server port.
|
||||
private final int imapPort;
|
||||
|
||||
// Socket security.
|
||||
private final SocketSecurity imapSecurity;
|
||||
|
||||
// Username.
|
||||
private final String imapUser;
|
||||
|
||||
// ============ SMTP settings ============
|
||||
|
||||
// Server hostname or IP address.
|
||||
private final String smtpServer;
|
||||
|
||||
// Server port.
|
||||
private final int smtpPort;
|
||||
|
||||
// Socket security.
|
||||
private final SocketSecurity smtpSecurity;
|
||||
|
||||
// Username.
|
||||
private final String smtpUser;
|
||||
|
||||
// SMTP Password. Only needs to be specified if different than IMAP password.
|
||||
private final String smtpPassword;
|
||||
|
||||
// TLS options: whether to allow invalid certificates and/or
|
||||
// invalid hostnames
|
||||
private final EnteredCertificateChecks certificateChecks;
|
||||
|
||||
// If true, login via OAUTH2 (not recommended anymore)
|
||||
private final boolean oauth2;
|
||||
|
||||
public EnteredLoginParam(String addr,
|
||||
String password,
|
||||
String imapServer,
|
||||
int imapPort,
|
||||
SocketSecurity imapSecurity,
|
||||
String imapUser,
|
||||
String smtpServer,
|
||||
int smtpPort,
|
||||
SocketSecurity smtpSecurity,
|
||||
String smtpUser,
|
||||
String smtpPassword,
|
||||
EnteredCertificateChecks certificateChecks,
|
||||
boolean oauth2) {
|
||||
this.addr = addr;
|
||||
this.password = password;
|
||||
this.imapServer = imapServer;
|
||||
this.imapPort = imapPort;
|
||||
this.imapSecurity = imapSecurity;
|
||||
this.imapUser = imapUser;
|
||||
this.smtpServer = smtpServer;
|
||||
this.smtpPort = smtpPort;
|
||||
this.smtpSecurity = smtpSecurity;
|
||||
this.smtpUser = smtpUser;
|
||||
this.smtpPassword = smtpPassword;
|
||||
this.certificateChecks = certificateChecks;
|
||||
this.oauth2 = oauth2;
|
||||
}
|
||||
|
||||
public String getAddr() {
|
||||
return addr;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public String getImapServer() {
|
||||
return imapServer;
|
||||
}
|
||||
|
||||
public int getImapPort() {
|
||||
return imapPort;
|
||||
}
|
||||
|
||||
public SocketSecurity getImapSecurity() {
|
||||
return imapSecurity;
|
||||
}
|
||||
|
||||
public String getImapUser() {
|
||||
return imapUser;
|
||||
}
|
||||
|
||||
public String getSmtpServer() {
|
||||
return smtpServer;
|
||||
}
|
||||
|
||||
public int getSmtpPort() {
|
||||
return smtpPort;
|
||||
}
|
||||
|
||||
public SocketSecurity getSmtpSecurity() {
|
||||
return smtpSecurity;
|
||||
}
|
||||
|
||||
public String getSmtpUser() {
|
||||
return smtpUser;
|
||||
}
|
||||
|
||||
public String getSmtpPassword() {
|
||||
return smtpPassword;
|
||||
}
|
||||
|
||||
public EnteredCertificateChecks getCertificateChecks() {
|
||||
return certificateChecks;
|
||||
}
|
||||
|
||||
public boolean isOauth2() {
|
||||
return oauth2;
|
||||
}
|
||||
|
||||
public enum EnteredCertificateChecks {
|
||||
automatic, strict, acceptInvalidCertificates,
|
||||
}
|
||||
|
||||
public static EnteredCertificateChecks certificateChecksFromInt(int position) {
|
||||
switch (position) {
|
||||
case 0:
|
||||
return EnteredCertificateChecks.automatic;
|
||||
case 1:
|
||||
return EnteredCertificateChecks.strict;
|
||||
case 2:
|
||||
return EnteredCertificateChecks.acceptInvalidCertificates;
|
||||
}
|
||||
throw new IllegalArgumentException("Invalid certificate position: " + position);
|
||||
}
|
||||
|
||||
public enum SocketSecurity {
|
||||
// Unspecified socket security, select automatically.
|
||||
automatic,
|
||||
|
||||
// TLS connection.
|
||||
ssl,
|
||||
|
||||
// STARTTLS connection.
|
||||
starttls,
|
||||
|
||||
// No TLS, plaintext connection.
|
||||
plain,
|
||||
}
|
||||
|
||||
public static SocketSecurity socketSecurityFromInt(int position) {
|
||||
switch (position) {
|
||||
case 0:
|
||||
return SocketSecurity.automatic;
|
||||
case 1:
|
||||
return SocketSecurity.ssl;
|
||||
case 2:
|
||||
return SocketSecurity.starttls;
|
||||
case 3:
|
||||
return SocketSecurity.plain;
|
||||
}
|
||||
throw new IllegalArgumentException("Invalid socketSecurity position: " + position);
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
package com.b44t.messenger.rpc;
|
||||
|
||||
import android.util.Base64;
|
||||
|
||||
public class HttpResponse {
|
||||
// base64-encoded response body.
|
||||
private final String blob;
|
||||
// MIME type, e.g. "text/plain" or "text/html".
|
||||
private final String mimetype;
|
||||
// Encoding, e.g. "utf-8".
|
||||
private final String encoding;
|
||||
|
||||
public HttpResponse(String blob, String mimetype, String encoding) {
|
||||
this.blob = blob;
|
||||
this.mimetype = mimetype;
|
||||
this.encoding = encoding;
|
||||
}
|
||||
|
||||
public byte[] getBlob() {
|
||||
if (blob == null) {
|
||||
return null;
|
||||
}
|
||||
return Base64.decode(blob, Base64.NO_WRAP | Base64.NO_PADDING);
|
||||
}
|
||||
|
||||
public String getMimetype() {
|
||||
return mimetype;
|
||||
}
|
||||
|
||||
public String getEncoding() {
|
||||
return encoding;
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package com.b44t.messenger.rpc;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
public class Reaction {
|
||||
// The reaction emoji string.
|
||||
private final String emoji;
|
||||
// The count of users that have reacted with this reaction.
|
||||
private final int count;
|
||||
// true if self-account reacted with this reaction, false otherwise.
|
||||
private final boolean isFromSelf;
|
||||
|
||||
public Reaction(String emoji, int count, boolean isFromSelf) {
|
||||
this.emoji = emoji;
|
||||
this.count = count;
|
||||
this.isFromSelf = isFromSelf;
|
||||
}
|
||||
|
||||
public String getEmoji() {
|
||||
return emoji;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
public boolean isFromSelf() {
|
||||
return isFromSelf;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
if (obj instanceof Reaction) {
|
||||
Reaction reaction = (Reaction) obj;
|
||||
return emoji.equals(reaction.getEmoji()) && count == reaction.getCount() && isFromSelf == reaction.isFromSelf();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
package com.b44t.messenger.rpc;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class Reactions {
|
||||
// Map from a contact to it's reaction to message.
|
||||
private final HashMap<Integer, String[]> reactionsByContact;
|
||||
// Unique reactions, sorted in descending order.
|
||||
private final ArrayList<Reaction> reactions;
|
||||
|
||||
public Reactions(HashMap<Integer, String[]> reactionsByContact, ArrayList<Reaction> reactions) {
|
||||
this.reactionsByContact = reactionsByContact;
|
||||
this.reactions = reactions;
|
||||
}
|
||||
|
||||
public Map<Integer, String[]> getReactionsByContact() {
|
||||
return reactionsByContact;
|
||||
}
|
||||
|
||||
public List<Reaction> getReactions() {
|
||||
return reactions;
|
||||
}
|
||||
}
|
||||
@@ -1,203 +0,0 @@
|
||||
package com.b44t.messenger.rpc;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.b44t.messenger.DcJsonrpcInstance;
|
||||
import com.b44t.messenger.util.concurrent.SettableFuture;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
public class Rpc {
|
||||
private final static String TAG = Rpc.class.getSimpleName();
|
||||
|
||||
private final Map<Integer, SettableFuture<JsonElement>> requestFutures = new ConcurrentHashMap<>();
|
||||
private final DcJsonrpcInstance dcJsonrpcInstance;
|
||||
private int requestId = 0;
|
||||
private boolean started = false;
|
||||
private final Gson gson = new GsonBuilder().serializeNulls().create();
|
||||
|
||||
public Rpc(DcJsonrpcInstance dcJsonrpcInstance) {
|
||||
this.dcJsonrpcInstance = dcJsonrpcInstance;
|
||||
}
|
||||
|
||||
private void processResponse() throws JsonSyntaxException {
|
||||
String jsonResponse = dcJsonrpcInstance.getNextResponse();
|
||||
|
||||
Response response = gson.fromJson(jsonResponse, Response.class);
|
||||
if (response == null) {
|
||||
Log.e(TAG, "Error parsing JSON: " + jsonResponse);
|
||||
return;
|
||||
} else if (response.id == 0) {
|
||||
// Got JSON-RPC notification/event, ignore
|
||||
return;
|
||||
}
|
||||
|
||||
SettableFuture<JsonElement> future = requestFutures.remove(response.id);
|
||||
if (future == null) { // Got a response with unknown ID, ignore
|
||||
return;
|
||||
}
|
||||
|
||||
if (response.error != null) {
|
||||
String message;
|
||||
try {
|
||||
message = response.error.getAsJsonObject().get("message").getAsString();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Can't get response error message: " + e);
|
||||
message = response.error.toString();
|
||||
}
|
||||
future.setException(new RpcException(message));
|
||||
} else if (response.result != null) {
|
||||
future.set(response.result);
|
||||
} else {
|
||||
future.setException(new RpcException("Got JSON-RPC response without result or error: " + jsonResponse));
|
||||
}
|
||||
}
|
||||
|
||||
public void start() {
|
||||
started = true;
|
||||
new Thread(() -> {
|
||||
while (true) {
|
||||
try {
|
||||
processResponse();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}, "jsonrpcThread").start();
|
||||
}
|
||||
|
||||
public SettableFuture<JsonElement> call(String method, Object... params) throws RpcException {
|
||||
if (!started) throw new RpcException("RPC not started yet.");
|
||||
|
||||
int id;
|
||||
synchronized (this) {
|
||||
id = ++requestId;
|
||||
}
|
||||
String jsonRequest = gson.toJson(new Request(method, params, id));
|
||||
SettableFuture<JsonElement> future = new SettableFuture<>();
|
||||
requestFutures.put(id, future);
|
||||
dcJsonrpcInstance.request(jsonRequest);
|
||||
return future;
|
||||
}
|
||||
|
||||
public JsonElement getResult(String method, Object... params) throws RpcException {
|
||||
try {
|
||||
return call(method, params).get();
|
||||
} catch (ExecutionException e) {
|
||||
throw (RpcException)e.getCause();
|
||||
} catch (InterruptedException e) {
|
||||
throw new RpcException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public List<VcardContact> parseVcard(String path) throws RpcException {
|
||||
TypeToken<List<VcardContact>> listType = new TypeToken<List<VcardContact>>(){};
|
||||
return gson.fromJson(getResult("parse_vcard", path), listType.getType());
|
||||
}
|
||||
|
||||
public String makeVcard(int accountId, int... contacts) throws RpcException {
|
||||
return gson.fromJson(getResult("make_vcard", accountId, contacts), String.class);
|
||||
}
|
||||
|
||||
public List<Integer> importVcard(int accountId, String path) throws RpcException {
|
||||
TypeToken<List<Integer>> listType = new TypeToken<List<Integer>>(){};
|
||||
return gson.fromJson(getResult("import_vcard", accountId, path), listType.getType());
|
||||
}
|
||||
|
||||
public HttpResponse getHttpResponse(int accountId, String url) throws RpcException {
|
||||
return gson.fromJson(getResult("get_http_response", accountId, url), HttpResponse.class);
|
||||
}
|
||||
|
||||
public Reactions getMsgReactions(int accountId, int msgId) throws RpcException {
|
||||
return gson.fromJson(getResult("get_message_reactions", accountId, msgId), Reactions.class);
|
||||
}
|
||||
|
||||
public int sendReaction(int accountId, int msgId, String... reaction) throws RpcException {
|
||||
return getResult("send_reaction", accountId, msgId, reaction).getAsInt();
|
||||
}
|
||||
|
||||
public int draftSelfReport(int accountId) throws RpcException {
|
||||
return getResult("draft_self_report", accountId).getAsInt();
|
||||
}
|
||||
|
||||
public void sendWebxdcRealtimeData(Integer accountId, Integer instanceMsgId, List<Integer> data) throws RpcException {
|
||||
getResult("send_webxdc_realtime_data", accountId, instanceMsgId, data);
|
||||
}
|
||||
|
||||
public void sendWebxdcRealtimeAdvertisement(Integer accountId, Integer instanceMsgId) throws RpcException {
|
||||
getResult("send_webxdc_realtime_advertisement", accountId, instanceMsgId);
|
||||
}
|
||||
|
||||
public void leaveWebxdcRealtime(Integer accountId, Integer instanceMessageId) throws RpcException {
|
||||
getResult("leave_webxdc_realtime", accountId, instanceMessageId);
|
||||
}
|
||||
|
||||
public int getAccountFileSize(int accountId) throws RpcException {
|
||||
return getResult("get_account_file_size", accountId).getAsInt();
|
||||
}
|
||||
|
||||
public void changeContactName(int accountId, int contactId, String name) throws RpcException {
|
||||
getResult("change_contact_name", accountId, contactId, name);
|
||||
}
|
||||
|
||||
public int addAccount() throws RpcException {
|
||||
return getResult("add_account").getAsInt();
|
||||
}
|
||||
|
||||
public void addTransportFromQr(int accountId, String qrCode) throws RpcException {
|
||||
getResult("add_transport_from_qr", accountId, qrCode);
|
||||
}
|
||||
|
||||
public void addOrUpdateTransport(int accountId, EnteredLoginParam param) throws RpcException {
|
||||
getResult("add_or_update_transport", accountId, param);
|
||||
}
|
||||
|
||||
public int createBroadcast(int accountId, String chatName) throws RpcException {
|
||||
return gson.fromJson(getResult("create_broadcast", accountId, chatName), Integer.class);
|
||||
}
|
||||
|
||||
public int createGroupChatUnencrypted(int accountId, String chatName) throws RpcException {
|
||||
return gson.fromJson(getResult("create_group_chat_unencrypted", accountId, chatName), Integer.class);
|
||||
}
|
||||
|
||||
public void setAccountsOrder(List<Integer> order) throws RpcException {
|
||||
getResult("set_accounts_order", order);
|
||||
}
|
||||
|
||||
private static class Request {
|
||||
private final String jsonrpc = "2.0";
|
||||
public final String method;
|
||||
public final Object[] params;
|
||||
public final int id;
|
||||
|
||||
public Request(String method, Object[] params, int id) {
|
||||
this.method = method;
|
||||
this.params = params;
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
|
||||
public String getMigrationError(int accountId) throws RpcException {
|
||||
return gson.fromJson(getResult("get_migration_error", accountId), String.class);
|
||||
}
|
||||
|
||||
private static class Response {
|
||||
public final int id;
|
||||
public final JsonElement result;
|
||||
public final JsonElement error;
|
||||
|
||||
public Response(int id, JsonElement result, JsonElement error) {
|
||||
this.id = id;
|
||||
this.result = result;
|
||||
this.error = error;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
package com.b44t.messenger.rpc;
|
||||
|
||||
/**
|
||||
* An exception occurred while processing a request in Delta Chat core.
|
||||
**/
|
||||
public class RpcException extends Exception {
|
||||
|
||||
public RpcException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
package com.b44t.messenger.rpc;
|
||||
|
||||
import android.util.Base64;
|
||||
|
||||
public class VcardContact {
|
||||
// Email address.
|
||||
private final String addr;
|
||||
|
||||
// The contact's name, or the email address if no name was given.
|
||||
private final String displayName;
|
||||
|
||||
// Public PGP key in Base64.
|
||||
private final String key;
|
||||
|
||||
// Profile image in Base64.
|
||||
private final String profileImage;
|
||||
|
||||
// Contact color in HTML color format.
|
||||
private final String color;
|
||||
|
||||
// Last update timestamp.
|
||||
private final int timestamp;
|
||||
|
||||
public VcardContact(String addr, String displayName, String key, String profileImage, String color, int timestamp) {
|
||||
this.addr = addr;
|
||||
this.displayName = displayName;
|
||||
this.key = key;
|
||||
this.profileImage = profileImage;
|
||||
this.color = color;
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public String getAddr() {
|
||||
return addr;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
public byte[] getKey() {
|
||||
return key == null? null : Base64.decode(key, Base64.NO_WRAP | Base64.NO_PADDING);
|
||||
}
|
||||
|
||||
public boolean hasProfileImage() {
|
||||
return profileImage != null;
|
||||
}
|
||||
|
||||
public byte[] getProfileImage() {
|
||||
return profileImage == null? null : Base64.decode(profileImage, Base64.NO_WRAP | Base64.NO_PADDING);
|
||||
}
|
||||
|
||||
public String getColor() {
|
||||
return color;
|
||||
}
|
||||
|
||||
public int getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user