Skip to content

fix: make announce filter dialog scrollable on short displays#483

Merged
torlando-tech merged 3 commits intomainfrom
fix/announce-filter-dialog-scrollable
Feb 18, 2026
Merged

fix: make announce filter dialog scrollable on short displays#483
torlando-tech merged 3 commits intomainfrom
fix/announce-filter-dialog-scrollable

Conversation

@torlando-tech
Copy link
Copy Markdown
Owner

Summary

  • Adds verticalScroll to the NodeTypeFilterDialog column so all 9 checkbox rows (3 node types + audio + 5 interface types) are accessible on shorter phone screens

Closes #480

Test plan

  • Open the announce filter dialog on a short-display device (or emulator with small screen)
  • Verify all checkboxes (including the bottom interface type filters) are reachable by scrolling
  • Verify the dialog still looks correct on larger displays (no unnecessary scrollbar)

🤖 Generated with Claude Code

torlando-tech and others added 3 commits February 15, 2026 19:42
CompanionDeviceService (API 31) superclass causes class verification
crash on Samsung S8 (API 26-28) even though runtime code is guarded.
Use resource-qualified android:enabled to gate the service at the
manifest level so it's never loaded on older devices.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…oggle

fix: Oboe SIGSEGV on OpenSL ES + speaker toggle for native stream
The NodeTypeFilterDialog had 9 checkbox rows plus headers that could
overflow on shorter phone screens. Wraps the dialog Column in
verticalScroll so all filter options are accessible.

Closes #480

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@sentry
Copy link
Copy Markdown
Contributor

sentry bot commented Feb 17, 2026

Codecov Report

❌ Patch coverage is 84.31373% with 8 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
python/lxst_modules/call_manager.py 84.31% 8 Missing ⚠️

📢 Thoughts on this report? Let us know!

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Feb 17, 2026

Greptile Summary

This PR combines three separate changes:

