feat: Move to addTransport / addTransportFromQr API (#3676), remove AEAP message (#3698)

This commit is contained in:
Hocuri 2025-04-02 18:14:49 +02:00 committed by GitHub
parent e2d7f2c3d3
commit 10c4a105bb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 256 additions and 54 deletions

View file

@ -5,6 +5,7 @@
* hide superfluous "Show Classic E-mails" advanced setting for chatmail
* remove mostly non-telling transport addresses when referring to a contact;
the contact's profile gives a much better overview
* Disable AEAP to enable us to overhaul some things - there are big changes underway in this area, which will come in a few months
## v1.56.1
2025-03

View file

@ -118,7 +118,6 @@ public class DcContext {
public native void setStockTranslation (int stockId, String translation);
public native String getBlobdir ();
public native String getLastError ();
public native void configure ();
public native void stopOngoingProcess ();
public native int isConfigured ();
public native boolean open (String passphrase);

View file

@ -0,0 +1,171 @@
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);
}
}

View file

@ -1,5 +1,7 @@
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;
@ -8,12 +10,16 @@ import com.google.gson.JsonElement;
import com.google.gson.JsonSyntaxException;
import com.google.gson.reflect.TypeToken;
import org.thoughtcrime.securesms.qr.QrShowFragment;
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;
@ -38,7 +44,14 @@ public class Rpc {
}
if (response.error != null) {
future.setException(new RpcException(response.error.toString()));
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 {
@ -137,6 +150,14 @@ public class Rpc {
return getResult("add_account").getAsInt();
}
public void addTransportFromQr(int accountId, String qrCode) throws RpcException {
getResult("add_transport_from_qr", accountId, qrCode);
}
public void addTransport(int accountId, EnteredLoginParam param) throws RpcException {
getResult("add_transport", accountId, param);
}
private static class Request {
private final String jsonrpc = "2.0";
public final String method;

View file

@ -32,6 +32,8 @@ import androidx.loader.app.LoaderManager;
import com.b44t.messenger.DcContext;
import com.b44t.messenger.DcEvent;
import com.b44t.messenger.DcLot;
import com.b44t.messenger.rpc.Rpc;
import com.b44t.messenger.rpc.RpcException;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.request.target.CustomTarget;
import com.bumptech.glide.request.transition.Transition;
@ -408,14 +410,7 @@ public class InstantOnboardingActivity extends BaseActionBarActivity implements
if (eventId == DcContext.DC_EVENT_CONFIGURE_PROGRESS) {
long progress = event.getData1Int();
if (progress==0/*error/aborted*/) {
progressError(event.getData2Str());
} else if (progress<1000/*progress in permille*/) {
progressUpdate((int)progress);
} else if (progress==1000/*done*/) {
DcHelper.getAccounts(this).startIo();
progressSuccess();
}
progressUpdate((int)progress);
}
}
@ -499,14 +494,13 @@ public class InstantOnboardingActivity extends BaseActionBarActivity implements
DcHelper.getEventCenter(this).captureNextError();
new Thread(() -> {
if (!dcContext.setConfigFromQr(qrCode)) {
Util.runOnMain(() -> {
progressError(dcContext.getLastError());
});
return;
}
DcHelper.getAccounts(this).stopIo();
dcContext.configure();
Rpc rpc = DcHelper.getRpc(this);
try {
rpc.addTransportFromQr(dcContext.getAccountId(), qrCode);
progressSuccess();
} catch (RpcException e) {
Util.runOnMain(() -> progressError(e.getMessage()));
}
}).start();
}

View file

@ -46,6 +46,9 @@ import androidx.constraintlayout.widget.Group;
import com.b44t.messenger.DcContext;
import com.b44t.messenger.DcEvent;
import com.b44t.messenger.DcProvider;
import com.b44t.messenger.rpc.EnteredLoginParam;
import com.b44t.messenger.rpc.Rpc;
import com.b44t.messenger.rpc.RpcException;
import com.b44t.messenger.util.concurrent.ListenableFuture;
import com.b44t.messenger.util.concurrent.SettableFuture;
import com.google.android.material.textfield.TextInputEditText;
@ -280,19 +283,7 @@ public class RegistrationActivity extends BaseActionBarActivity implements DcEve
int id = item.getItemId();
if (id == R.id.do_register) {
String oldAddr = DcHelper.getSelfAddr(this);
String newAddr = emailInput.getText().toString();
if (!TextUtils.isEmpty(oldAddr)
&& !TextUtils.equals(oldAddr.toLowerCase(Locale.ROOT), newAddr.toLowerCase(Locale.ROOT))) {
// Tell the user about AEAP if they are about to change their address
new AlertDialog.Builder(this)
.setMessage(getString(R.string.aeap_explanation, oldAddr, newAddr))
.setNegativeButton(R.string.cancel, (d, w) -> {})
.setPositiveButton(R.string.perm_continue, (d, w) -> do_register())
.show();
} else {
do_register();
}
do_register();
return true;
} else if (id == android.R.id.home) {
// handle close button click here
@ -616,9 +607,42 @@ public class RegistrationActivity extends BaseActionBarActivity implements DcEve
// calling configure() results in
// receiving multiple DC_EVENT_CONFIGURE_PROGRESS events
DcHelper.getAccounts(this).stopIo();
DcHelper.getEventCenter(this).captureNextError();
DcHelper.getContext(this).configure();
EnteredLoginParam param = new EnteredLoginParam(
getParam(R.id.email_text, true),
getParam(R.id.password_text, false),
getParam(R.id.imap_server_text, true),
Util.objectToInt(getParam(R.id.imap_port_text, true)),
EnteredLoginParam.socketSecurityFromInt(imapSecurity.getSelectedItemPosition()),
getParam(R.id.imap_login_text, false),
getParam(R.id.smtp_server_text, true),
Util.objectToInt(getParam(R.id.smtp_port_text, true)),
EnteredLoginParam.socketSecurityFromInt(smtpSecurity.getSelectedItemPosition()),
getParam(R.id.smtp_login_text, false),
getParam(R.id.smtp_password_text, false),
EnteredLoginParam.certificateChecksFromInt(certCheck.getSelectedItemPosition()),
authMethod.getSelectedItemPosition() == 1
);
new Thread(() -> {
Rpc rpc = DcHelper.getRpc(this);
try {
rpc.addTransport(DcHelper.getContext(this).getAccountId(), param);
DcHelper.getAccounts(this).startIo();
DcHelper.getEventCenter(this).endCaptureNextError();
progressDialog.dismiss();
Intent conversationList = new Intent(getApplicationContext(), ConversationListActivity.class);
startActivity(conversationList);
finish();
} catch (RpcException e) {
Util.runOnMain(() -> {
DcHelper.getEventCenter(this).endCaptureNextError();
progressDialog.dismiss();
WelcomeActivity.maybeShowConfigurationError(this, e.getMessage());
});
}
}).start();
}
private void setConfig(@IdRes int viewId, String configTarget, boolean doTrim) {
@ -630,6 +654,15 @@ public class RegistrationActivity extends BaseActionBarActivity implements DcEve
DcHelper.getContext(this).setConfig(configTarget, value.isEmpty()? null : value);
}
private String getParam(@IdRes int viewId, boolean doTrim) {
TextInputEditText view = findViewById(viewId);
String value = view.getText().toString();
if(doTrim) {
value = value.trim();
}
return value.isEmpty()? null : value;
}
private void stopLoginProcess() {
DcHelper.getContext(this).stopOngoingProcess();
}
@ -637,25 +670,9 @@ public class RegistrationActivity extends BaseActionBarActivity implements DcEve
@Override
public void handleEvent(@NonNull DcEvent event) {
if (event.getId()==DcContext.DC_EVENT_CONFIGURE_PROGRESS) {
long progress = event.getData1Int();
if (progress==0/*error/aborted*/) {
DcHelper.getAccounts(this).startIo(); // start-io is also needed on errors to make previous config work in case of changes
DcHelper.getEventCenter(this).endCaptureNextError();
progressDialog.dismiss();
WelcomeActivity.maybeShowConfigurationError(this, event.getData2Str());
}
else if (progress<1000/*progress in permille*/) {
int percent = (int)progress / 10;
progressDialog.setMessage(getResources().getString(R.string.one_moment)+String.format(" %d%%", percent));
}
else if (progress==1000/*done*/) {
DcHelper.getAccounts(this).startIo();
DcHelper.getEventCenter(this).endCaptureNextError();
progressDialog.dismiss();
Intent conversationList = new Intent(getApplicationContext(), ConversationListActivity.class);
startActivity(conversationList);
finish();
}
long progress = event.getData1Int(); // progress in permille
int percent = (int)progress / 10;
progressDialog.setMessage(getResources().getString(R.string.one_moment)+String.format(" %d%%", percent));
}
}
}

