Skip to content

feat: debug page — sharper snapshots and a header that works on phones (#103) - #104

Merged
compot-dev merged 4 commits into
v2from
feat/snapshot-quality
Oct 13, 2026
Merged

compot-dev merged 4 commits into
v2from
feat/snapshot-quality

Conversation

@compot-dev

@compot-dev compot-dev commented Oct 13, 2026 •

Copy link
Copy Markdown
Collaborator

Closes #103.

Snapshots

  • The snapshot is as wide as the screen (640…1920), 16:9: 1280×720 on the AG-216 instead of 768×432. Each cell is copied with PixelCopy at the size it has in the picture (426×240 in 3×3 instead of 256×144); the single view takes the TextureView at full picture size.
  • JPEG quality 85 (was 60), bitmap filtering when the frames are drawn. The labelled variant (/snapshot.jpg) scales its text with the cell.
  • Measured on Aceline-1 (8 cameras, 2 playing): about 80 KiB, copy 90–280 ms, JPEG 105–160 ms in steady state (seconds right after the start while the decoders come up).

Page

  • One header line: state dot, name, version · git, device time, uptime; JSON / Log buttons and a 3 / 5 / 10 s switch. Wraps cleanly.
  • Cell text scales with the cell (container query units) and is cut with an ellipsis.
  • Narrow screens (< 720 px): the snapshot takes the full width with the slot numbers only, the cells are listed below it.
  • Checked with headless Chromium at 1440×900, 1024×768 and 390×844 against the box.

READMEs (EN/RU/ZH) updated.

@compot-dev

Copy link
Copy Markdown
Collaborator Author

/review

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Labelled snapshots can overlap cell edges ✓ Resolved
Description
drawLabel paints the full label without measuring it against the cell width or adding an ellipsis.
Long status or camera text in the labelled snapshot extends into adjacent cells instead of being
truncated.
Code

android/app/src/main/java/com/openipc/decoder/Snapshots.java[R155-157]

+            canvas.drawRect(x, y + ch - bandH, x + cw, y + ch, band);
         text.setColor(Color.WHITE);
         canvas.drawText(label, x + 4, y + ch - 6, text);
Evidence
The labelled snapshot draws label directly with canvas.drawText and performs no width check or
truncation, violating the requirement that overflowing text be ellipsized.

Scaled and non-overlapping snapshot labels
android/app/src/main/java/com/openipc/decoder/Snapshots.java[152-162]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The labelled snapshot draws complete labels without constraining them to the cell bounds, so long text can overlap adjacent cells.
## Fix Focus Areas
- android/app/src/main/java/com/openipc/decoder/Snapshots.java[151-162]
## Recommended Fix
Measure each label before drawing, reserve space for an ellipsis, and draw a truncated version that fits within the cell width while preserving the scaled text size.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Recovered pages stay marked offline ✓ Resolved
Description
tick() changes the state dot to bad when a stats request fails but a successful render(s)
never restores the ok class. After a temporary /stats.json failure, the page remains red even
when polling recovers, so the indicator no longer represents the current connection state.
Code

android/app/src/main/java/com/openipc/decoder/DebugServer.java[R521-524]

+            + "function tick(){fetch('/stats.json',{cache:'no-store'}).then(function(r){return"
+            + " r.json()}).then(function(s){render(s)})\n"
+            + ".catch(function(e){$('err').textContent='offline';$('dot').className='dot bad'}"
+            + ").finally(function(){later(tick,1500)})}\n"
Evidence
The failure path assigns the dot the bad class, while the success path only calls render(s);
render sets warn or ok based on ui_thread and therefore does not restore the online state
after an offline failure unless the UI-thread condition happens to be processed as normal.

Responsive debug-page header and controls
android/app/src/main/java/com/openipc/decoder/DebugServer.java[521-524]
android/app/src/main/java/com/openipc/decoder/DebugServer.java[503-508]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The debug page marks the state dot red after a failed stats request but does not reset it after a later successful request, leaving recovered pages visibly offline.
## Fix Focus Areas
- android/app/src/main/java/com/openipc/decoder/DebugServer.java[503-508]
- android/app/src/main/java/com/openipc/decoder/DebugServer.java[521-524]
## Recommended Fix
Set the dot back to the normal green class in the successful polling path after `render(s)`, while preserving the orange class for a stuck UI thread and the red class for the current offline state.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Three-by-three snapshots lose width ✓ Resolved
Description
Snapshots.render truncates cell dimensions using integer division by the grid side, then sizes the
output bitmap from those truncated dimensions. When a 3×3 layout uses a screen width not divisible
by three, a 1280×720 target becomes 1278×720 (or 640×360 becomes 639×360), so the snapshot no longer
matches the screen width or exact 16:9 dimensions.
Code

android/app/src/main/java/com/openipc/decoder/Snapshots.java[R89-90]

+        int cw = width / side;
+        int ch = height(width) / side;
Evidence
snapshotCells() supplies the clamped display width, and render() divides the target dimensions
by the grid side before multiplying the truncated cell dimensions back to size the output bitmap.
When cameras beyond the first four select a 3×3 grid, a 1280-pixel target width yields 426-pixel
cells and a 1278-pixel-wide output.

Match snapshot dimensions to screen width
android/app/src/main/java/com/openipc/decoder/Snapshots.java[89-90]
android/app/src/main/java/com/openipc/decoder/Decoder.java[629-633]
android/app/src/main/java/com/openipc/decoder/Decoder.java[979-982]
android/app/src/main/java/com/openipc/decoder/Decoder.java[1002-1027]
android/app/src/main/java/com/openipc/decoder/Snapshots.java[87-102]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Three-by-three layouts truncate cell dimensions through integer division, so widths not divisible by three produce JPEGs narrower than the bounded screen width and break the exact 16:9 dimensions.
## Fix Focus Areas
- android/app/src/main/java/com/openipc/decoder/Snapshots.java[89-102]
## Recommended Fix
Allocate the output bitmap at the full target width and height, then distribute remainder pixels across cell rectangles and their PixelCopy dimensions so the grid fills the exact bounded screen dimensions.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

4. Stopped decoder shows a green status ✓ Resolved
Description
render() assigns the green status class whenever ui_thread is absent, including the server’s `no
activity` response. When the decoder activity is destroyed, the server keeps serving that fallback
response, so the header indicates a healthy decoder even though no activity is running.
Code

android/app/src/main/java/com/openipc/decoder/DebugServer.java[R507-508]

+            + "var ui=s.ui_thread&&s.ui_thread!=='ok';$('err').textContent=ui?'UI '+"
+            + "s.ui_thread:'';$('dot').className='dot '+(ui?'warn':'ok');\n"
Evidence
Activity destruction clears the server source; the resulting fallback stats omit ui_thread, which
the new indicator logic interprets as OK.

android/app/src/main/java/com/openipc/decoder/Decoder.java[1398-1402]
android/app/src/main/java/com/openipc/decoder/DebugServer.java[210-219]
android/app/src/main/java/com/openipc/decoder/DebugServer.java[503-508]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The page treats a missing `ui_thread` field as healthy, but the server omits that field when there is no decoder activity.
## Fix Focus Areas
- android/app/src/main/java/com/openipc/decoder/DebugServer.java[507-508]
- android/app/src/main/java/com/openipc/decoder/DebugServer.java[210-219]
## Recommended Fix
Make the header distinguish the `no activity` response from an explicit `ui_thread: "ok"` response. Show a non-green state and an appropriate message while retaining the existing warning for an unresponsive UI thread and red state for failed requests.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Timed-out captures still burden the screen ✓ Resolved
Description
debugSnapshotJpeg() returns after a one-second wait without cancelling its posted UI task, which
now calls getBitmap() at up to 1920×1080. When the UI task runs slowly or starts after the request
times out, the client gets a 503 while the main thread still performs the full capture; a bitmap
produced after that timeout also bypasses render()'s recycle path.
Code

android/app/src/main/java/com/openipc/decoder/Decoder.java[998]

+                c.frame = mSurface.getBitmap(width, Snapshots.height(width));
Evidence
The request posts snapshotCells() to the main looper but does not cancel that runnable on timeout.
The changed single-view call requests a much larger bitmap; only bitmaps passed to
Snapshots.render() are recycled, and the HTTP handler turns the timed-out null result into a 503.

android/app/src/main/java/com/openipc/decoder/Decoder.java[941-972]
android/app/src/main/java/com/openipc/decoder/Decoder.java[979-999]
android/app/src/main/java/com/openipc/decoder/Snapshots.java[40-43]
android/app/src/main/java/com/openipc/decoder/Snapshots.java[119-126]
android/app/src/main/java/com/openipc/decoder/DebugServer.java[175-184]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A timed-out JPEG request can still perform the newly enlarged single-view capture on the main thread and leave its bitmap for garbage collection.
## Fix Focus Areas
- android/app/src/main/java/com/openipc/decoder/Decoder.java[954-972]
- android/app/src/main/java/com/openipc/decoder/Decoder.java[990-999]
## Recommended Fix
Make request expiry visible to the posted task so it skips captures that have not started, and explicitly recycle any bitmap produced for a request that has already expired. Keep the expensive capture work from repeatedly blocking the UI thread.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context sources
Review mode: Auto: ⚖️ Balanced: Downgraded extended -> standard: change is below the extended eligibility bar (hunks 13/18, lines 314/200; both must reach the floor). Router rationale: Multiple independent UI, threading, snapshot, and rendering paths create dense defect risk.

Grey Divider

Tip of the day
💡 Did you know, you can turn these tips off under Display > Comment parts

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Previous reviews

Review updated until commit e150ebd ⚖️ Balanced

Results up to commit 847e37a


🐞 Bugs (2) 📘 Rule violations (0) 📎 Requirement gaps (3) 📜 Skill insights (0)


Action required
1. Labelled snapshots can overlap cell edges 📎 Requirement gap ≡ Correctness ⭐ New
Description
drawLabel paints the full label without measuring it against the cell width or adding an ellipsis.
Long status or camera text in the labelled snapshot extends into adjacent cells instead of being
truncated.
Code

android/app/src/main/java/com/openipc/decoder/Snapshots.java[R155-157]

+            canvas.drawRect(x, y + ch - bandH, x + cw, y + ch, band);
            text.setColor(Color.WHITE);
            canvas.drawText(label, x + 4, y + ch - 6, text);
Evidence
The labelled snapshot draws label directly with canvas.drawText and performs no width check or
truncation, violating the requirement that overflowing text be ellipsized.

Scaled and non-overlapping snapshot labels
android/app/src/main/java/com/openipc/decoder/Snapshots.java[152-162]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The labelled snapshot draws complete labels without constraining them to the cell bounds, so long text can overlap adjacent cells.

## Fix Focus Areas
- android/app/src/main/java/com/openipc/decoder/Snapshots.java[151-162]

## Recommended Fix
Measure each label before drawing, reserve space for an ellipsis, and draw a truncated version that fits within the cell width while preserving the scaled text size.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Recovered pages stay marked offline 📎 Requirement gap ≡ Correctness ⭐ New
Description
tick() changes the state dot to bad when a stats request fails but a successful render(s)
never restores the ok class. After a temporary /stats.json failure, the page remains red even
when polling recovers, so the indicator no longer represents the current connection state.
Code

android/app/src/main/java/com/openipc/decoder/DebugServer.java[R521-524]

+            + "function tick(){fetch('/stats.json',{cache:'no-store'}).then(function(r){return"
+            + " r.json()}).then(function(s){render(s)})\n"
+            + ".catch(function(e){$('err').textContent='offline';$('dot').className='dot bad'}"
+            + ").finally(function(){later(tick,1500)})}\n"
Evidence
The failure path assigns the dot the bad class, while the success path only calls render(s);
render sets warn or ok based on ui_thread and therefore does not restore the online state
after an offline failure unless the UI-thread condition happens to be processed as normal.

Responsive debug-page header and controls
android/app/src/main/java/com/openipc/decoder/DebugServer.java[521-524]
android/app/src/main/java/com/openipc/decoder/DebugServer.java[503-508]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The debug page marks the state dot red after a failed stats request but does not reset it after a later successful request, leaving recovered pages visibly offline.

## Fix Focus Areas
- android/app/src/main/java/com/openipc/decoder/DebugServer.java[503-508]
- android/app/src/main/java/com/openipc/decoder/DebugServer.java[521-524]

## Recommended Fix
Set the dot back to the normal green class in the successful polling path after `render(s)`, while preserving the orange class for a stuck UI thread and the red class for the current offline state.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Three-by-three snapshots lose width 📎 Requirement gap ≡ Correctness
Description
Snapshots.render truncates cell dimensions using integer division by the grid side, then sizes the
output bitmap from those truncated dimensions. When a 3×3 layout uses a screen width not divisible
by three, a 1280×720 target becomes 1278×720 (or 640×360 becomes 639×360), so the snapshot no longer
matches the screen width or exact 16:9 dimensions.
Code

android/app/src/main/java/com/openipc/decoder/Snapshots.java[R89-90]

+        int cw = width / side;
+        int ch = height(width) / side;
Evidence
snapshotCells() supplies the clamped display width, and render() divides the target dimensions
by the grid side before multiplying the truncated cell dimensions back to size the output bitmap.
When cameras beyond the first four select a 3×3 grid, a 1280-pixel target width yields 426-pixel
cells and a 1278-pixel-wide output.

Match snapshot dimensions to screen width
android/app/src/main/java/com/openipc/decoder/Snapshots.java[89-90]
android/app/src/main/java/com/openipc/decoder/Decoder.java[629-633]
android/app/src/main/java/com/openipc/decoder/Decoder.java[979-982]
android/app/src/main/java/com/openipc/decoder/Decoder.java[1002-1027]
android/app/src/main/java/com/openipc/decoder/Snapshots.java[87-102]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Three-by-three layouts truncate cell dimensions through integer division, so widths not divisible by three produce JPEGs narrower than the bounded screen width and break the exact 16:9 dimensions.
## Fix Focus Areas
- android/app/src/main/java/com/openipc/decoder/Snapshots.java[89-102]
## Recommended Fix
Allocate the output bitmap at the full target width and height, then distribute remainder pixels across cell rectangles and their PixelCopy dimensions so the grid fills the exact bounded screen dimensions.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended
4. Stopped decoder shows a green status 🐞 Bug ≡ Correctness ⭐ New
Description
render() assigns the green status class whenever ui_thread is absent, including the server’s `no
activity` response. When the decoder activity is destroyed, the server keeps serving that fallback
response, so the header indicates a healthy decoder even though no activity is running.
Code

android/app/src/main/java/com/openipc/decoder/DebugServer.java[R507-508]

+            + "var ui=s.ui_thread&&s.ui_thread!=='ok';$('err').textContent=ui?'UI '+"
+            + "s.ui_thread:'';$('dot').className='dot '+(ui?'warn':'ok');\n"
Evidence
Activity destruction clears the server source; the resulting fallback stats omit ui_thread, which
the new indicator logic interprets as OK.

android/app/src/main/java/com/openipc/decoder/Decoder.java[1398-1402]
android/app/src/main/java/com/openipc/decoder/DebugServer.java[210-219]
android/app/src/main/java/com/openipc/decoder/DebugServer.java[503-508]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The page treats a missing `ui_thread` field as healthy, but the server omits that field when there is no decoder activity.

## Fix Focus Areas
- android/app/src/main/java/com/openipc/decoder/DebugServer.java[507-508]
- android/app/src/main/java/com/openipc/decoder/DebugServer.java[210-219]

## Recommended Fix
Make the header distinguish the `no activity` response from an explicit `ui_thread: "ok"` response. Show a non-green state and an appropriate message while retaining the existing warning for an unresponsive UI thread and red state for failed requests.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Timed-out captures still burden the screen 🐞 Bug ☼ Reliability
Description
debugSnapshotJpeg() returns after a one-second wait without cancelling its posted UI task, which
now calls getBitmap() at up to 1920×1080. When the UI task runs slowly or starts after the request
times out, the client gets a 503 while the main thread still performs the full capture; a bitmap
produced after that timeout also bypasses render()'s recycle path.
Code

android/app/src/main/java/com/openipc/decoder/Decoder.java[998]

+                c.frame = mSurface.getBitmap(width, Snapshots.height(width));
Evidence
The request posts snapshotCells() to the main looper but does not cancel that runnable on timeout.
The changed single-view call requests a much larger bitmap; only bitmaps passed to
Snapshots.render() are recycled, and the HTTP handler turns the timed-out null result into a 503.

android/app/src/main/java/com/openipc/decoder/Decoder.java[941-972]
android/app/src/main/java/com/openipc/decoder/Decoder.java[979-999]
android/app/src/main/java/com/openipc/decoder/Snapshots.java[40-43]
android/app/src/main/java/com/openipc/decoder/Snapshots.java[119-126]
android/app/src/main/java/com/openipc/decoder/DebugServer.java[175-184]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A timed-out JPEG request can still perform the newly enlarged single-view capture on the main thread and leave its bitmap for garbage collection.
## Fix Focus Areas
- android/app/src/main/java/com/openipc/decoder/Decoder.java[954-972]
- android/app/src/main/java/com/openipc/decoder/Decoder.java[990-999]
## Recommended Fix
Make request expiry visible to the posted task so it skips captures that have not started, and explicitly recycle any bitmap produced for a request that has already expired. Keep the expensive capture work from repeatedly blocking the UI thread.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Context sources
Review mode: Auto: ⚖️ Balanced: Downgraded extended -> standard: change is below the extended eligibility bar (hunks 14/18, lines 257/200; both must reach the floor). Router rationale: Multiple independent snapshot, concurrency, rendering, and responsive UI paths changed.
Results up to commit N/A


🐞 Bugs (1) 📘 Rule violations (0) 📎 Requirement gaps (1) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)


