██╗ ██╗███╗ ██╗██╗██╗ ██╗███████╗██████╗ ███████╗ █████╗ ██╗
██║ ██║████╗ ██║██║██║ ██║██╔════╝██╔══██╗██╔════╝██╔══██╗██║
██║ ██║██╔██╗ ██║██║╚██╗ ██╔╝█████╗ ██████╔╝███████╗███████║██║
██║ ██║██║╚██╗██║██║ ╚████╔╝ ██╔══╝ ██╔══██╗╚════██║██╔══██║██║
╚██████╔╝██║ ╚████║██║ ╚██╔╝ ███████╗██║ ██║███████║██║ ██║███████╗
╚═════╝ ╚═╝ ╚═══╝╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚══════╝
C O D E S C A N N E R
Un lector de códigos que asume que un código puede mentir.
Interpreta el contenido, lo explica en campos legibles, evalúa
16 señales de riesgo locales
y espera una confirmación explícita antes de ejecutar cualquier acción externa.
Interpretar primero. Actuar después.
Un QR es una instrucción opaca: nadie puede leer a simple vista si lleva a un banco o a una copia del banco. La mayoría de los lectores resuelve esa ambigüedad abriendo el enlace de inmediato. Este hace lo contrario.
| Pregunta | Cómo responde Universal Code Scanner |
|---|---|
| ¿A dónde lleva realmente este QR? | Muestra dominio, protocolo, ruta y consulta antes de abrir nada. |
| ¿Este enlace intenta engañarme? | 16 señales locales: Punycode, alfabetos mezclados, credenciales en la URL, IP literal, acortador, descarga ejecutable… |
| ¿Qué dice un código que no es un enlace? | Interpreta Wi-Fi, vCard, eventos, OTP, GS1, ISBN, EMVCo, SEPA, Swiss QR, criptomonedas y AAMVA. |
| ¿Puedo contar existencias con el teléfono? | Sesiones de inventario con conteo, notas y exportación a CSV, XLSX y JSON. |
| ¿Y si el código está en un PDF o en fotos? | Lotes de hasta 20 imágenes y 50 páginas, con progreso y cancelación. |
| ¿Queda mi historial expuesto? | Cifrado AES-256-GCM en disco; OTP y Wi-Fi con contraseña nunca se guardan solos. |
| ¿Qué pasa si la base se corrompe? | Modo temporal en memoria y Centro de recuperación por registro. |
| ¿Necesito generar un código? | Generador PNG y SVG de 10 simbologías. |
| ¿Está escaneando ahora mismo? | Barra de estado permanente: solo se mueve mientras el motor analiza cuadros. |
Capturas del APK release corriendo en el emulador oficial de Android (API 36). La pestaña Escanear corresponde a 1.1.0 y muestra la barra de estado en «Escaneando»; las demás se tomaron en 1.0.0 sobre pantallas que no han cambiado.
La lectura es automática: no hay que pulsar nada para leer un código. Lo que sí está siempre a la vista es en qué estado se encuentra la cámara, porque una cámara que no arranca no puede parecerse a una cámara esperando un código.
| Estado | Qué se ve | Qué se ofrece |
|---|---|---|
| Iniciando cámara | Barra en movimiento | — |
| Escaneando | Barra en movimiento y línea que recorre el marco | Botón «Pausar» |
| Escaneo en pausa | Barra detenida y vista atenuada | «Reanudar escaneo», o tocar la vista |
| Cámara no disponible | Barra en color de error y motivo concreto | «Reintentar» y «Reiniciar cámara» |
Cada lectura conseguida suena con un tono empaquetado en la aplicación —no con el efecto de sonido del sistema, que Android silencia en cuanto se apagan los sonidos táctiles— y vibra. Sonido y vibración son ajustes independientes.
Con movimiento reducido activado, la barra de estado y el marco se dibujan quietos: la información sigue estando, la animación no.
La landing no adivina por ti: la guía de diagnóstico por síntoma —encuadre, recorte de lectura, permiso denegado, silencio— está en SCANNER_UX.md, con la causa técnica de cada fallo corregido en 1.1.0.
La cobertura real de cada simbología depende del backend activo —ML Kit, Apple Vision o el motor web—. Detalle y exclusiones en SUPPORTED_FORMATS.md.
| Categoría | Cobertura en 1.1.0 |
|---|---|
| 2D | QR, Micro QR, Data Matrix, Aztec, PDF417, MaxiCode |
| Lineales | Code 128, Code 39, Code 93, Codabar, EAN-13, EAN-8, UPC-A, UPC-E, ITF |
| GS1 | DataBar, DataBar Expanded, DataBar Limited, Application Identifiers |
| Contenidos | URL, texto, binario, Wi-Fi, vCard, MeCard, vEvent, correo, teléfono, SMS, geo, OTP, ISBN, producto |
| Pagos | EMVCo, EPC/SEPA, Swiss QR Bill, Bitcoin, Lightning, Ethereum |
| Identidad | AAMVA/PDF417 reconocible |
| Generación | QR, Data Matrix, Aztec, PDF417, Code 128, Code 39, EAN-13, EAN-8, UPC-A, GS1-128 |
Ninguna rama de este flujo abre un enlace, marca un teléfono ni conecta una red sin que la persona lo confirme viendo antes los campos interpretados.
Todo ocurre en el dispositivo. Sin cuentas, sin publicidad, sin analítica, sin servidores propios.
| Garantía | Implementación |
|---|---|
| Nada se abre solo | Confirmación explícita para cada acción externa |
| Historial cifrado | AES-256-GCM con nonce y MAC por registro |
| Llave protegida | Keychain / Keystore vía flutter_secure_storage |
| Llave ausente ≠ llave nueva | El descifrado falla, informa y bloquea escrituras con ese identificador |
| Rotación segura | Reencriptado completo dentro de una transacción; llave huérfana eliminada si falla |
| Secretos efímeros | OTP y Wi-Fi con contraseña fuera del historial; portapapeles con borrado programado |
| Diagnóstico sin datos | Solo tipo de error y huella de pila: nunca cargas, URLs ni notas |
| Telemetría | Cero. No hay red saliente propia |
Las carpetas nativas no se versionan: se generan de forma reproducible con tool/bootstrap.py, que
además aplica permisos, minSdk, entitlements, descripciones de uso, la cadena de compilación de
Gradle y los iconos. Así el repositorio contiene únicamente fuente propia.
| Plataforma | Estado en 1.1.0 | Notas |
|---|---|---|
| Android (API 24+) | APK compilado y ejecutado en emulador | Motor ML Kit vía mobile_scanner |
| iOS (15.5+) | Compila en CI (--no-codesign) |
Motor Apple Vision · sin ejecución verificada |
| macOS | Compila en CI | Cámara y galería · sin ejecución verificada |
| Web / PWA | Compila y se ejecuta (release) | Es la versión publicada en /app/ · sin lectura de PDF local |
| Windows · Linux | Vía PWA | Sin motor nativo en 1.1.0 |
En pantallas anchas —navegador de escritorio o tablet— la interfaz se centra con un ancho máximo de teléfono en lugar de estirarse de borde a borde.
Los APK de cada versión están en Releases. Cada
archivo publica su SHA-256 en checksums.txt para que puedas verificar la descarga.
Móviles antiguos de 32 bits.
Emuladores y equipos x86.
Universal: funciona en todos, pesa el triple.
CN=Android Debug),
porque 1.1.0 no incluye firma de publicación. Sirven para instalar y probar. No sirven para
subir a Google Play, y una versión futura firmada con una clave propia no podrá actualizarlos: habrá que
desinstalar antes. Android pedirá permiso para instalar desde una fuente desconocida; es el flujo normal de
cualquier APK que no venga de una tienda.
¿Prefieres no instalar nada? La misma aplicación corre en el navegador en /app/: pedirá el permiso de cámara del navegador y, como toda la versión web, no lee PDF locales.
Ejecutado con Flutter 3.44.7 sobre esta misma versión del código.
| Comprobación | Resultado |
|---|---|
flutter analyze --fatal-infos |
0 hallazgos |
flutter test |
57 de 57 en verde |
flutter build web --release |
Compila |
flutter build apk --release |
Compila los cuatro APK publicados |
| Tono de lectura dentro del APK | Presente en los cuatro artefactos, no solo en el repositorio |
| Instalación y arranque | Emulador Android API 36: la cámara transmite y la barra marca «Escaneando» |
| SHA-256 del APK publicado | El archivo descargado del release coincide con checksums.txt |
Lo que no se ha verificado todavía está declarado sin adornos en VALIDATION.md y IMPLEMENTATION_STATUS.md: esta versión no ha sido probada en dispositivos físicos.
Esta versión prefiere decir lo que no hace antes que insinuar que lo hace.
Todo lo que sostiene lo anterior, en el repositorio.