OASIS_MOBILE/android
SITO c3a1c841e6 fix: cerrar la integracion 0.9.5 — recursos que faltaban y regresiones
Una revision a fondo del arbol contra 0.9.5 saco varios fallos, unos heredados y
otros que introdujo la propia integracion. Verificados uno a uno antes de tocar.

Recursos referenciados que no existian (los introduje al adoptar vistas de 0.9.5
que los usan, mientras la rama los habia borrado):
- Los cuatro temas *-SNH.css. settings_view ofrece Dark, Clear, Matrix y Purple,
  pero solo estaba OasisMobile: elegir cualquiera daba 404 y la app se quedaba sin
  estilos. Ahora los cuatro responden 200.
- pdf.min.mjs, pdf.worker.min.mjs y pdf-viewer.js, referenciados desde seis vistas.

Regresiones revertidas a 0.9.5 verbatim en catorce ficheros. Las de fondo:
- larp_model perdio getGoverningPeriodId, que backend.js llama: el anuncio de
  gobierno no salia nunca, con el TypeError tragado por un catch.
- courts_view y search_view tenian selected: false, que hyperscript SI serializa
  como atributo y HTML interpreta como seleccionado. La forma de upstream, el
  spread condicional, es la correcta.
- pm_model perdio includeDeleted, projects_model la validacion de deadline pasado,
  jobs_model el desempate por timestamp, middleware el flag de debug HTTP.

forum_view pasa a 0.9.5 conservando el filtro hot. Esto arregla un fallo activo: la
vista de la rama declaraba un cuarto parametro topic y backend.js llama con cinco
argumentos, asi que al abrir una respuesta el %clave del hilo se colaba como tema y
el hilo salia VACIO. Upstream pasa los mismos cinco argumentos a una firma de tres y
JS los ignora, que es lo correcto.

chats_view pasa a 0.9.5 y se implementa replyTo de verdad. Estaba muerto de punta a
punta: backend.js no leia ctx.query.replyTo ni pasaba el cuarto argumento a
sendMessage, asi que ningun mensaje llegaba a tener replyTo y la cita no se
renderizaba jamas. Ahora la cadena esta completa —modelo, GET, POST y vista— con
indice msgById armado antes de mezclar con las encuestas, y sin gate de movil:
responder a un mensaje no es una funcion de movil.

aria-label: encontrada la causa. No es que hyperaxe lo ignore, es que hyperscript
solo asigna como propiedad y html-element solo serializa atributos de su tabla, en
la que aria-* no esta; data-* si tiene rama propia, por eso las tablas responsive
funcionaban. Se arregla con el escape hatch attrs:{}, sin tocar node_modules.

prepare.sh copia README.md y falla si no esta: backend.js lo lee con readFileSync
sin try/catch, de modo que sin el fichero el backend muere con ENOENT al arrancar.

Verificado con la app en marcha: 17 rutas OK, los cuatro recursos que faltaban
sirviendo 200, aria-label emitido en el HTML, e interfaz intacta (10 categorias,
50 modulos, 7 accesos rapidos, 0 etiquetas vacias).
2026-08-18 23:48:14 +02:00
..
app android: fijar HOME y reutilizar el node_modules podado del APK base 2026-08-18 23:35:19 +02:00
gradle/wrapper android: wrapper propio, con los permisos que faltaban para las videollamadas 2026-08-18 23:13:03 +02:00
scripts fix: cerrar la integracion 0.9.5 — recursos que faltaban y regresiones 2026-08-18 23:48:14 +02:00
.gitignore android: wrapper propio, con los permisos que faltaban para las videollamadas 2026-08-18 23:13:03 +02:00
build.gradle.kts android: wrapper propio, con los permisos que faltaban para las videollamadas 2026-08-18 23:13:03 +02:00
gradle.properties android: wrapper propio, con los permisos que faltaban para las videollamadas 2026-08-18 23:13:03 +02:00
gradlew android: wrapper propio, con los permisos que faltaban para las videollamadas 2026-08-18 23:13:03 +02:00
gradlew.bat android: wrapper propio, con los permisos que faltaban para las videollamadas 2026-08-18 23:13:03 +02:00
README.md android: wrapper propio, con los permisos que faltaban para las videollamadas 2026-08-18 23:13:03 +02:00
settings.gradle.kts android: wrapper propio, con los permisos que faltaban para las videollamadas 2026-08-18 23:13:03 +02:00

