Merge remote-tracking branch 'upstream/main'

This commit is contained in:
adbenitez
2026-03-19 16:55:53 +01:00
63 changed files with 2919 additions and 6505 deletions
+28 -8
View File
@@ -289,6 +289,7 @@ public class Rpc {
* from a server encoded in a QR code.
* - [Self::list_transports()] to get a list of all configured transports.
* - [Self::delete_transport()] to remove a transport.
* - [Self::set_transport_unpublished()] to set whether contacts see this transport.
*/
public void addOrUpdateTransport(Integer accountId, EnteredLoginParam param) throws RpcException {
transport.call("add_or_update_transport", mapper.valueToTree(accountId), mapper.valueToTree(param));
@@ -312,11 +313,22 @@ public class Rpc {
* Returns the list of all email accounts that are used as a transport in the current profile.
* Use [Self::add_or_update_transport()] to add or change a transport
* and [Self::delete_transport()] to delete a transport.
* Use [Self::list_transports_ex()] to additionally query
* whether the transports are marked as 'unpublished'.
*/
public java.util.List<EnteredLoginParam> listTransports(Integer accountId) throws RpcException {
return transport.callForResult(new TypeReference<java.util.List<EnteredLoginParam>>(){}, "list_transports", mapper.valueToTree(accountId));
}
/**
* Returns the list of all email accounts that are used as a transport in the current profile.
* Use [Self::add_or_update_transport()] to add or change a transport
* and [Self::delete_transport()] to delete a transport.
*/
public java.util.List<TransportListEntry> listTransportsEx(Integer accountId) throws RpcException {
return transport.callForResult(new TypeReference<java.util.List<TransportListEntry>>(){}, "list_transports_ex", mapper.valueToTree(accountId));
}
/**
* Removes the transport with the specified email address
* (i.e. [EnteredLoginParam::addr]).
@@ -325,6 +337,22 @@ public class Rpc {
transport.call("delete_transport", mapper.valueToTree(accountId), mapper.valueToTree(addr));
}
/**
* Change whether the transport is unpublished.
* <p>
* Unpublished transports are not advertised to contacts,
* and self-sent messages are not sent there,
* so that we don't cause extra messages to the corresponding inbox,
* but can still receive messages from contacts who don't know our new transport addresses yet.
* <p>
* The default is false, but when the user updates from a version that didn't have this flag,
* existing secondary transports are set to unpublished,
* so that an existing transport address doesn't suddenly get spammed with a lot of messages.
*/
public void setTransportUnpublished(Integer accountId, String addr, Boolean unpublished) throws RpcException {
transport.call("set_transport_unpublished", mapper.valueToTree(accountId), mapper.valueToTree(addr), mapper.valueToTree(unpublished));
}
/** Signal an ongoing process to stop. */
public void stopOngoingProcess(Integer accountId) throws RpcException {
transport.call("stop_ongoing_process", mapper.valueToTree(accountId));
@@ -410,14 +438,6 @@ public class Rpc {
return transport.callForResult(new TypeReference<Integer>(){}, "estimate_auto_deletion_count", mapper.valueToTree(accountId), mapper.valueToTree(fromServer), mapper.valueToTree(seconds));
}
public String initiateAutocryptKeyTransfer(Integer accountId) throws RpcException {
return transport.callForResult(new TypeReference<String>(){}, "initiate_autocrypt_key_transfer", mapper.valueToTree(accountId));
}
public void continueAutocryptKeyTransfer(Integer accountId, Integer messageId, String setupCode) throws RpcException {
transport.call("continue_autocrypt_key_transfer", mapper.valueToTree(accountId), mapper.valueToTree(messageId), mapper.valueToTree(setupCode));
}
public java.util.List<Integer> getChatlistEntries(Integer accountId, Integer listFlags, String queryString, Integer queryContactId) throws RpcException {
return transport.callForResult(new TypeReference<java.util.List<Integer>>(){}, "get_chatlist_entries", mapper.valueToTree(accountId), mapper.valueToTree(listFlags), mapper.valueToTree(queryString), mapper.valueToTree(queryContactId));
}
@@ -387,6 +387,8 @@ public abstract class EventType {
public static class IncomingCallAccepted extends EventType {
/** ID of the chat which the message belongs to. */
public Integer chat_id;
/** The call was accepted from this device (process). */
public Boolean from_this_device;
/** ID of the info message referring to the call. */
public Integer msg_id;
}
@@ -32,7 +32,6 @@ public class Message {
public Boolean isEdited;
public Boolean isForwarded;
public Boolean isInfo;
public Boolean isSetupmessage;
@com.fasterxml.jackson.annotation.JsonSetter(nulls = com.fasterxml.jackson.annotation.Nulls.SET)
public Integer originalMsgId;
@com.fasterxml.jackson.annotation.JsonSetter(nulls = com.fasterxml.jackson.annotation.Nulls.SET)
@@ -47,8 +46,6 @@ public class Message {
@com.fasterxml.jackson.annotation.JsonSetter(nulls = com.fasterxml.jackson.annotation.Nulls.SET)
public Integer savedMessageId;
public Contact sender;
@com.fasterxml.jackson.annotation.JsonSetter(nulls = com.fasterxml.jackson.annotation.Nulls.SET)
public String setupCodeBegin;
/**
* True if the message was correctly encrypted&signed, false otherwise. Historically, UIs showed a small padlock on the message then.
* <p>
@@ -42,7 +42,6 @@ public abstract class MessageLoadResult {
public Boolean isEdited;
public Boolean isForwarded;
public Boolean isInfo;
public Boolean isSetupmessage;
@com.fasterxml.jackson.annotation.JsonSetter(nulls = com.fasterxml.jackson.annotation.Nulls.SET)
public Integer originalMsgId;
@com.fasterxml.jackson.annotation.JsonSetter(nulls = com.fasterxml.jackson.annotation.Nulls.SET)
@@ -57,8 +56,6 @@ public abstract class MessageLoadResult {
@com.fasterxml.jackson.annotation.JsonSetter(nulls = com.fasterxml.jackson.annotation.Nulls.SET)
public Integer savedMessageId;
public Contact sender;
@com.fasterxml.jackson.annotation.JsonSetter(nulls = com.fasterxml.jackson.annotation.Nulls.SET)
public String setupCodeBegin;
/**
* True if the message was correctly encrypted&signed, false otherwise. Historically, UIs showed a small padlock on the message then.
* <p>
@@ -0,0 +1,9 @@
/* Autogenerated file, do not edit manually */
package chat.delta.rpc.types;
public class TransportListEntry {
/** Whether this transport is set to 'unpublished'. See `set_transport_unpublished` / `setTransportUnpublished` for details. */
public Boolean isUnpublished;
/** The login data entered by the user. */
public EnteredLoginParam param;
}
@@ -176,8 +176,6 @@ public class DcContext {
public native String getConnectivityHtml();
public native String initiateKeyTransfer();
public native void imex(int what, String dir);
public native String imexHasBackup(String dir);
@@ -25,7 +25,6 @@ public class DcMsg {
public static final int DC_INFO_GROUP_IMAGE_CHANGED = 3;
public static final int DC_INFO_MEMBER_ADDED_TO_GROUP = 4;
public static final int DC_INFO_MEMBER_REMOVED_FROM_GROUP = 5;
public static final int DC_INFO_AUTOCRYPT_SETUP_MESSAGE = 6;
public static final int DC_INFO_SECURE_JOIN_MESSAGE = 7;
public static final int DC_INFO_LOCATIONSTREAMING_ENABLED = 8;
public static final int DC_INFO_LOCATION_ONLY = 9;
@@ -191,8 +190,6 @@ public class DcMsg {
public native boolean hasHtml();
public native String getSetupCodeBegin();
public native void setText(String text);
public native void setSubject(String text);
@@ -81,19 +81,14 @@ public abstract class BaseActionBarActivity extends AppCompatActivity {
}
}
public void makeSearchMenuVisible(final Menu menu, final MenuItem searchItem, boolean visible) {
public void makeSearchMenuVisible(final Menu menu, final MenuItem searchItem) {
for (int i = 0; i < menu.size(); ++i) {
MenuItem item = menu.getItem(i);
int id = item.getItemId();
if (id == R.id.menu_search_up || id == R.id.menu_search_down) {
item.setVisible(visible);
} else if (id == R.id.menu_search_counter) {
item.setVisible(false); // always hide menu_search_counter initially
} else if (item == searchItem) {
; // searchItem is just always visible
} else {
item.setVisible(
!visible); // if search is shown, other items are hidden - and the other way round
item.setVisible(true);
} else if (item != searchItem) {
item.setVisible(false); // hide all other items
}
}
}
@@ -344,6 +344,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
setDcEventListener(); // reset event listener
handleRelaying();
invalidateOptionsMenu(); // set correct menu visibility in case of chat changes
if (fragment != null) {
fragment.onNewIntent();
@@ -574,7 +575,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
@Override
public boolean onMenuItemActionCollapse(final MenuItem item) {
searchCollapse(menu, item);
searchCollapse();
return true;
}
});
@@ -1825,14 +1826,15 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
beforeSearchComposeVisibility = composePanel.getVisibility();
composePanel.setVisibility(View.GONE);
ConversationActivity.this.makeSearchMenuVisible(menu, searchItem, true);
ConversationActivity.this.makeSearchMenuVisible(menu, searchItem);
}
private void searchCollapse(final Menu menu, final MenuItem searchItem) {
private void searchCollapse() {
searchMenu = null;
composePanel.setVisibility(beforeSearchComposeVisibility);
ConversationActivity.this.makeSearchMenuVisible(menu, searchItem, false);
// trigger onPrepareOptionsMenu() to restore correct menu visibility
invalidateOptionsMenu();
}
private void handleMenuSearchNext(boolean searchNext) {
@@ -194,13 +194,13 @@ public class WebViewActivity extends PassphraseRequiredActionBarActivity
public boolean onMenuItemActionExpand(final MenuItem item) {
searchMenu = menu;
WebViewActivity.this.lastQuery = "";
WebViewActivity.this.makeSearchMenuVisible(menu, searchItem, true);
WebViewActivity.this.makeSearchMenuVisible(menu, searchItem);
return true;
}
@Override
public boolean onMenuItemActionCollapse(final MenuItem item) {
WebViewActivity.this.makeSearchMenuVisible(menu, searchItem, false);
WebViewActivity.this.invalidateOptionsMenu();
return true;
}
});
@@ -16,7 +16,7 @@ import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import chat.delta.rpc.Rpc;
import chat.delta.rpc.RpcException;
import chat.delta.rpc.types.EnteredLoginParam;
import chat.delta.rpc.types.TransportListEntry;
import com.b44t.messenger.DcContext;
import com.b44t.messenger.DcEvent;
import com.google.zxing.integration.android.IntentIntegrator;
@@ -53,7 +53,7 @@ public class RelayListActivity extends BaseActionBarActivity
private ActivityResultLauncher<Intent> qrScannerLauncher;
/** Relay selected for context menu via onRelayLongClick() */
private EnteredLoginParam contextMenuRelay = null;
private TransportListEntry contextMenuRelay = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -162,7 +162,7 @@ public class RelayListActivity extends BaseActionBarActivity
String finalMainRelayAddr = mainRelayAddr;
try {
List<EnteredLoginParam> relays = rpc.listTransports(accId);
List<TransportListEntry> relays = rpc.listTransportsEx(accId);
Util.runOnMain(() -> adapter.setRelays(relays, finalMainRelayAddr));
} catch (RpcException e) {
@@ -173,12 +173,12 @@ public class RelayListActivity extends BaseActionBarActivity
}
@Override
public void onRelayClick(EnteredLoginParam relay) {
if (relay.addr != null && !relay.addr.equals(adapter.getMainRelay())) {
public void onRelayClick(TransportListEntry relay) {
if (relay.param.addr != null && !relay.param.addr.equals(adapter.getMainRelay())) {
Util.runOnAnyBackgroundThread(
() -> {
try {
rpc.setConfig(accId, DcHelper.CONFIG_CONFIGURED_ADDRESS, relay.addr);
rpc.setConfig(accId, DcHelper.CONFIG_CONFIGURED_ADDRESS, relay.param.addr);
} catch (RpcException e) {
Log.e(TAG, "RPC.setConfig() failed", e);
}
@@ -189,7 +189,7 @@ public class RelayListActivity extends BaseActionBarActivity
}
@Override
public void onRelayLongClick(View view, EnteredLoginParam relay) {
public void onRelayLongClick(View view, TransportListEntry relay) {
contextMenuRelay = relay;
registerForContextMenu(view);
openContextMenu(view);
@@ -201,8 +201,10 @@ public class RelayListActivity extends BaseActionBarActivity
super.onCreateContextMenu(menu, v, menuInfo);
getMenuInflater().inflate(R.menu.relay_item_context, menu);
boolean nonNullAddr = contextMenuRelay != null && contextMenuRelay.addr != null;
boolean isMain = nonNullAddr && contextMenuRelay.addr.equals(adapter.getMainRelay());
boolean nonNullAddr = contextMenuRelay != null && contextMenuRelay.param.addr != null;
boolean isMain = nonNullAddr && contextMenuRelay.param.addr.equals(adapter.getMainRelay());
Util.redMenuItem(menu, R.id.menu_delete_relay);
menu.findItem(R.id.menu_delete_relay).setVisible(!isMain);
}
@@ -230,28 +232,40 @@ public class RelayListActivity extends BaseActionBarActivity
return super.onContextItemSelected(item);
}
private void onRelayEdit(EnteredLoginParam relay) {
private void onRelayEdit(TransportListEntry relay) {
Intent intent = new Intent(this, EditRelayActivity.class);
intent.putExtra(EditRelayActivity.EXTRA_ADDR, relay.addr);
intent.putExtra(EditRelayActivity.EXTRA_ADDR, relay.param.addr);
startActivity(intent);
}
private void onRelayDelete(EnteredLoginParam relay) {
new AlertDialog.Builder(this)
.setTitle(R.string.remove_transport)
.setMessage(getString(R.string.confirm_remove_transport, relay.addr))
.setPositiveButton(
R.string.ok,
(dialog, which) -> {
try {
rpc.deleteTransport(accId, relay.addr);
loadRelays();
} catch (RpcException e) {
Log.e(TAG, "RPC.deleteTransport() failed", e);
}
})
.setNegativeButton(R.string.cancel, null)
.show();
private void onRelayDelete(TransportListEntry relay) {
AlertDialog dialog =
new AlertDialog.Builder(this)
.setTitle(R.string.remove_transport)
.setMessage(getString(R.string.confirm_remove_or_hide_transport_x, relay.param.addr))
.setPositiveButton(
R.string.remove_transport,
(d, which) -> {
try {
rpc.deleteTransport(accId, relay.param.addr);
loadRelays();
} catch (RpcException e) {
Log.e(TAG, "RPC.deleteTransport() failed", e);
}
})
.setNegativeButton(R.string.cancel, null)
.setNeutralButton(
R.string.hide_from_contacts,
(d, which) -> {
try {
rpc.setTransportUnpublished(accId, relay.param.addr, true);
loadRelays();
} catch (RpcException e) {
Log.e(TAG, "cannot unpublish relay: ", e);
}
})
.show();
Util.redPositiveButton(dialog);
}
@Override
@@ -1,5 +1,6 @@
package org.thoughtcrime.securesms.relay;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -8,21 +9,21 @@ import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
import chat.delta.rpc.types.EnteredLoginParam;
import chat.delta.rpc.types.TransportListEntry;
import java.util.ArrayList;
import java.util.List;
import org.thoughtcrime.securesms.R;
public class RelayListAdapter extends RecyclerView.Adapter<RelayListAdapter.RelayViewHolder> {
private List<EnteredLoginParam> relays = new ArrayList<>();
private List<TransportListEntry> relays = new ArrayList<>();
private final OnRelayClickListener listener;
private String mainRelayAddr;
public interface OnRelayClickListener {
void onRelayClick(EnteredLoginParam relay);
void onRelayClick(TransportListEntry relay);
void onRelayLongClick(View view, EnteredLoginParam relay);
void onRelayLongClick(View view, TransportListEntry relay);
}
public RelayListAdapter(OnRelayClickListener listener) {
@@ -33,7 +34,7 @@ public class RelayListAdapter extends RecyclerView.Adapter<RelayListAdapter.Rela
return mainRelayAddr;
}
public void setRelays(@Nullable List<EnteredLoginParam> relays, String mainRelayAddr) {
public void setRelays(@Nullable List<TransportListEntry> relays, String mainRelayAddr) {
this.relays = relays != null ? relays : new ArrayList<>();
this.mainRelayAddr = mainRelayAddr;
notifyDataSetChanged();
@@ -49,8 +50,8 @@ public class RelayListAdapter extends RecyclerView.Adapter<RelayListAdapter.Rela
@Override
public void onBindViewHolder(@NonNull RelayViewHolder holder, int position) {
EnteredLoginParam relay = relays.get(position);
boolean isMain = relay.addr != null && relay.addr.equals(mainRelayAddr);
TransportListEntry relay = relays.get(position);
boolean isMain = relay.param.addr != null && relay.param.addr.equals(mainRelayAddr);
holder.bind(relay, isMain, listener);
}
@@ -71,10 +72,20 @@ public class RelayListAdapter extends RecyclerView.Adapter<RelayListAdapter.Rela
mainIndicator = itemView.findViewById(R.id.main_indicator);
}
public void bind(EnteredLoginParam relay, boolean isMain, OnRelayClickListener listener) {
String[] parts = relay.addr.split("@");
public void bind(TransportListEntry relay, boolean isMain, OnRelayClickListener listener) {
Context context = itemView.getContext();
String[] parts = relay.param.addr.split("@");
titleText.setText(parts.length == 2 ? parts[1] : parts[0]);
subtitleText.setText(parts.length == 2 ? parts[0] : "");
String subtitle = parts.length == 2 ? parts[0] : "";
if (isMain) {
subtitle += " · " + context.getString(R.string.used_for_sending);
}
if (relay.isUnpublished) {
subtitle += " · " + context.getString(R.string.hidden_from_contacts);
}
subtitleText.setText(subtitle);
mainIndicator.setVisibility(isMain ? View.VISIBLE : View.INVISIBLE);
itemView.setOnClickListener(