1. UI Fix (Issue #480): Makes the announce filter dialog scrollable on short-display devices by adding verticalScroll(rememberScrollState()) to the NodeTypeFilterDialog Column in AnnounceStreamScreen.kt:424. This ensures all 9 checkbox rows are accessible on smaller screens.

2. Companion Service Fix: Disables RNodeCompanionService on pre-API 31 devices by adding conditional enabling via android:enabled="@bool/enable_companion_service" in AndroidManifest.xml. The service is disabled by default and only enabled on API 31+ through resource qualifiers.

3. Audio Call Optimization: Implements TX batching in call_manager.py to reduce GIL contention during voice calls. Audio frames are accumulated (batch size: 3 frames, timeout: 150ms) before sending, reducing RNS.Packet.send() calls from ~16.7/sec to ~5.6/sec. Also refactors __link_closed() to prevent deadlocks by moving Kotlin callbacks outside the lock.

Issues Found:

  • Missing _tx_batch_start reset in hangup() method could cause issues if timestamp from previous call is reused

Confidence Score: 4/5

  • This PR is generally safe to merge with one minor bug fix needed in the Python batching code
  • The UI fix is simple and safe (confidence: 5/5). The companion service changes follow Android best practices (confidence: 5/5). The Python audio batching implementation is well-tested and documented, but has a minor issue where _tx_batch_start is not reset in hangup(), which could cause incorrect timeout calculations if a new call starts before the next frame (confidence: 4/5)
  • python/lxst_modules/call_manager.py needs the missing _tx_batch_start reset added to the hangup() method

Important Files Changed

Filename Overview
app/src/main/java/com/lxmf/messenger/ui/screens/AnnounceStreamScreen.kt Added verticalScroll modifier to make filter dialog scrollable on short displays - fixes issue #480
app/src/main/AndroidManifest.xml Added conditional enabling of RNodeCompanionService via @bool/enable_companion_service resource
python/lxst_modules/call_manager.py Added TX batching for audio frames to reduce GIL contention; refactored __link_closed to avoid deadlocks

Flowchart

flowchart TD
    A[Kotlin: Audio frame ready] --> B[receive_audio_packet called]
    B --> C{Is this first frame<br/>in batch?}
    C -->|Yes| D[Set _tx_batch_start = now]
    C -->|No| E[Add frame to _tx_batch]
    D --> E
    E --> F{Batch size >= 3<br/>OR timeout >= 150ms?}
    F -->|No| G[Wait for more frames]
    F -->|Yes| H[_flush_tx_batch]
    H --> I[Pack frames as msgpack]
    I --> J[RNS.Packet.send]
    J --> K[Reset _tx_batch = empty]
    K --> G
    L[hangup called] --> M[Flush remaining batch]
    M --> N[Reset batch state]
    N --> O[Teardown link]
Loading

Last reviewed commit: 238c7f9

Copy link
Copy Markdown
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

7 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Comment on lines +442 to +445
# Reset batch state
self._tx_batch = []
self._tx_batch_count = 0
self._tx_send_time_sum = 0.0
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_tx_batch_start should also be reset here (currently only _tx_batch, _tx_batch_count, and _tx_send_time_sum are reset)

Suggested change
# Reset batch state
self._tx_batch = []
self._tx_batch_count = 0
self._tx_send_time_sum = 0.0
# Reset batch state
self._tx_batch = []
self._tx_batch_start = 0.0
self._tx_batch_count = 0
self._tx_send_time_sum = 0.0
Prompt To Fix With AI
This is a comment left during a code review.
Path: python/lxst_modules/call_manager.py
Line: 442:445

Comment:
`_tx_batch_start` should also be reset here (currently only `_tx_batch`, `_tx_batch_count`, and `_tx_send_time_sum` are reset)

```suggestion
        # Reset batch state
        self._tx_batch = []
        self._tx_batch_start = 0.0
        self._tx_batch_count = 0
        self._tx_send_time_sum = 0.0
```

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +436 to +438
if link is not None and self._tx_batch:
try:
self._flush_tx_batch(link)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: The hangup() and receive_audio_packet() methods access the shared _tx_batch list concurrently without a lock, creating a race condition that can cause audio frames to be lost.
Severity: MEDIUM

Suggested Fix

Wrap the code block in hangup() that accesses and flushes _tx_batch (lines 436-440) within a with self._call_handler_lock: block to ensure thread-safe access to the shared batch state.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.

Location: python/lxst_modules/call_manager.py#L436-L438

Potential issue: A race condition exists because the `hangup()` and
`receive_audio_packet()` methods can be executed on different threads and both access
the shared `_tx_batch` list without acquiring the `_call_handler_lock`. If an audio
packet arrives while a call is being hung up, this concurrent access to the shared
mutable state can lead to lost audio frames or potential list corruption errors. The
code acknowledges the risk of a race condition with `hangup()` but only protects the
`active_call` reference, not the shared `_tx_batch` state.

Did we get this right? 👍 / 👎 to inform future reviews.

Comment on lines +566 to +576
should_notify = True

# Notify Kotlin OUTSIDE lock (same pattern as hangup())
if should_notify:
self._send_signal_to_kotlin(STATUS_AVAILABLE)

if self._kotlin_call_bridge is not None:
try:
self._kotlin_call_bridge.onCallEnded(identity)
except Exception as e:
RNS.log(f"Error notifying Kotlin of link close: {e}", RNS.LOG_ERROR)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: The __link_closed() method does not clear the _tx_batch, causing stale audio frames from a previous call to be sent to the recipient of a subsequent call.
Severity: CRITICAL

Suggested Fix

In the __link_closed() method, reset the transmit batch state by clearing _tx_batch and resetting _tx_batch_start and _tx_send_time_sum, similar to the logic already present in the hangup() method.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.

Location: python/lxst_modules/call_manager.py#L559-L576

Potential issue: When a call ends because the remote party hangs up or due to a network
failure, the `__link_closed()` method is invoked. This method fails to clear the audio
transmit buffer (`_tx_batch`) and its associated timestamp (`_tx_batch_start`). If a new
call is initiated shortly after, the first audio packet of the new call will be appended
to the stale frames from the previous call. The old timestamp will likely cause an
immediate flush, sending audio from the previous call to the recipient of the new call,
resulting in a data leak between calls.

Did we get this right? 👍 / 👎 to inform future reviews.

@torlando-tech torlando-tech merged commit 1afb041 into main Feb 18, 2026
14 checks passed
@torlando-tech torlando-tech deleted the fix/announce-filter-dialog-scrollable branch February 18, 2026 02:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Announces filter have a fifth box with no description

1 participant