Files
XQKqueue/.project-docs/50-evidence/topics/20260811-native-display-f2456ea9__range-display-fit.md

3.1 KiB

Native range-display truncation evidence

Reported symptom

On the target 1920x1080 television, a batch that called tickets 00010 and 00011 rendered the main current-call field as only 00010 至; the latest-ticket metric at the right still showed 00011.

Boundary verification

The production public snapshot was read on 2026-08-12 from GET https://queue.nianxx.cn/api/display/YYHHC/snapshot. It contained:

  • latest_ticket_number: "00011"
  • current_batch.ticket_count: 2
  • current_batch.tickets[0].display_number: "00010"
  • current_batch.tickets[1].display_number: "00011"

DisplayFormatting.currentBatchNumbers therefore produces the complete string 00010 至 00011. This rules out the server response, JSON parser, and range formatter as the cause.

Root cause

The original native current-number TextView started at 74sp, allowed one line, and called NativeDisplayUi.autoSize. That helper only activated Android platform auto-size on API 26 and later, although the APK supports API 23. On API 23-25, long range text stayed at 74sp and could be silently clipped by the half-width current-call panel. Platform behavior and display density also made the guarantee device-dependent on newer releases.

An API-25 Robolectric regression using a full ProjectDisplayView, 1920x1080 exact layout, and the real two-ticket snapshot was run before the fix. Its red signal confirmed the legacy view retained the initial 74sp rather than applying a fitting policy.

Fix and invariant

SingleLineFitTextView now owns current-call fitting on API 23 and later. On text, size, or padding changes it:

  • computes actual content width and height after compound padding;
  • measures the real text and font metrics with a copy of the active TextPaint;
  • binary-searches for the largest whole-sp size that fits both dimensions;
  • enforces a single line and never ellipsizes;
  • falls below the normal 30sp readability floor only when necessary to preserve the complete ticket range.

Short single numbers still receive the largest fitting size, up to 74sp.

Verification

  • gradlew.bat clean testDebugUnitTest lintDebug assembleDebug: PASS.
  • JVM tests: 56 across 12 suites, 0 failures, 0 errors, 0 skipped.
  • Lint: 0 errors, 24 warnings.
  • ProjectDisplayViewTest locks the full 00010 至 00011 string, one-line behavior, and no ellipsis on API 25.
  • A narrow ASCII control asserts the fitted TextPaint width and font height are within actual content bounds; deterministic sizing tests call the production two-stage decision and lock largest-fitting selection, full-size selection, below-floor fallback, and the 1sp terminal boundary.
  • APK: versionCode 9, versionName 1.5.2; Android Debug v1/v2 signatures verified.
  • Build and deliverable SHA-256: 0D56C10B8CE706893F3EA8E59B21329847517F56F7347ABEA127AB30C6C71D77.

Remaining acceptance

Robolectric's host font metrics are not the target television's font metrics. The production code uses the device's real TextPaint, but final acceptance still requires installing 1.5.2 on the target television and visually confirming both endpoints for 00010 至 00011 and at least one wider representative range.