CameraX: un agente de código descubre que ImageAnalysis.targetRotation no se actualiza al girar el tablet
El bug se originó porque ImageAnalysis no cambiaba su targetRotation al girar el dispositivo 180°, y la solución requirió mapear correctamente la rotación del sensor.

El problema
Un escáner de códigos de barras en Android, basado en CameraX, mostraba la vista previa correctamente tras girar el tablet 180°, pero los fotogramas analizados llegaban invertidos. El error no se veía en la UI, solo en los frames que llegaban al ImageAnalysis. La raíz era que ImageAnalysis.targetRotation permanecía con la rotación previa.
La investigación con un agente de código
El agente de código automatizó la mayoría de los pasos: arrancó adb logcat, añadió instrumentación, ejecutó la prueba física y recogió los valores de estado. Los estados claves que se compararon fueron:
Configuration.orientationdisplayRotationPreview.targetRotationImageAnalysis.targetRotationImageProxy.rotationDegrees
Los logs mostraron que tras rotar el tablet, displayRotation cambió de 1 a 3, pero ImageAnalysis.targetRotation se quedó en 1. El agente concluyó que era necesario esperar a que previewView.display.rotation se actualizara antes de rebinding.
La solución
Se sustituyó la lógica de rebinding por una que actualiza directamente los targetRotation de Preview e ImageAnalysis cuando se detecta un cambio de orientación:
OrientationEventListener {
val calculatedRotation = mapRotation(orientationDegrees)
preview.targetRotation = calculatedRotation
imageAnalysis.targetRotation = calculatedRotation
}
La función mapRotation convierte los grados del listener a los constantes Surface.ROTATION_*, con un mapeo especial: 45‑134° se convierten a ROTATION_270 en lugar de ROTATION_90.
Validación
Se repitió el experimento físico. Antes: displayRotation=1, previewTargetRotation=1, analysisTargetRotation=1. Después: displayRotation=3, previewTargetRotation=3, analysisTargetRotation=3, y imageRotation cambió de 0 a 180° sin necesidad de recrear los casos de uso.
Por qué importa
La corrección elimina la dependencia de postDelayed, unbindAll() y bindToLifecycle(), reduciendo latencia y complejidad. Además, muestra un patrón de depuración reproducible: identificar estados que deben coincidir, capturarlos, y repetir la prueba tras el cambio.
Próximos pasos
Aunque funciona en el dispositivo probado, la lógica de mapeo puede variar en otros tablets. Se recomienda documentar la transformación de grados a Surface.ROTATION_* y probar en múltiples modelos.
