diff --git a/.gitignore b/.gitignore index c86e909..1fafaa8 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ node_modules/ # pruebas en local: el node_modules suele ser un enlace y el estado de la llamada lo escribe el movil src/server/node_modules .phone_state.json +android/.tools/ diff --git a/GUIA_DE_DESARROLLO.md b/GUIA_DE_DESARROLLO.md index cf9b62a..a46f338 100644 --- a/GUIA_DE_DESARROLLO.md +++ b/GUIA_DE_DESARROLLO.md @@ -55,10 +55,11 @@ Ahora mismo hay enganches en `main_views.js`, `tribes_view.js`, `vote_view.js`, La rama tuvo un módulo de salas y llamadas propio (Karvan). Se quitó el 19/10/2026 porque el dev ya trae Teléfono y Salas. En Android el sonido y la cámara pasan por un -puente nativo de la app (`.audio_bridge`, ficheros `src/server/phone_*_android.js`, -que son del dev). La APK oficial lo trae; nuestra app Android todavía no, y es lo -que falta para que las llamadas suenen. Si falta algo en las llamadas, se arregla en -ese puente o se le pide al dev, no se monta otro módulo. +puente nativo de la app del dev (`.audio_bridge`; los ficheros +`src/server/phone_*_android.js` también son suyos). Por eso la APK se monta sobre la +oficial con `android/scripts/sobre_oficial.sh`: su app tal cual, nuestro backend +dentro y paquete propio. Si falta algo en las llamadas, se le pide al dev, no se monta +otro módulo. Ojo al comparar con el dev: para el móvil la referencia es el `nodejs-project` de su APK oficial, no su repositorio git. Hay ficheros, como `SSB_server.js` o @@ -89,7 +90,8 @@ Aquí se puede hacer lo que haga falta. - `src/models/fork_workflows.js`: nuestros workflows. - `src/client/assets/translations/i18n_fork.js`: nuestros textos. - `src/client/assets/images/ux-*.svg`: nuestras imágenes. -- `android/`. +- `android/scripts/sobre_oficial.sh`: monta la APK sobre la oficial del dev. El resto + de `android/` (el proyecto Gradle propio) queda como estaba, sin uso. ## Cómo hacer cada cosa @@ -160,8 +162,8 @@ nombre es el de la ruta: `/tribes` es `tribes`. 3. Se repite la medición de `docs/FRONTERA_UPSTREAM.md`: qué ha tocado y si ha desaparecido alguna clase que usen nuestras hojas. 4. Se repasan a 412 px las pantallas de la lista de arriba. -5. Se compila la APK con la oficial de esa misma versión como base - (`android/scripts/release.sh` con `OASIS_APK_BASE`). +5. Se monta la APK sobre la oficial de esa misma versión: + `OASIS_VERSION=x.y.z android/scripts/sobre_oficial.sh`. ## Probar en local diff --git a/README.md b/README.md index 85640c5..9c27fc3 100644 --- a/README.md +++ b/README.md @@ -35,8 +35,10 @@ Android 7 o superior, 64 bits · se instala junto al Oasis oficial sin pisarlo parlamento, votaciones y eventos. El resto se enciende en *Módulos* o en *Ajustes → Workflows*. - Las llamadas son las de Oasis. Fuera Karvan, el módulo propio de salas: ahora se usan - el Teléfono y las Salas del dev. Ojo: en esta APK todavía no suenan, porque a nuestra - app Android le falta el puente de audio nativo que sí trae la oficial (ver abajo). + el Teléfono y las Salas del dev, con su levantar la mano, grabación y emisión. +- La APK se monta sobre la oficial: se coge la del dev, se le cambia el backend por el + de la rama y se le da otro nombre de paquete. Así trae todo lo nativo del dev (el + puente de audio y cámara, las notificaciones de llamada) y convive con la oficial. - Votaciones como en asamblea: al crear una se elige Sí/No, semáforo (verde, amarillo, rojo) o termómetro (del 1 al 5). Los resultados salen en barras. - Borrar pide confirmación. Antes la X de una tribu la borraba de un toque. @@ -59,12 +61,12 @@ Android 7 o superior, 64 bits · se instala junto al Oasis oficial sin pisarlo | | Oasis oficial | Esta rama | |---|---|---| -| Llamadas y salas | Teléfono y Salas, con micrófono y cámara | Los mismos; el puente de audio de Android todavía falta | +| Llamadas y salas | Teléfono y Salas, con micrófono y cámara | Los mismos, tal cual | | Interfaz de móvil | El menú de escritorio en una pantalla pequeña | Tema Acampada, lanzador con iconos, fichas, barra de cuatro atajos | | Primer arranque | Casi sesenta módulos encendidos | Modo acampada: lo justo para organizarse | | Votaciones | Seis opciones fijas en una tabla | Sí/No, semáforo o termómetro, con barras | | Varias identidades | Una por instalación | Varias en el mismo móvil, con aviso si dos comparten clave | -| Envoltorio Android | El del dev, sin código publicado | Proyecto Gradle propio y abierto: permisos, subida de ficheros, servicio que sobrevive | +| App Android | La del dev | La del dev, con el backend de la rama y paquete propio (`net.laenre.oasis`) | | Metadatos de las fotos | Se quitan solo si está `sharp`; en Android nunca | Se quitan siempre: GPS, marca, modelo, número de serie | Los nombres de las categorías, los módulos y el resto de la aplicación son los de @@ -104,13 +106,15 @@ mensaje en tu nombre ni cambiar lo que dijiste. ![Llamadas: las del dev, con el móvil enchufado](docs/capturas/esquema-llamadas.png) El Teléfono (llamadas de uno a uno, con buzón de voz) y las Salas (hasta cincuenta -personas, con vídeo, grabación y emisión) son de Oasis. En Android el sonido y la -cámara no los maneja Node: el servidor se conecta a un puente nativo de la app -(`.audio_bridge`), que graba, reproduce, saca fotogramas de la cámara y emite ficheros. +personas, con vídeo, levantar la mano, grabación y emisión) son de Oasis. En Android +el sonido y la cámara no los maneja Node: el servidor se conecta a un puente nativo de +la app (`.audio_bridge`), que graba, reproduce, saca fotogramas de la cámara y emite +ficheros. -La APK oficial del dev trae ese puente. La de esta rama todavía no: nuestra app -Android es otra (tiene el código publicado) y el puente hay que escribirlo. Hasta -entonces, en esta APK el Teléfono dice que las llamadas no están disponibles. +Ese puente está en la app Android del dev, que no tiene el código publicado. Por eso la +APK de esta rama se monta sobre la oficial: se queda con su app tal cual y solo cambia +lo que corre dentro (el backend con nuestra capa) y el nombre del paquete. Probado en +el emulador: se entra en una sala y el micrófono graba. En la barra de arriba, junto al avatar, está el botón de *No molestar* del Teléfono. @@ -187,25 +191,23 @@ descomprime el servidor. ### Compilar -Con Android Studio, lo más fácil: - -```sh -./android/scripts/android-studio.sh # trae el Oasis oficial y su APK, y empaqueta el backend -``` - -Después, en Android Studio: *File → Open →* la carpeta `android/`, esperar al Gradle -Sync, y *Build → Build App Bundle(s) / APK(s) → Build APK(s)*. Si cambias algo en -`src/` o en `main.js`, vuelve a lanzar el script antes de compilar. - -Desde la terminal, paso a paso: +La APK se monta sobre la oficial de la misma versión. Hace falta Java 17 o más y las +build-tools de Android (zipalign y apksigner); apktool se baja solo. ```sh cd android -# la primera vez hace falta la APK oficial de la MISMA versión (ahora la 1.3.3): -# de ella salen las librerías nativas y el node_modules podado del móvil -OASIS_APK_BASE=/ruta/oasis-v1.3.3.apk ./scripts/release.sh # compila, verifica y calcula la huella +./scripts/sobre_oficial.sh # baja la oficial, le pone el backend de la rama, la firma +# para publicar, con la clave del hacklab: +OASIS_KEYSTORE=... OASIS_KEYSTORE_PASS=... OASIS_KEY_ALIAS=... ./scripts/sobre_oficial.sh ``` +Sale en `android/build/sobre-oficial/oasis--hacklab.apk`. Sin clave se firma +con la de depuración y queda depurable, para poder inspeccionarla desde +`chrome://inspect`. + +El proyecto Gradle propio de `android/` (`release.sh`, Android Studio) sigue ahí, pero +ya no es el camino: no tiene el puente de audio y en él no hay llamadas. + ![Qué hay dentro de la APK](https://gitea.laenre.net/hacklab/OASIS_MOBILE/wiki/raw/img/esq/16-arquitectura-movil.png) --- diff --git a/android/README.md b/android/README.md index 3df3429..8834c99 100644 --- a/android/README.md +++ b/android/README.md @@ -6,8 +6,9 @@ molde. ## Por que existe Nota (19/10/2026): desde la 1.3.x la APK oficial ya pide microfono y camara y trae un -puente nativo de audio y camara (`.audio_bridge`) para el Telefono y las Salas. Esta app -aun no lo tiene. Lo de abajo describe la situacion anterior. +puente nativo de audio y camara (`.audio_bridge`) para el Telefono y las Salas. Por eso +la APK de la rama se monta ahora sobre la oficial con `scripts/sobre_oficial.sh`, y el +proyecto Gradle de esta carpeta queda sin uso. Lo de abajo describe la situacion anterior. 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 diff --git a/android/scripts/sobre_oficial.sh b/android/scripts/sobre_oficial.sh new file mode 100755 index 0000000..32d91e3 --- /dev/null +++ b/android/scripts/sobre_oficial.sh @@ -0,0 +1,126 @@ +#!/usr/bin/env bash +# +# Construye la APK de la rama encima de la APK oficial del dev. +# +# Desde la 1.3.x la app Android de epsylon trae cosas que la nuestra no tiene: el +# puente nativo de audio y camara (.audio_bridge) que usan el Telefono y las Salas, +# las notificaciones de llamada y sus permisos. Su codigo no esta publicado, asi que +# en vez de imitarlo se parte de su APK: +# +# 1. se baja la APK oficial de la misma version (github, epsylon/oasis) +# 2. se desmonta con apktool +# 3. su backend (assets/nodejs-project.zip) se cambia por el de la rama, conservando +# su node_modules, que es el podado para el movil +# 4. se le cambia el paquete a net.laenre.oasis (y lo que depende de el) para que +# conviva con la oficial +# 5. se vuelve a montar, se alinea y se firma +# +# Uso: +# ./scripts/sobre_oficial.sh # firma con la clave de depuracion +# OASIS_KEYSTORE=... OASIS_KEYSTORE_PASS=... OASIS_KEY_ALIAS=... ./scripts/sobre_oficial.sh +# +# Variables: +# OASIS_VERSION version del dev (por defecto la de package.json) +# OASIS_APK_OFICIAL ruta a la APK oficial, si ya se tiene (si no, se baja) +# ANDROID_HOME por defecto ~/Android/Sdk +# JAVA_HOME un JDK 17 o superior +set -euo pipefail + +HERE="$(cd "$(dirname "$0")/.." && pwd)" +REPO="$(cd "$HERE/.." && pwd)" +SDK="${ANDROID_HOME:-$HOME/Android/Sdk}" +JAVA="${JAVA_HOME:+$JAVA_HOME/bin/}java" +TOOLS="$HERE/.tools" +OUT="$HERE/build/sobre-oficial" +PKG_DEV="com.solarnethub.oasis" +PKG="net.laenre.oasis" +APKTOOL_VER="3.0.3" +APKTOOL_SHA="dbf930b076c6b9be08d57c449cacefc3bdd6b71ebd59b3066fc0e1f5b14f9423" + +say() { printf '\n== %s\n' "$*"; } +die() { printf '\n!! %s\n' "$*" >&2; exit 1; } + +VERSION="${OASIS_VERSION:-$(node -p "require('$REPO/package.json').version")}" +BT="$(ls -d "$SDK"/build-tools/*/ | sort -V | tail -1)" +[ -x "$BT/zipalign" ] || die "no encuentro zipalign en $BT" +mkdir -p "$TOOLS" "$OUT" + +# --- apktool, version fija y comprobada --------------------------------------- +APKTOOL="$TOOLS/apktool_$APKTOOL_VER.jar" +if [ ! -f "$APKTOOL" ]; then + say "bajando apktool $APKTOOL_VER" + curl -fsSL -o "$APKTOOL" "https://github.com/iBotPeaches/Apktool/releases/download/v$APKTOOL_VER/apktool_$APKTOOL_VER.jar" +fi +echo "$APKTOOL_SHA $APKTOOL" | sha256sum -c --quiet - || die "apktool no coincide con la huella esperada" + +# --- la APK oficial ------------------------------------------------------------- +OFICIAL="${OASIS_APK_OFICIAL:-$TOOLS/oasis-v$VERSION.apk}" +if [ ! -f "$OFICIAL" ]; then + say "bajando la APK oficial $VERSION" + curl -fsSL -o "$OFICIAL" "https://github.com/epsylon/oasis/releases/download/oasis-android-v$VERSION/oasis-v$VERSION.apk" +fi +say "APK oficial: $OFICIAL" +"$BT/apksigner" verify --print-certs "$OFICIAL" | grep -m2 -E "certificate DN|SHA-256" || die "la APK oficial no verifica" + +# --- desmontar ------------------------------------------------------------------ +say "desmontando" +rm -rf "$OUT/dec" +"$JAVA" -jar "$APKTOOL" d -q -f -o "$OUT/dec" "$OFICIAL" +DEC="$OUT/dec" + +# --- backend de la rama, con el node_modules del dev --------------------------- +say "cambiando el backend por el de la rama" +work="$(mktemp -d)" +( cd "$work" && unzip -q "$DEC/assets/nodejs-project.zip" ) +mv "$work/nodejs-project/src/server/node_modules" "$work/_nm" +rm -rf "$work/nodejs-project/src" +cp -r "$REPO/src" "$work/nodejs-project/src" +rm -rf "$work/nodejs-project/src/server/node_modules" +mv "$work/_nm" "$work/nodejs-project/src/server/node_modules" +cp "$REPO/main.js" "$REPO/package.json" "$REPO/README.md" "$work/nodejs-project/" +rm -f "$work/nodejs-project/.phone_state.json" +rm -f "$DEC/assets/nodejs-project.zip" +( cd "$work" && zip -q -r "$DEC/assets/nodejs-project.zip" nodejs-project ) +rm -rf "$work" + +# --- paquete propio, para convivir con la oficial ------------------------------ +# Las clases se quedan como estan (com.solarnethub.oasis.OasisActivity...); cambia +# el paquete, los authorities, el permiso propio y los nombres de las acciones de +# llamada, que si no se cruzarian con los de la app oficial. +say "paquete $PKG" +sed -i \ + -e "s|package=\"$PKG_DEV\"|package=\"$PKG\"|" \ + -e "s|$PKG_DEV.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION|$PKG.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION|g" \ + -e "s|android:authorities=\"$PKG_DEV.|android:authorities=\"$PKG.|g" \ + "$DEC/AndroidManifest.xml" +grep -rl "\"$PKG_DEV\.[A-Z_]*\"" "$DEC"/smali* | xargs -r sed -i -E "s|\"$PKG_DEV\.([A-Z_]+)\"|\"$PKG.\1\"|g" + +# las de prueba (firmadas con la clave de depuracion) se marcan depurables: asi se +# puede inspeccionar la WebView desde chrome://inspect. Las de publicar, nunca +if [ -z "${OASIS_KEYSTORE:-}" ]; then + sed -i 's|