Action required
1. Three-by-three snapshots lose width 📎 Requirement gap ≡ Correctness
Description
Snapshots.render truncates cell dimensions using integer division by the grid side, then sizes the
output bitmap from those truncated dimensions. When a 3×3 layout uses a screen width not divisible
by three, a 1280×720 target becomes 1278×720 (or 640×360 becomes 639×360), so the snapshot no longer
matches the screen width or exact 16:9 dimensions.
Code

android/app/src/main/java/com/openipc/decoder/Snapshots.java[R89-90]

+        int cw = width / side;
+        int ch = height(width) / side;
Evidence
snapshotCells() supplies the clamped display width, and render() divides the target dimensions
by the grid side before multiplying the truncated cell dimensions back to size the output bitmap.
When cameras beyond the first four select a 3×3 grid, a 1280-pixel target width yields 426-pixel
cells and a 1278-pixel-wide output.

Match snapshot dimensions to screen width
android/app/src/main/java/com/openipc/decoder/Snapshots.java[89-90]
android/app/src/main/java/com/openipc/decoder/Decoder.java[629-633]
android/app/src/main/java/com/openipc/decoder/Decoder.java[979-982]
android/app/src/main/java/com/openipc/decoder/Decoder.java[1002-1027]
android/app/src/main/java/com/openipc/decoder/Snapshots.java[87-102]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Three-by-three layouts truncate cell dimensions through integer division, so widths not divisible by three produce JPEGs narrower than the bounded screen width and break the exact 16:9 dimensions.
## Fix Focus Areas
- android/app/src/main/java/com/openipc/decoder/Snapshots.java[89-102]
## Recommended Fix
Allocate the output bitmap at the full target width and height, then distribute remainder pixels across cell rectangles and their PixelCopy dimensions so the grid fills the exact bounded screen dimensions.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended
2. Timed-out captures still burden the screen 🐞 Bug ☼ Reliability
Description
debugSnapshotJpeg() returns after a one-second wait without cancelling its posted UI task, which
now calls getBitmap() at up to 1920×1080. When the UI task runs slowly or starts after the request
times out, the client gets a 503 while the main thread still performs the full capture; a bitmap
produced after that timeout also bypasses render()'s recycle path.
Code

