Compare commits

...

8 Commits

Author SHA1 Message Date
copilot-swe-agent[bot] 8217d3fc22 Replace AccessibilityDelegateCompat with ViewCompat.addAccessibilityAction for link actions
The previous AccessibilityDelegateCompat approach had two fatal flaws:
1. The delegate was only set when isTouchExplorationEnabled() returned
   true AT BIND TIME — a timing bug that caused TalkBack to see no
   actions if the view was bound before TalkBack fully initialised.
2. Even when set, the delegate/custom-action-ID approach did not
   reliably produce an "Actions" submenu in the TalkBack local context
   menu on Android 14+.

Replace the entire mechanism with ViewCompat.addAccessibilityAction(),
the modern API (androidx.core 1.5.0+) that TalkBack always surfaces
under "Actions". Actions are registered unconditionally at bind time;
stale actions from recycled views are removed via
ViewCompat.removeAccessibilityAction() before re-registration.

Delete the now-unused LinkAccessibilityDelegate class.

Agent-Logs-Url: https://github.com/ArcaneChat/android/sessions/95854760-c8e5-43d8-b7b4-55879a7c5dea

Co-authored-by: adbenitez <24558636+adbenitez@users.noreply.github.com>
2026-05-01 11:22:20 +00:00
copilot-swe-agent[bot] dfa80abd13 Fix TalkBack custom actions: use AccessibilityDelegateCompat + ViewCompat
The previous code used View.AccessibilityDelegate (raw framework) and
AccessibilityNodeInfoCompat.wrap() which is deprecated and does not
reliably propagate addAction() to the real AccessibilityNodeInfo that
TalkBack reads.

Switch to AccessibilityDelegateCompat (AndroidX) set via
ViewCompat.setAccessibilityDelegate(). Its onInitializeAccessibilityNodeInfo
callback receives a properly-backed AccessibilityNodeInfoCompat so
info.addAction() reliably surfaces custom actions in the TalkBack
context menu (L-gesture) on all Android versions including API 36.

Also clear the delegate in the CALL/empty-text branch to prevent a
stale delegate remaining on a recycled view.

Agent-Logs-Url: https://github.com/ArcaneChat/android/sessions/87aa7735-a87e-473d-b80c-67e697cd4fa7

Co-authored-by: adbenitez <24558636+adbenitez@users.noreply.github.com>
2026-05-01 11:02:12 +00:00
copilot-swe-agent[bot] 177b68a42c Fix: attach accessibility delegate to ConversationItem, not bodyText
bodyText has importantForAccessibility="no" in the XML layout, so
TalkBack focuses the parent ConversationItem. The delegate must be
set on `this` and must read spans from a bodyText reference, not
from the view it's attached to.

Agent-Logs-Url: https://github.com/ArcaneChat/android/sessions/598a695d-a5cd-4948-85e9-edce5e10c67c

Co-authored-by: adbenitez <24558636+adbenitez@users.noreply.github.com>
2026-04-30 18:47:42 +00:00
copilot-swe-agent[bot] e8d3458a7b Add clarifying comments for accessibility implementation
- Explain why accessibility delegate is not set during batch selection
- Explain why map is cleared and rebuilt on each accessibility update
- Improves code maintainability and readability

Co-authored-by: adbenitez <24558636+adbenitez@users.noreply.github.com>
2026-02-11 23:14:55 +00:00
copilot-swe-agent[bot] 54332e8bac Address code review feedback
- Reuse LinkAccessibilityDelegate instance instead of creating new one each time
- Add detailed comment explaining the magic number for action IDs
- Improves performance by avoiding unnecessary object allocations

Co-authored-by: adbenitez <24558636+adbenitez@users.noreply.github.com>
2026-02-11 23:13:50 +00:00
copilot-swe-agent[bot] 6b06422fa1 Use internationalized string for accessibility link label
- Added accessibility_link_action string resource
- Updated LinkAccessibilityDelegate to accept Context and use resource
- Updated ConversationItem to pass context to delegate

Co-authored-by: adbenitez <24558636+adbenitez@users.noreply.github.com>
2026-02-11 23:12:45 +00:00
copilot-swe-agent[bot] a7bd40f851 Add accessibility support for links in chat messages
- Created LinkAccessibilityDelegate to expose links as custom actions
- Updated ConversationItem to use delegate when TalkBack is enabled
- Each link becomes a separate accessible action with clear label

Co-authored-by: adbenitez <24558636+adbenitez@users.noreply.github.com>
2026-02-11 23:11:36 +00:00
copilot-swe-agent[bot] 78659bb9e0 Initial plan 2026-02-11 23:05:37 +00:00
2 changed files with 42 additions and 0 deletions
@@ -23,6 +23,7 @@ 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;
@@ -36,6 +37,7 @@ 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;
@@ -66,6 +68,7 @@ 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;
@@ -73,6 +76,7 @@ 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;
@@ -125,6 +129,9 @@ 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;
@@ -413,6 +420,13 @@ 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();
@@ -428,6 +442,31 @@ 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
View File
@@ -1179,6 +1179,9 @@
<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>