Skip to content

ADFA-4640: rework the Usage server-log console for real terminal-style scrolling#176

Merged
luisguzman-adfa merged 8 commits into
mainfrom
fix/ADFA-4640-server-log-console
Jul 10, 2026
Merged

ADFA-4640: rework the Usage server-log console for real terminal-style scrolling#176
luisguzman-adfa merged 8 commits into
mainfrom
fix/ADFA-4640-server-log-console

Conversation

@luisguzman-adfa

Copy link
Copy Markdown
Collaborator

Replaces the Usage-tab server-log TextView (which force-scrolled to the bottom on every line, had a decorative-only scrollbar, and no follow/tail state) with a RecyclerView-backed ServerLogView.

Behavior (the three requested)

  • Sticky read position: new lines don't move the viewport unless you're following the tail — you can stop and read while the log grows.
  • Follow / tail (like less +F): auto-sticks to the newest line only when the viewport is at the bottom; scrolling up detaches; a "↓ Newest" button (shown only when detached) re-attaches and jumps to the end.
  • Draggable fast-scroll thumb: RecyclerView fast-scroll to move start↔end across many lines without thumb-by-thumb dragging.
  • Plus a bounded ring buffer (8000 lines) so long streams stay smooth.

Files

  • New ServerLogView (compound view) + view_server_log.xml, item_log_line.xml, scrollbar_track.xml.
  • fragment_usage.xml: connection_log swapped TextViewServerLogView (same id).
  • UsageFragment: drives it via append()/setContent()/clear()/getContent(); removed scrollToBottom() and the TextView movement/selectable/touch hacks; dropped now-unused imports.
  • build.gradle: explicit androidx.recyclerview dependency.
  • New string log_jump_latest localized across all 33 locales.

Verify (device)

  • Open the server log during a long stream (e.g. a module install). Scroll up: it stays put while the log keeps growing; the "↓ Newest" button appears.
  • Tap "↓ Newest" (or scroll back to the bottom): it re-attaches and follows the tail.
  • Drag the scrollbar thumb to jump start↔end quickly.
  • Clear / Copy log still work.

Relates to ADFA-1028.

…e scrolling

Replace the plain TextView (force-scrolled to the bottom on every line,
decorative-only scrollbar, no follow/tail state) with a RecyclerView-backed
ServerLogView that:
- keeps your read position while the log grows (sticky),
- follows the newest line (tail, like less +F) only when at the bottom, with a
  'jump to newest' button to re-attach after scrolling up,
- exposes a draggable fast-scroll thumb to move start<->end quickly,
- caps the buffer (ring buffer, 8000 lines) so long streams stay smooth.

UsageFragment drives it via append()/setContent()/clear()/getContent(); the old
scrollToBottom() and the TextView movement/selectable/touch hacks are removed.
New string log_jump_latest localized across all 33 locales. Adds explicit
androidx.recyclerview dependency.
…tateListDrawable

RecyclerView's fast scroller casts the fastScroll*ThumbDrawable to StateListDrawable;
the previous <shape> compiled to a GradientDrawable, so inflating the log RecyclerView
threw ClassCastException (GradientDrawable -> StateListDrawable) and crashed the Usage
tab. Use dedicated <selector> (StateListDrawable) drawables with an intrinsic size for
the fast-scroll thumb and track; drop the now-unused scrollbar_track shape.
…persistence)

The log lived only in the transient UsageFragment view, and the install/Ansible
receiver was tied to DeployFragment's lifecycle, so: (a) while Usage was offscreen
addToLog was a no-op and lines were dropped, and (b) hiding the console re-read the
watchdog blackbox and setContent-wiped the live install log. Ansible output — the
most important record of Module Management — was being lost.

Introduce LogRepository: a process-scoped, observable, bounded single source of
truth. Every producer funnels into it (timestamped centrally):
- MainActivity.addToLog and UsageFragment.addToLog -> LogRepository.append
- InstallService.log() -> LogRepository.append directly (same process), so Ansible/
  install output is captured regardless of which fragment/tab is on screen
- removed DeployFragment's install-log BroadcastReceiver (its only job was to forward
  to addToLog; now redundant)

UsageFragment observes LogRepository (addListener + render snapshot in onResume,
removeListener in onPause) so the console updates live and survives tab switches /
hide-show. handleLogToggle no longer setContent-wipes from the blackbox file (it now
renders the LogRepository snapshot and reads the file only for the rapid-growth
warning + size). Reset clears the repository (the source of truth).
…ollbar; fix follow

The AndroidX fast-scroller plus the native scrollbars=vertical rendered TWO bars,
the fast-scroll thumb looked split, was hard to grab, and had no tap-to-jump. And the
follow state detached on programmatic scrolls, so the tail 'stuck' mid-list.

- Drop android:scrollbars and app:fastScroll* from the RecyclerView (removes the double
  bar and the split-thumb artifact); delete the now-unused fast_scroll_* drawables.
- Add a custom scrollbar (track + thumb) in ServerLogView: tap anywhere on the track to
  jump there and drag to move across thousands of lines (maps the touch fraction to an
  item index via scrollToPositionWithOffset). The thumb is sized/positioned from the
  RecyclerView scroll metrics and only shown when the content overflows.
- Follow fix: only a user-driven scroll (state != IDLE) detaches the tail; programmatic
  appends/jumps are instant and keep it pinned. 'At bottom' now uses canScrollVertically.
… across restarts

LogRepository was in-memory only, so the log survived tab switches and hide/show
but not a process kill. Mirror the buffer to a dedicated file (server_log.txt) with a
debounced background write, and reload it on process start (IIABApplication.init), so
the Module Management / Ansible log is durable.

Kept separate from the watchdog blackbox (watchdog_heartbeat_log.txt): LogRepository
does not broadcast, so there is no feedback loop with IIABWatchdog. The file is bounded
by the same MAX_LINES cap as the buffer (each flush rewrites the capped snapshot).
… reflow)

The default RecyclerView ItemAnimator animated every notifyItemInserted, so a burst
of streamed lines plus scrollToPosition on jump-to-newest produced a chaotic
'sea of letters' reflow. A streaming log should not animate: setItemAnimator(null)
(and setHasFixedSize(true), since the console is a fixed 250dp box) makes appends and
jumps render cleanly and cheaper.
…t the watchdog blackbox

updateLogSizeUI now reports LogRepository's persisted file size via a reusable
LogManager.formatSize(context, bytes) helper, so the 'Size' figure matches what the
console actually shows/persists.
@luisguzman-adfa luisguzman-adfa merged commit ddd4e5b into main Jul 10, 2026
1 check passed
@luisguzman-adfa luisguzman-adfa deleted the fix/ADFA-4640-server-log-console branch July 10, 2026 03:14
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.

1 participant