android/app/src/main/java/com/openipc/decoder/Decoder.java[998]

+                c.frame = mSurface.getBitmap(width, Snapshots.height(width));
Evidence
The request posts snapshotCells() to the main looper but does not cancel that runnable on timeout.
The changed single-view call requests a much larger bitmap; only bitmaps passed to
Snapshots.render() are recycled, and the HTTP handler turns the timed-out null result into a 503.

android/app/src/main/java/com/openipc/decoder/Decoder.java[941-972]
android/app/src/main/java/com/openipc/decoder/Decoder.java[979-999]
android/app/src/main/java/com/openipc/decoder/Snapshots.java[40-43]
android/app/src/main/java/com/openipc/decoder/Snapshots.java[119-126]
android/app/src/main/java/com/openipc/decoder/DebugServer.java[175-184]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A timed-out JPEG request can still perform the newly enlarged single-view capture on the main thread and leave its bitmap for garbage collection.
## Fix Focus Areas
- android/app/src/main/java/com/openipc/decoder/Decoder.java[954-972]
- android/app/src/main/java/com/openipc/decoder/Decoder.java[990-999]
## Recommended Fix
Make request expiry visible to the posted task so it skips captures that have not started, and explicitly recycle any bitmap produced for a request that has already expired. Keep the expensive capture work from repeatedly blocking the UI thread.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