View file

@ -237,7 +237,6 @@ public class DcHelper {
dcContext.setStockTranslation(120, context.getString(R.string.qrshow_join_group_hint).replace("\"", ""));
dcContext.setStockTranslation(121, context.getString(R.string.connectivity_not_connected));
dcContext.setStockTranslation(122, context.getString(R.string.aeap_addr_changed));
dcContext.setStockTranslation(123, context.getString(R.string.aeap_explanation));
dcContext.setStockTranslation(162, context.getString(R.string.multidevice_qr_subtitle));
dcContext.setStockTranslation(163, context.getString(R.string.multidevice_transfer_done_devicemsg));

View file

@ -531,7 +531,7 @@
<string name="send_message">Send Message</string>
<!-- Placeholder %1$s will be replaced by the name of the contact changing their address. Placeholders %2$s and %3$s will be replaced by old/new addresses. -->
<string name="aeap_addr_changed">%1$s changed their address from %2$s to %3$s</string>
<!-- the explanation is shown (1) as a modal dialog with the buttons "Cancel" and "Continue" as well as (2) as a device message -->
<!-- deprecated -->
<string name="aeap_explanation">You changed your email address from %1$s to %2$s.\n\nIf you now send a message to a verified group, contacts there will automatically replace the old with your new address.\n\nIt\'s highly advised to set up your old email provider to forward all emails to your new email address. Otherwise you might miss messages of contacts who did not get your new address yet.</string>