{"slug": "a-coding-agent-tried-to-fix-my-camerax-rotation-bug-by-waiting-i-asked-for-proof", "title": "A Coding Agent Tried to Fix My CameraX Rotation Bug by Waiting. I Asked for Proof Instead.", "summary": "A developer debugging an Android barcode scanner found that a CameraX preview stayed visually correct after a tablet was rotated 180°, but the frames delivered to the ImageAnalysis analyzer were upside down. After halting a coding agent's proposed timing-based workaround, the developer added instrumentation for Configuration.orientation, displayRotation, Preview.targetRotation, ImageAnalysis.targetRotation and ImageProxy.rotationDegrees and reproduced the bug, showing ImageAnalysis.targetRotation stayed at ROTATION_90 while the display reported ROTATION_270. The fix drives both Preview.targetRotation and ImageAnalysis.targetRotation from an OrientationEventListener's calculated rotation, eliminating the need for delay, postDelayed, polling, unbindAll() or bindToLifecycle().", "body_md": "My Android barcode scanner had a CameraX bug: after rotating a tablet through 180°, the preview still looked correct, but the frames received by the analyzer were upside down.\n\nThe interesting part wasn't the final fix. It was deciding when we actually had enough evidence to trust it.\n\nA coding agent handled most of the debugging: reading the code, adding instrumentation, running `adb logcat`, changing the implementation, and checking the results. I handled the physical tablet.\n\nThe debugging loop was unusual.\n\nThe agent started `adb logcat`. I opened the scanner and physically rotated\n\nthe tablet. Then the agent collected the new values and adjusted the\n\ninstrumentation.\n\nWe repeated this several times until we had the states we needed to compare.\n\nThese were the values that eventually explained the bug. Before rotation:\n\n```\nConfiguration.orientation=2\ndisplayRotation=1\nanalysisTargetRotation=1\nimageRotation=0\n```\n\nAfter rotation:\n\n```\nConfiguration.orientation=2\ndisplayRotation=3\nanalysisTargetRotation=1\nimageRotation=0\n```\n\nThe display had moved on. `ImageAnalysis` had not.\n\nGetting to that evidence required stopping a different line of work first: waiting for a rotation value to update, then rebinding the camera.\n\nDuring an earlier experiment, the agent observed that the display-change callback had already fired while `previewView.display.rotation` still held the old value. About half a second later, that value became correct.\n\nThe agent concluded:\n\npost is not enough. We need to wait for previewView.display.rotation to actually change before rebinding.\n\nThe proposed direction looked like this:\n\n```\nDisplayListener\n→ callback\n→ display.rotation still has the old value\n→ post\n→ still the old value\n→ wait\n→ get the new rotation\n→ rebind CameraX\n```\n\nI stopped the work at that point. I did not yet know the correct fix. We might have needed another listener, different `ImageAnalysis` settings, or a deeper investigation of YUV processing.\n\nBut the timing approach left basic questions unanswered. Why did rotating the tablet require recreating the camera use cases? What exactly were we waiting for? Half a second on this tablet—how long on another?\n\nI asked the agent to stop fixing things and prove the cause first.\n\nWe added these values to the diagnostics:\n\n```\nConfiguration.orientation\ndisplayRotation\nPreview.targetRotation\nImageAnalysis.targetRotation\nImageProxy.rotationDegrees\n```\n\nThen we repeated the physical experiment. The agent started a fresh log capture; I opened the scanner screen and rotated the tablet. It collected the output, adjusted the instrumentation, and asked me to repeat the experiment when needed.\n\nThat produced the before-and-after logs at the top of this article.\n\nThe tablet had moved from `ROTATION_90` to `ROTATION_270`. Both positions were still landscape, so `Configuration.orientation` remained `2`:\n\n```\nORIENTATION_LANDSCAPE → ORIENTATION_LANDSCAPE\n```\n\nThe display already reported rotation `3`, while the analyzer retained its old target:\n\n```\ndisplay                 ROTATION_270\nImageAnalysis.target    ROTATION_90\n```\n\n`ImageProxy.rotationDegrees` consequently remained `0`.\n\nWe now had a specific, measured cause: **`ImageAnalysis.targetRotation` was not being updated when the device rotated through 180°.** That gave us something concrete to fix and a way to verify the result.\n\nThe implementation moved to this flow:\n\n```\nOrientationEventListener\n→ calculatedRotation\n→ Preview.targetRotation = calculatedRotation\n→ ImageAnalysis.targetRotation = calculatedRotation\n```\n\nThe existing `Preview` and `ImageAnalysis` instances remained in place. Handling the rotation no longer required `delay`, `postDelayed`, polling, `unbindAll()`, or `bindToLifecycle()`.\n\nThere was one subtle mapping detail. The degrees reported by `OrientationEventListener` could not be mapped directly to the `Surface.ROTATION_*` constant with the same angle in its name. In our case, the range 45..134° had to map to `ROTATION_270`, not `ROTATION_90`.\n\nWe verified the change with the same physical experiment. Before rotating the tablet:\n\n```\ndisplayRotation=1\npreviewTargetRotation=1\nanalysisTargetRotation=1\nimageRotation=0\norientationDegrees=70..79\ncalculatedRotation=3\ndisplayRotation=3\npreviewTargetRotation=3\nanalysisTargetRotation=3\nimageRotation=180\n```\n\nThe chain was consistent. CameraX received the new `targetRotation`, and `ImageProxy.rotationDegrees` changed from `0` to `180`, without recreating the use cases or rebinding.\n\nThese are the observations from our tablet and scanner. The mapping detail belongs to this case; it is not a claim that the same implementation has been verified on every device.\n\nThe agent performed most of the mechanical work. It read the existing code, added diagnostics, collected device data, analyzed the logs, changed the implementation, repeated the checks, and updated the merge request.\n\nMy contribution included designing the experiment, physically rotating the tablet, evaluating the hypotheses, and deciding what would count as sufficient evidence.\n\nAt the critical moment, I did not know the right five lines of Kotlin. I could still recognize that “the value arrives later, so wait for it” was an incomplete explanation of the bug.\n\nThe useful intervention was to require the agent to show where the state diverged. That changed both the investigation and the fix.\n\nFor me, the reusable lesson is to make the verification criterion explicit: identify the states that should agree, capture their values during the failing behavior, and repeat the same experiment after the change. Here, that meant checking the display rotation, both use-case targets, and the image rotation together.\n\nA coding agent can run much of that loop. Deciding what the experiment proves—and whether its evidence justifies accepting the fix—remains engineering work.\n\nThe [full case study](https://hram.github.io/en/articles/camerax-agent/) includes the debugging dialogue and a more detailed breakdown of how we divided the work.", "url": "https://wpnews.pro/news/a-coding-agent-tried-to-fix-my-camerax-rotation-bug-by-waiting-i-asked-for-proof", "canonical_source": "https://dev.to/hram/a-coding-agent-tried-to-fix-my-camerax-rotation-bug-by-waiting-i-asked-for-proof-instead-3fh9", "published_at": "2026-09-25 16:26:51+00:00", "updated_at": "2026-09-25 17:01:45.536038+00:00", "lang": "en", "topics": ["computer-vision", "ai-agents", "developer-tools"], "entities": ["CameraX", "Android", "ImageAnalysis", "OrientationEventListener", "ImageProxy"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/a-coding-agent-tried-to-fix-my-camerax-rotation-bug-by-waiting-i-asked-for-proof", "markdown": "https://wpnews.pro/news/a-coding-agent-tried-to-fix-my-camerax-rotation-bug-by-waiting-i-asked-for-proof.md", "text": "https://wpnews.pro/news/a-coding-agent-tried-to-fix-my-camerax-rotation-bug-by-waiting-i-asked-for-proof.txt", "jsonld": "https://wpnews.pro/news/a-coding-agent-tried-to-fix-my-camerax-rotation-bug-by-waiting-i-asked-for-proof.jsonld"}}