Comment on lines +89 to +90
int cw = width / side;
int ch = height(width) / side;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

1. Three-by-three snapshots lose width 📎 Requirement gap ≡ Correctness

Snapshots.render truncates cell dimensions using integer division by the grid side, then sizes the
output bitmap from those truncated dimensions. When a 3×3 layout uses a screen width not divisible
by three, a 1280×720 target becomes 1278×720 (or 640×360 becomes 639×360), so the snapshot no longer
matches the screen width or exact 16:9 dimensions.
Agent Prompt
## Issue description
Three-by-three layouts truncate cell dimensions through integer division, so widths not divisible by three produce JPEGs narrower than the bounded screen width and break the exact 16:9 dimensions.

## Fix Focus Areas
- android/app/src/main/java/com/openipc/decoder/Snapshots.java[89-102]

## Recommended Fix
Allocate the output bitmap at the full target width and height, then distribute remainder pixels across cell rectangles and their PixelCopy dimensions so the grid fills the exact bounded screen dimensions.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

? statusText.getText().toString() : c.live ? "Playing" : "No new frames";
if (c.live) {
c.frame = mSurface.getBitmap(Snapshots.CELL_W * 2, Snapshots.CELL_H * 2);
c.frame = mSurface.getBitmap(width, Snapshots.height(width));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Remediation recommended

2. Timed-out captures still burden the screen 🐞 Bug ☼ Reliability

debugSnapshotJpeg() returns after a one-second wait without cancelling its posted UI task, which
now calls getBitmap() at up to 1920×1080. When the UI task runs slowly or starts after the request
times out, the client gets a 503 while the main thread still performs the full capture; a bitmap
produced after that timeout also bypasses render()'s recycle path.
Agent Prompt
## Issue description
A timed-out JPEG request can still perform the newly enlarged single-view capture on the main thread and leave its bitmap for garbage collection.
## Fix Focus Areas
- android/app/src/main/java/com/openipc/decoder/Decoder.java[954-972]
- android/app/src/main/java/com/openipc/decoder/Decoder.java[990-999]
## Recommended Fix
Make request expiry visible to the posted task so it skips captures that have not started, and explicitly recycle any bitmap produced for a request that has already expired. Keep the expensive capture work from repeatedly blocking the UI thread.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@compot-dev compot-dev changed the title feat: sharper debug page snapshots — screen resolution, JPEG quality 85 (#103) feat: debug page — sharper snapshots and a header that works on phones (#103) Oct 13, 2026
@compot-dev

Copy link
Copy Markdown
Collaborator Author

/review

Comment thread android/app/src/main/java/com/openipc/decoder/Snapshots.java
Comment thread android/app/src/main/java/com/openipc/decoder/DebugServer.java
Comment thread android/app/src/main/java/com/openipc/decoder/DebugServer.java Outdated
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 847e37a

@compot-dev

Copy link
Copy Markdown
Collaborator Author

Qodo review — decisions:

  1. Labelled snapshots can overlap cell edges — fixed: labels are cut to the cell with TextUtils.ellipsize.
  2. Recovered pages stay marked offline — not a bug: render() runs on every successful poll and always sets the dot (ok / warn) and the error text again, so the red state clears with the next good response.
  3. Three-by-three snapshots lose width — fixed: the picture is exactly width × 16:9; cell edges are at i·size/side, so cells differ by at most a pixel and copies use each cell's own size.
  4. Stopped decoder shows a green status — fixed: without ui_thread (mode: no activity) the dot is orange and the mode is shown.
  5. Timed-out captures still burden the screen — fixed: on timeout the posted UI task is removed and marked abandoned; if it already ran or runs late, its frames are recycled and nothing is captured.

@compot-dev

Copy link
Copy Markdown
Collaborator Author

/review

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Qodo review — decisions:

  1. Labelled snapshots can overlap cell edges — fixed: labels are cut to the cell with TextUtils.ellipsize.
  2. Recovered pages stay marked offline — not a bug: render() runs on every successful poll and always sets the dot (ok / warn) and the error text again, so the red state clears with the next good response.
  3. Three-by-three snapshots lose width — fixed: the picture is exactly width × 16:9; cell edges are at i·size/side, so cells differ by at most a pixel and copies use each cell's own size.
  4. Stopped decoder shows a green status — fixed: without ui_thread (mode: no activity) the dot is orange and the mode is shown.
  5. Timed-out captures still burden the screen — fixed: on timeout the posted UI task is removed and marked abandoned; if it already ran or runs late, its frames are recycled and nothing is captured.

Understood. The decisions are:

  • finding 1: fixed with TextUtils.ellipsize.
  • finding 2: considered not a bug because successful polling resets the status.
  • finding 3: fixed by rendering the full target dimensions and distributing cell remainders.
  • finding 4: fixed by treating missing ui_thread as no activity/orange.
  • finding 5: fixed with timeout cancellation, abandonment checks, and bitmap recycling.

No review, dismissal, or implementation action was requested, so I have not changed any findings.

@compot-dev
compot-dev merged commit aaf72be into v2 Oct 13, 2026
4 checks passed
@compot-dev
compot-dev deleted the feat/snapshot-quality branch October 13, 2026 08:53
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit e150ebd

compot-dev added a commit that referenced this pull request Oct 15, 2026
compot-dev added a commit that referenced this pull request Oct 15, 2026
feat: debug page — sharper snapshots and a header that works on phones (#103)
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.

Debug page: sharper snapshots — screen resolution and better JPEG quality

1 participant