Wrapper Android propio

Proyecto Gradle para construir la APK de la rama sin depender del APK oficial como molde.

Por que existe

El APK que se venia usando es el de epsylon re-firmado, y su classes.dex no implementa WebChromeClient.onPermissionRequest. Sin ese metodo el WebView de Android deniega por defecto toda peticion de getUserMedia(), de modo que las videollamadas de Karvan no pueden funcionar por mucho que se parchee el manifest: la lista de permisos es estatica y hay que declararla al compilar.

Este wrapper resuelve las dos puertas: declara los permisos y los concede al contenido web solo tras el consentimiento del usuario.

Permisos: declarados, no concedidos

CAMERA, RECORD_AUDIO y MODIFY_AUDIO_SETTINGS figuran en el manifest porque Android exige que la lista sea fija. Pero son permisos peligrosos: se piden en tiempo de ejecucion la primera vez que se pulsa Llamar, no al instalar. Si nadie llama, no se pide nada y la app no accede a la camara ni al microfono.

uses-feature ... required="false" evita excluir de la tienda a dispositivos sin camara o sin microfono.

Que reutiliza y que es nuevo

Las tres librerias nativas se toman tal cual del APK existente:

  • libnode.so — nodejs-mobile v18.20.4 oficial (OpenSSL 3.0.13+quic), un artefacto publico; no hay que compilar Node.
  • libnative-lib.so — 6,8 KB, un solo simbolo JNI: Java_com_solarnethub_oasis_OasisActivity_startNodeWithArguments.
  • libc++_shared.so

Por eso el namespace del modulo es com.solarnethub.oasis: JNI resuelve por nombre y la clase puente tiene que vivir en ese paquete. El applicationId si es propio (net.laenre.oasis), asi que la app se instala junto a la oficial sin pisarla.

Si algun dia se quiere soltar el .so de epsylon, reimplementar esos 6,8 KB son unas 40 lineas de C++ contra las cabeceras de nodejs-mobile, y ahi si haria falta el NDK.

Mejoras sobre el APK actual

  • targetSdk 35 (el actual se quedo en uno ya caducado para la tienda).
  • Trafico en claro permitido solo hacia loopback via network_security_config, en vez de usesCleartextTraffic="true" global.
  • allowBackup="false": adb backup deja de poder extraer ~/.ssb.
  • El backend corre en un servicio en primer plano, en el proceso :node, asi que sobrevive en segundo plano — imprescindible para recibir llamadas. El APK actual declara FOREGROUND_SERVICE pero no registra ningun servicio.
  • Al descomprimir el backend se descartan las entradas con rutas sospechosas (zip slip).

Construir

# 1. dependencias del backend (una vez)
(cd ../src/server && npm install)

# 2. librerias nativas + empaquetado del backend
./scripts/prepare.sh ../../oasis_mobile      # o la ruta de un APK

# 3. compilar
./gradlew assembleDebug                       # o assembleRelease

Para firmar la release, por entorno (la keystore nunca al repositorio):

export OASIS_KEYSTORE=/ruta/oasis-alfa-key.jks
export OASIS_KEYSTORE_PASS=...
export OASIS_KEY_ALIAS=oasis
./gradlew assembleRelease

Requisitos

JDK 17 o superior con compilador. El OpenJDK del sistema puede ser solo runtime; gradle.properties apunta al JDK que trae Android Studio. Cambia esa linea si compilas en otra maquina.

Estado

Compila y genera APK con los permisos y el onPermissionRequest en su sitio. Falta probarlo en un dispositivo real: que arranque el backend, que la interfaz cargue y que getUserMedia resuelva al pulsar Llamar.