Probado con la APK instalada en el emulador de Android Studio (Pixel 9, Android 16): - La barra de arriba llevaba 65px de mas y la de abajo 24: el contenedor de la app del dev ya se aparta de las barras del sistema y nuestro CSS volvia a sumar safe-area-inset. Ahora --inset-top y --inset-bottom, a 0, en mobile_base.css. - Borrar en una tarjeta: la barra de confirmar se metia en la fila de botones y 'Si, borrar' salia letra a letra. La fila ya puede saltar de linea. - Acampada: .time-chip y .pref-pill del dev quedaban en negro sobre gris oscuro, y la pildora elegida con texto claro sobre el resalte en las paletas oscuras. - Zonas tactiles a 44px: barra de arriba, fichas, pasos de la bienvenida, aviso de bienvenida, pie, casillas con su etiqueta, botones del buzon; enlaces en el texto a 24px. - Acceso a un bloque de Ajustes: el titulo ya no queda bajo la barra de arriba. En la guia y en ARQUITECTURA_APK.md: no usar env(safe-area-inset-*), los tamaños minimos y el fallo del boton atras de la app del dev (la tecla retrocede dos pasos).
159 lines
7 KiB
Markdown
159 lines
7 KiB
Markdown
# Cómo está hecha la APK de Oasis
|
|
|
|
Análisis de la APK oficial de epsylon (`oasis-v1.3.3.apk`, sha256 `4e8d60d4…5550d`)
|
|
con REA 6.7.0 y JADX headless 0.7.1, el 20/10/2026. Lo que se cita sale del código
|
|
decompilado de `com.solarnethub.oasis.OasisActivity`; las salidas completas están
|
|
en `OASIS_APK/ANALISIS_REA/` (fuera del repo).
|
|
|
|
|
|
## Las piezas
|
|
|
|
La app es una envoltura Android pequeña (cuatro clases propias) alrededor de un
|
|
servidor Node.js que corre dentro del móvil, con nodejs-mobile.
|
|
|
|
```
|
|
APK
|
|
├── classes.dex OasisActivity pantalla: WebView, extracción, arranque de node
|
|
│ OasisService servicio en primer plano (sigue vivo con la app cerrada)
|
|
│ OasisAudioBridge, OasisStreamDecoder micro, altavoz y cámara del Teléfono
|
|
├── lib/*/libnode.so node para el móvil
|
|
└── assets/nodejs-project.zip
|
|
└── nodejs-project/ main.js, package.json, src/ (backend, vistas, CSS, node_modules)
|
|
```
|
|
|
|
La interfaz no es nativa: es HTML que sirve node en `http://127.0.0.1:3000` y que la
|
|
WebView enseña. Todo lo que vemos (vistas, CSS, textos) va dentro del zip.
|
|
|
|
|
|
## El arranque
|
|
|
|
`onCreate`:
|
|
|
|
1. `isExtractionNeeded()`: mira si hay que descomprimir.
|
|
2. `configureWebView()` y la pantalla de carga.
|
|
3. En otro hilo: `copyNodeProjectIfNeeded()` y después `startNode()`, que lanza
|
|
`node nodejs-project/main.js` y el servicio en primer plano.
|
|
4. Cuando node responde en `127.0.0.1:3000`, la WebView carga la página.
|
|
|
|
|
|
## Cuándo se descomprime el zip
|
|
|
|
```java
|
|
// copyNodeProjectIfNeeded()
|
|
File file2 = new File(file, ".version");
|
|
String appVersion = getAppVersion(); // versionName + "." + versionCode
|
|
String strTrim = file2.exists() ? readFile(file2).trim() : null;
|
|
if (file.exists() && appVersion.equals(strTrim)) {
|
|
Log.i("OASIS", "Node project already up to date (v" + appVersion + ")");
|
|
return;
|
|
}
|
|
...
|
|
stashLegacyState(file3, file4); // copia src/configs a .configs-stash
|
|
deleteRecursive(file);
|
|
extractZipAsset(ASSET_ZIP, ...);
|
|
restoreLegacyState(file4, file3); // vuelve a poner los configs de antes
|
|
writeFile(file2, appVersion);
|
|
```
|
|
|
|
Solo se descomprime si cambia `versionName.versionCode`. Una APK nueva con el mismo
|
|
nombre y número de versión arranca con el código de la vez anterior, aunque dentro
|
|
lleve otro. Eso es lo que pasaba con nuestras APK de prueba: el `versionCode` solo
|
|
subía con cada commit, y al instalar una APK hecha sobre el mismo commit (o con
|
|
cambios sin commitear) se veía el CSS y las vistas de antes. Desde el 20/10/2026
|
|
`sobre_oficial.sh` pone una huella del código en el `versionName`.
|
|
|
|
Al descomprimir se conservan los ficheros de `src/configs` (la config de cada cual:
|
|
tema, idioma, workflow, colores). Consecuencia: si cambiamos la config de serie en
|
|
`configs/oasis-config.json`, solo la ve quien instala de cero; quien actualiza se
|
|
queda con la suya.
|
|
|
|
|
|
## La WebView no guarda caché
|
|
|
|
```java
|
|
// configureWebView()
|
|
settings.setCacheMode(2); // LOAD_NO_CACHE
|
|
this.webView.clearCache(true);
|
|
```
|
|
|
|
Cada página y cada hoja de estilo se piden de nuevo al node del móvil. Si después de
|
|
instalar se ve CSS viejo, no es la caché: es que node está sirviendo el código viejo
|
|
(ver el punto anterior).
|
|
|
|
La WebView solo navega dentro de `127.0.0.1:3000` y `localhost:3000`. Los enlaces
|
|
a una `.apk` los descarga ella misma y el resto de enlaces http(s) los abre en otra
|
|
app (el navegador). Hay rutas que intercepta para el Teléfono (`/phone/speaker`,
|
|
`/phone/accept-speaker`).
|
|
|
|
|
|
## El backend por dentro
|
|
|
|
`rea analyze-javascript-application` sobre el `nodejs-project` oficial, sin
|
|
`node_modules` (463 ficheros JavaScript). Del grafo de imports, contando solo
|
|
ficheros del dev:
|
|
|
|
- `backend/backend.js` lo cose todo: hace 896 de los imports. Rutas, middleware y
|
|
la llamada a cada modelo y cada vista pasan por él.
|
|
- Las capas van `backend → views` (568 imports), `backend ↔ models` (218 y 223) y
|
|
`models → configs` (180). Las vistas casi no importan nada del backend (3).
|
|
- Lo más importado, y por tanto lo que más rompe si cambia: `views/main_views.js`
|
|
(217), `models/typed_log.js` (194), `configs/config-manager.js` (152),
|
|
`views/clearnet_view.js` (116), `backend/dedupe.js` (112).
|
|
|
|
Cuadra con la lista de lo que no se toca de la guía: un cambio nuestro en
|
|
`backend.js` o en `main_views.js` tropieza con casi todo lo demás en cada versión.
|
|
Por eso lo nuestro va en `fork_routes.js`, `mobile_ui.js` y las hojas propias, que
|
|
el dev no importa.
|
|
|
|
|
|
## Barras del sistema y botón atrás
|
|
|
|
`onCreate` pone un `OnApplyWindowInsetsListener` que aparta el contenedor de la
|
|
WebView de la barra de estado, del recorte de la cámara y de la barra de gestos
|
|
(`setPadding` con esos insets), salvo en pantalla completa. Pero devuelve los insets
|
|
sin consumir, y la WebView los sigue anunciando como `safe-area-inset-*`: si el CSS
|
|
los suma, el hueco sale doble (65 px de más arriba y 24 abajo en un Pixel 9 con
|
|
Android 16). Por eso nuestras hojas usan `--inset-top` y `--inset-bottom` a 0.
|
|
|
|
El botón atrás pasa por `goBackInWebView()`, que hace `webView.goBack()` o, si no
|
|
puede, carga la página anterior de su propia lista (`visitedUrls`). Con el gesto de
|
|
atrás se llama una vez. Con la tecla (móviles con tres botones) se llama dos veces,
|
|
por `onKeyDown` y por `registerPredictiveBack`, y se retrocede dos pasos: con el menú
|
|
abierto, en vez de cerrarlo se va a la pantalla anterior. Es de su código Android;
|
|
hay que pedírselo al dev.
|
|
|
|
|
|
## Lo que hace nuestra APK encima
|
|
|
|
`android/scripts/sobre_oficial.sh` coge esta APK tal cual y cambia:
|
|
|
|
- `assets/nodejs-project.zip`: nuestro `src/`, `main.js` y `package.json`, con el
|
|
`node_modules` del dev (el podado para el móvil).
|
|
- El paquete, a `net.laenre.oasis`, para que conviva con la oficial.
|
|
- El icono, por el adaptativo de `android/icono/`.
|
|
- `versionName`, a `<versión>-hacklab.<huella del código>`.
|
|
|
|
La parte Android (Java, puente de audio, servicio) es la del dev sin tocar.
|
|
|
|
|
|
## Repetir el análisis
|
|
|
|
```sh
|
|
npm install --global --prefix ~/.local rea-agents@6.7.0
|
|
# JADX headless 0.7.1, comprobando la huella que fija REA
|
|
curl -fL -o jadx-headless-mcp-0.7.1-all.jar \
|
|
https://github.com/1013503897/jadx-headless-mcp/releases/download/v0.7.1/jadx-headless-mcp-0.7.1-all.jar
|
|
echo '6e5eacf500b64292bfb73c49797c1958f6ee44646e43e868039ae7feb573ff75 jadx-headless-mcp-0.7.1-all.jar' | sha256sum -c -
|
|
export REA_JADX_MCP_JAR=$PWD/jadx-headless-mcp-0.7.1-all.jar JAVA_HOME=/opt/android-studio/jbr
|
|
|
|
rea inspect-android-package oasis-v1.3.3.apk
|
|
rea inspect-android-class oasis-v1.3.3.apk com.solarnethub.oasis.OasisActivity
|
|
rea inspect-android-method oasis-v1.3.3.apk com.solarnethub.oasis.OasisActivity copyNodeProjectIfNeeded
|
|
# el backend: sacar nodejs-project del zip y quitarle src/server/node_modules
|
|
# (con ellas, 16.500 ficheros, se corta por tiempo)
|
|
rea --json analyze-javascript-application /ruta/absoluta/nodejs-project-sin-node_modules
|
|
```
|
|
|
|
Con cada versión nueva del dev conviene mirar otra vez `copyNodeProjectIfNeeded`,
|
|
`configureWebView` y el manifiesto: si cambia la forma de extraer o la caché,
|
|
`sobre_oficial.sh` puede tener que cambiar también.
|