Compare commits

..

5 Commits

Author SHA1 Message Date
copilot-swe-agent[bot] 71652290d5 Fix XML attribute ordering in context menu resource
Co-authored-by: adbenitez <24558636+adbenitez@users.noreply.github.com>
2026-02-11 19:33:59 +00:00
copilot-swe-agent[bot] 12a373b48d Refactor: Extract clearContextMenuState() helper method
Co-authored-by: adbenitez <24558636+adbenitez@users.noreply.github.com>
2026-02-11 19:32:17 +00:00
copilot-swe-agent[bot] 5df63855f9 Clean up context menu state to prevent memory leaks
Co-authored-by: adbenitez <24558636+adbenitez@users.noreply.github.com>
2026-02-11 19:31:24 +00:00
copilot-swe-agent[bot] 6d44e74dc2 Replace edit/delete buttons with context menu in relay list items
Co-authored-by: adbenitez <24558636+adbenitez@users.noreply.github.com>
2026-02-11 19:29:14 +00:00
copilot-swe-agent[bot] 29ec07a478 Initial plan 2026-02-11 19:26:06 +00:00
6 changed files with 70 additions and 81 deletions
@@ -23,7 +23,6 @@ import android.graphics.Color;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.text.Spannable;
import android.text.Spanned;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
@@ -37,7 +36,6 @@ import androidx.annotation.DimenRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.core.view.ViewCompat;
import com.b44t.messenger.DcChat;
import com.b44t.messenger.DcContact;
@@ -68,7 +66,6 @@ import org.thoughtcrime.securesms.mms.VcardSlide;
import org.thoughtcrime.securesms.reactions.ReactionsConversationView;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.Linkifier;
import org.thoughtcrime.securesms.util.LongClickCopySpan;
import org.thoughtcrime.securesms.util.LongClickMovementMethod;
import org.thoughtcrime.securesms.util.MarkdownUtil;
import org.thoughtcrime.securesms.util.MediaUtil;
@@ -76,7 +73,6 @@ import org.thoughtcrime.securesms.util.Util;
import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.views.Stub;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
@@ -129,9 +125,6 @@ public class ConversationItem extends BaseConversationItem
private Stub<VcardView> vcardViewStub;
private Stub<CallItemView> callViewStub;
private @Nullable EventListener eventListener;
// IDs of accessibility actions registered via ViewCompat.addAccessibilityAction, kept so they
// can be removed on rebind (RecyclerView reuses views for different messages).
private final List<Integer> linkActionIds = new ArrayList<>();
private int measureCalls;
@@ -420,13 +413,6 @@ public class ConversationItem extends BaseConversationItem
bodyText.setClickable(false);
bodyText.setFocusable(false);
// Remove any link actions registered for the previous message binding.
// RecyclerView reuses views, so stale actions from a previous item must be cleared.
for (int id : linkActionIds) {
ViewCompat.removeAccessibilityAction(this, id);
}
linkActionIds.clear();
String subject = messageRecord.getSubject();
String text = messageRecord.getText();
@@ -442,31 +428,6 @@ public class ConversationItem extends BaseConversationItem
}
bodyText.setText(spannable);
bodyText.setVisibility(View.VISIBLE);
// Register a TalkBack "Actions" entry for each link in the message.
// ViewCompat.addAccessibilityAction is used instead of an AccessibilityDelegateCompat because
// it is the modern, reliable API (available since androidx.core 1.5.0) that TalkBack always
// surfaces under the "Actions" submenu of the local context menu (L-gesture).
// The actions are registered unconditionally — no isTouchExplorationEnabled() guard — because
// the registration is cheap and the guard was a source of timing bugs at bind time.
// Links are only present when not in batch-selection mode (Linkifier is not called in that
// branch above), so no actions are added in batch-selection mode.
if (spannable instanceof Spanned) {
Spanned spanned = (Spanned) spannable;
final TextView tv = bodyText;
for (LongClickCopySpan span : spanned.getSpans(0, spanned.length(), LongClickCopySpan.class)) {
int start = spanned.getSpanStart(span);
int end = spanned.getSpanEnd(span);
if (start >= 0 && end > start && end <= spanned.length()) {
String linkText = spanned.subSequence(start, end).toString();
String label = context.getString(R.string.accessibility_link_action, linkText);
linkActionIds.add(ViewCompat.addAccessibilityAction(this, label, (v, args) -> {
span.onClick(tv);
return true;
}));
}
}
}
}
int downloadState = messageRecord.getDownloadState();
@@ -3,7 +3,9 @@ package org.thoughtcrime.securesms.relay;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.ContextMenu;
import android.view.MenuItem;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBar;
@@ -43,6 +45,10 @@ public class RelayListActivity extends BaseActionBarActivity
private RelayListAdapter adapter;
private Rpc rpc;
private int accId;
/** Relay selected for context menu */
private EnteredLoginParam contextMenuRelay = null;
private boolean contextMenuRelayIsMain = false;
/** QR provided via Intent extras needs to be saved to pass it to QrCodeHandler when authorization finishes */
private String qrData = null;
@@ -166,6 +172,56 @@ public class RelayListActivity extends BaseActionBarActivity
.show();
}
@Override
public void onRelayLongClick(View view, EnteredLoginParam relay, boolean isMain) {
contextMenuRelay = relay;
contextMenuRelayIsMain = isMain;
registerForContextMenu(view);
openContextMenu(view);
unregisterForContextMenu(view);
}
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
getMenuInflater().inflate(R.menu.relay_item_context, menu);
// Hide delete option if this is the main relay
if (contextMenuRelayIsMain) {
menu.findItem(R.id.menu_delete).setVisible(false);
}
}
@Override
public void onContextMenuClosed(android.view.Menu menu) {
super.onContextMenuClosed(menu);
clearContextMenuState();
}
@Override
public boolean onContextItemSelected(@NonNull MenuItem item) {
int itemId = item.getItemId();
if (itemId == R.id.menu_edit) {
if (contextMenuRelay != null) {
onRelayEdit(contextMenuRelay);
clearContextMenuState();
}
return true;
} else if (itemId == R.id.menu_delete) {
if (contextMenuRelay != null) {
onRelayDelete(contextMenuRelay);
clearContextMenuState();
}
return true;
}
return super.onContextItemSelected(item);
}
private void clearContextMenuState() {
contextMenuRelay = null;
contextMenuRelayIsMain = false;
}
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
if (item.getItemId() == android.R.id.home) {
@@ -27,6 +27,7 @@ public class RelayListAdapter extends RecyclerView.Adapter<RelayListAdapter.Rela
void onRelayClick(EnteredLoginParam relay);
void onRelayEdit(EnteredLoginParam relay);
void onRelayDelete(EnteredLoginParam relay);
void onRelayLongClick(View view, EnteredLoginParam relay, boolean isMain);
}
public RelayListAdapter(OnRelayClickListener listener) {
@@ -67,16 +68,12 @@ public class RelayListAdapter extends RecyclerView.Adapter<RelayListAdapter.Rela
private final TextView titleText;
private final TextView subtitleText;
private final ImageView mainIndicator;
private final ImageView editButton;
private final ImageView deleteButton;
public RelayViewHolder(@NonNull View itemView) {
super(itemView);
titleText = itemView.findViewById(R.id.title);
subtitleText = itemView.findViewById(R.id.subtitle);
mainIndicator = itemView.findViewById(R.id.main_indicator);
editButton = itemView.findViewById(R.id.edit_button);
deleteButton = itemView.findViewById(R.id.delete_button);
}
public void bind(EnteredLoginParam relay, boolean isMain, OnRelayClickListener listener) {
@@ -84,7 +81,6 @@ public class RelayListAdapter extends RecyclerView.Adapter<RelayListAdapter.Rela
titleText.setText(parts.length == 2? parts[1] : parts[0]);
subtitleText.setText(parts.length == 2? parts[0] : "");
mainIndicator.setVisibility(isMain ? View.VISIBLE : View.INVISIBLE);
deleteButton.setVisibility(isMain ? View.GONE : View.VISIBLE);
itemView.setOnClickListener(v -> {
if (listener != null) {
@@ -92,16 +88,11 @@ public class RelayListAdapter extends RecyclerView.Adapter<RelayListAdapter.Rela
}
});
editButton.setOnClickListener(v -> {
itemView.setOnLongClickListener(v -> {
if (listener != null) {
listener.onRelayEdit(relay);
}
});
deleteButton.setOnClickListener(v -> {
if (listener != null) {
listener.onRelayDelete(relay);
listener.onRelayLongClick(v, relay, isMain);
}
return true;
});
}
}
-26
View File
@@ -59,30 +59,4 @@
</LinearLayout>
<ImageView
android:id="@+id/delete_button"
android:layout_width="48dp"
android:layout_height="72dp"
android:paddingStart="12dp"
android:paddingEnd="12dp"
android:clickable="true"
android:focusable="true"
android:contentDescription="@string/remove_transport"
android:background="@drawable/touch_highlight_background"
android:src="@drawable/ic_delete_white_24dp"
app:tint="?attr/conversation_list_item_date_color" />
<ImageView
android:id="@+id/edit_button"
android:layout_width="48dp"
android:layout_height="72dp"
android:paddingStart="12dp"
android:paddingEnd="12dp"
android:clickable="true"
android:focusable="true"
android:contentDescription="@string/edit_transport"
android:background="@drawable/touch_highlight_background"
android:src="@drawable/ic_create_white_24dp"
app:tint="?attr/conversation_list_item_date_color" />
</LinearLayout>
+10
View File
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/menu_edit"
android:title="@string/edit_transport"/>
<item android:id="@+id/menu_delete"
android:title="@string/remove_transport"/>
</menu>
-3
View File
@@ -1179,9 +1179,6 @@
<string name="perm_enable_bg_reminder_title">Tap here to receive messages while Delta Chat is in the background.</string>
<string name="perm_enable_bg_already_done">You already allowed Delta Chat to receive messages in the background.\n\nIf messages still do not arrive in background, please also check your system settings.</string>
<!-- Accessibility -->
<string name="accessibility_link_action">Open link: %1$s</string>
<!-- device messages for updates -->
<string name="update_2_0">What\'s new?\n\n💯 End-to-end encryption is reliable and forever now. Padlocks 🔒 are gone!\n\n✉️ Classic email without end-to-end encryption is marked with a letter symbol\n\n😻 New enhanced profile screen for all your contacts\n\n🔲 New button for quick access to apps used in a chat\n\n❤️ Please donate to help us remain independent and continue to bring improvements: %1$s</string>
</resources>