quitar el proyecto Gradle propio y lo que dependia de el

La APK se monta sobre la oficial con sobre_oficial.sh; el proyecto Gradle, sus
scripts, el workflow de gitea, la guia en PDF y el pad de usabilidad ya no se usan.
This commit is contained in:
hacklab 2026-10-24 15:11:41 +02:00
parent e4bbbc23f2
commit 2d79e6779c
30 changed files with 17 additions and 2330 deletions

View file

@ -1,254 +0,0 @@
Prueba de usabilidad - Oasis Mobile 1.3.3 (rama PRUEBAS, tema Acampada)
19/10/2026 - emulador Pixel 9, Android 16, 1080x2424; tambien 720x1280 y letra al 130 %
Escenario: alguien de la acampada que entra, cambia a castellano, activa tribus
y parlamento, crea tribus, vota y abre una sala de voz.
=================================
Botones complejos de interpretar
=================================
- Los iconos del lanzador son letras del alfabeto vai (ꔙ ꕿ ꖒ ꕒ ꔗ...). No dicen
nada de lo que hacen: hay que leer siempre la etiqueta de debajo.
- Karvan (⧗) y Nodos (⧖) llevan casi el mismo reloj de arena.
Videos lleva algo que parece un simbolo de dolar.
- En la sala de voz los botones 🎤 🎥 ⛔ no tienen texto ni descripcion.
⛔ no se sabe si es colgar, silenciar o salir.
- En cada tarjeta de tribu el primer boton es una ✕ roja: es BORRAR TRIBU y
borra con un solo toque, sin preguntar. Probado: la tribu desaparece.
- En "Crear tribu" el texto CREAR TRIBU sale tres veces (boton de arriba,
titulo y boton de enviar). El de arriba recarga la pagina y borra lo escrito.
- Dos campos seguidos se llaman "Ubicacion" (lugar y enlace de mapa).
- "Estado: Privada/Publica" y "Modo: Estricto/Abierta" no explican que
significan. Por defecto sale Privada + Estricto.
- Votacion: las opciones salen mezcladas en ingles y castellano
(YES, NO, ABSTENTION, CONFUNDIDO, SEGUIR MAYORIA, SIN INTERES), partidas por
silabas y la tabla se corta por la derecha.
- Jerga a la vista: "server relay", "Synced-peers", "HcT/HcH", "TASA ECO",
la clave @...ed25519 completa en el pie y en el buzon.
=================================
Inicio intuitivo "empezar"
=================================
- El aviso "Tu avatar aun no esta configurado - EMPEZAR" sale arriba en TODAS
las paginas. En pantalla pequena se come casi una quinta parte. EMPEZAR no
dice que va a pasar (es poner foto y nombre).
- En Bienvenida el primer boton, el mas grande, es "AHORA NO". Lo facil es
saltarse todo.
- El nombre viene relleno con un trozo de la clave (jlYELyx4). Parece un nombre
ya puesto y no lo es.
- El idioma sale en el del movil la primera vez (el emulador estaba en ingles).
Cambiarlo despues costaba: Ajustes, bajar a la segunda pantalla, desplegable,
Espanol y boton "Establecer idioma".
- La pantalla de carga si sale en castellano y tarda unos 10 s: bien.
- Ajustes mide unas 7 pantallas. Lo que mas se toca (idioma, aspecto) queda
debajo de Identidades, que es lo mas tecnico.
- Al abrir una seccion apagada (p. ej. Tribus) te manda sin avisar a la tabla
de Modulos. Deberia decir "Tribus esta apagado - Activar".
- Invitar a una sala pide el @feed-id de la otra persona. Nadie lo sabe de
memoria: hace falta elegir de los contactos o compartir un enlace / QR.
=================================
Reduccion de funcionalidades
"skin activistas"
=================================
- El flujo "activistas" NO activa tribus, parlamento ni votaciones. Para una
acampada con 12 tribus es justo lo que falta. Propuesta: anadir tribes,
parliament y votes al flujo activistas (o hacer un flujo "acampada").
- Activar un modulo a mano: la pagina Modulos son 52 filas, unas 11 pantallas.
Tribus esta a 7800 px de altura y la casilla mide 16x16 px (lo minimo
recomendable son 48). El boton Guardar esta al final del todo.
- Propuesta: en movil, Modulos como interruptores grandes agrupados
(Hablar / Organizarse / Compartir) y guardado al tocar.
- Cada tribu que creas te manda a ti mismo un mensaje del POLITICALBOT
("Ha comenzado un nuevo ciclo de gobierno... Anarchy"). 12 tribus = 12 avisos
en el buzon. En movil sobra o deberia agruparse.
=================================
Voz y turnos de palabra
=================================
- LLAMAR en Karvan pide primero la CAMARA aunque solo quieras hablar. Si dices
que no a la camara la llamada no arranca, aunque des el micro. Sale el aviso
"pendiente del wrapper", que es una nota interna.
Arreglo sencillo: LLAMAR empieza solo con audio y 🎥 anade video.
(src/client/public/js/karvan.js, funcion startCall)
- Solo el boton 🎤 suelto funciona con audio.
- La llamada de Karvan se corta al cambiar de pagina (probado: el micro se para
al entrar en Parlamento). La sala que sigue viva al navegar es la de Mumble,
que va en otra app.
- Propuesta: en Parlamento y en cada tribu, boton "Entrar a la sala de voz" que
abra mumble://mumble.acampada26s.net/<canal de la tribu>.
- Turnos de palabra con tiempo: no existe. Encaja como cola de "pido palabra"
dentro de la sala (lista + cronometro visible para todas).
=================================
Listado de tribus
=================================
- Confirmado: cada tribu ocupa casi una pantalla porque todas repiten la misma
imagen generica grande. 12 tribus = 10 pantallas de scroll.
- Propuesta: lista compacta (nombre + miembros + publica/privada, una linea por
tribu), imagen solo si la tribu tiene una propia, y el buscador arriba fijo.
=================================
Votaciones: semaforo / termometro
=================================
- El servidor ya acepta opciones propias (campo "options" separado por comas),
pero el formulario no lo ofrece: siempre salen las 6 opciones fijas.
- Propuesta: al crear, elegir tipo: Si/No - Semaforo (verde, amarillo, rojo) -
Termometro (1 a 5). Sale casi gratis.
- Quien crea la votacion no puede votar en ella y la pantalla no lo explica.
En una asamblea, quien facilita se queda sin voto.
- El selector de fecha es el nativo de Android y propone una semana: bien.
=================================
Lo que va bien
=================================
- Sin desbordes horizontales en 1080 ni en 720 de ancho (salvo tablas).
- Tema Acampada legible, botones grandes, buen contraste.
- La barra de abajo (Actividad, Chats, Publicar, Buzon) se entiende sola.
- Actualizar encima de la 0.9.5 no pierde la identidad ni los datos.
Pendiente de probar: primer arranque en limpio (instalacion nueva), dos moviles
en la misma sala, y la barra de abajo que tapa media pantalla con el teclado
abierto (visto en Crear tribu).
=================================
Arreglado en PRUEBAS (19/10/2026)
=================================
- Borrar pide confirmacion ("¿Borrar para siempre? Si, borrar") y la X va la
ultima de la fila. Vale para tribus y para todo el contenido.
- Karvan: Llamar empieza solo con voz y la camara se anade con su boton.
Botones con icono y nombre (Llamar, Micro, Camara, Colgar). Sin el recuadro
gris en llamadas de voz. Ayuda para invitar. Textos en los 11 idiomas.
- Votaciones: al crear se elige Si/No, Semaforo (verde, amarillo, rojo) o
Termometro (1 a 5). Resultados en barras. Aviso a quien la crea.
- Flujo activistas con tribus, parlamento, votaciones y eventos.
- Abrir una seccion apagada dice cual es y ofrece "Activar" de un toque.
- Modulos: agrupados como el lanzador, interruptores grandes, Guardar fijo.
- Tribus en filas compactas (12 tribus: de 10 pantallas a unas 3). Dentro de
una tribu, las 25 secciones en una tira de fichas + "Mas".
- Iconos del lanzador y de la barra: dibujos claros en vez de letras vai.
La barra de abajo marca la seccion abierta y se esconde con el teclado.
- Ajustes empieza por idioma y aspecto; los idiomas se eligen de un toque.
- Aviso del avatar solo en la portada y en una linea. "Ahora no" al final de
la bienvenida.
- Pie de una linea: "Aun no sincronizas con nadie" / "Sincronizando con N".
- Fuera la jerga: tasa ECO, HcT/HcH, "server relay".
- Espacio: un solo margen de 14px (las fichas perdian 74px por lado).
Letra: Anton solo en titulos y en el boton principal.
=================================
Sigue pendiente
=================================
- Invitar a una sala o mandar un privado eligiendo de los contactos, no
pegando el @id.
- Turnos de palabra con tiempo en la sala de voz.
- Boton "Entrar a la sala de voz" (Mumble) en Parlamento y en cada tribu.
- Quien crea una votacion sigue sin poder votar (regla de upstream: cambiarla
haria que los nodos contaran distinto).
- Probar un primer arranque en limpio y dos moviles en la misma sala.
=================================
Segunda ronda (19/10/2026, tarde)
=================================
Desde ahora cada arreglo respeta docs/FRONTERA_UPSTREAM.md: primero CSS propio;
si hace falta HTML, una funcion propia y una linea en la vista del dev; nada en
backend, middleware ni modelos del dev.
- Boton principal de cada modulo (Crear chat, foro, pad, feed, evento...): el
mismo en todos. Antes unos a todo lo ancho, otros estrechos, otros con otra
letra. [CSS]
- Listas vacias: icono, texto centrado y recuadro discontinuo, en vez de una
linea suelta ("No se encontraron chats."). [CSS]
- El pie se queda abajo aunque la pagina sea corta. [CSS]
- Buzon: cada mensaje en pocas lineas (remitente y fecha arriba, el aviso, el
titulo y tres lineas de texto). Fuera Para (eres tu), Tasa ECO y Cifrado, que
eran iguales en todos. Antes un aviso ocupaba una pantalla. [CSS]
- Busqueda: primero el campo de buscar; las fechas y el ID despues. [CSS]
- Nodos: Refrescar, Quitar inactivos y Exportar en rejilla. [CSS]
- Workflow propio "Acampada" (chats, Karvan, pads, foros, wiki, tribus,
parlamento, votaciones, eventos...) en Ajustes y en la bienvenida, sin tocar
el "Activismo" del dev. [fichero propio]
Visto y pendiente de esta ronda:
- Buzon: Marcar como leido / Archivar / Borrar siguen uno debajo de otro.
- Buzon: los avisos del PoliticalBot de las tribus propias siguen llegando
(es upstream; se puede agrupar en la vista, no quitar).
- Los ID @...ed25519 siguen a la vista en perfil, buzon y nodos: falta mostrar
el nombre y dejar el ID para "copiar".
=================================
Tercera ronda: emulador limpio (19/10/2026, noche)
=================================
Emulador nuevo (Pixel 9, Android 16, 12 GB) para probar una instalacion desde
cero con la APK sin Karvan (codigo 102).
- Primer arranque: 18 segundos hasta la portada. Bien.
- Quien entra por primera vez no ve por donde empezar: el lanzador ocupa toda la
pantalla y el aviso "pon tu nombre y tu foto" quedaba a 1.300 px, abajo del
todo. Arreglado: el aviso va encima del lanzador. [CSS]
- El boton No molestar del Telefono ocupaba una fila entera arriba de cada
pagina. Arreglado: va en la barra de arriba, junto al avatar. [CSS]
- Ajustes marcaba English aunque se hubiera elegido otro idioma. Arreglado. [vista propia]
- LLAMADAS: en esta APK el Telefono dice "las llamadas no estan disponibles". La
APK oficial del dev trae un puente nativo de audio y camara (.audio_bridge) y
la nuestra no. Hay que escribirlo en la app Android o construir sobre la APK
oficial. Mientras tanto, en la rama no hay llamadas.
- El avatar por defecto (una calavera con gafas) asusta un poco para quien
empieza. Es del dev; se puede cambiar solo en el skin.
Con la APK montada sobre la oficial (1.3.3-hacklab, mismo emulador):
- Llamadas: ahora SI. El Telefono ofrece "Nueva llamada" y en una sala el micro
graba de verdad (comprobado en Android: VOICE_COMMUNICATION activo) y se corta
al salir.
- Turnos de palabra: las Salas del dev ya tienen "Levantar la mano". Es la base de
lo que pedia el pad; falta el cronometro.
- Dentro de una sala, los controles (silenciar, camara, levantar la mano, salir)
quedan muy abajo, debajo de la ficha de la sala. Habria que tenerlos a mano
mientras se esta dentro. [pendiente, CSS]
- La app del dev pide el permiso de camara nada mas abrirse, antes de ensenar
nada. Es su app; no se toca.
- El boton de crear sala se tapa con el teclado al escribir el titulo: hay que
cerrar el teclado para verlo.
=================================
Cuarta ronda: tamaños y sistemas (19/10/2026)
=================================
Con la navegación nueva (portada = actividad, pestaña Menú abajo).
- Android 16, Pixel 9 (APK real): menú, atrás con gesto y con tres botones, ir a un
módulo desde la hoja y volver. Bien.
- Fallaba: volver atrás desde un módulo abierto desde la hoja dejaba la portada con
la hoja abierta. Arreglado. [script propio]
- Android 9 (WebView 69, el que trae sin actualizar): fichas pegadas, hoja del menú
sin pintar, grupos montados y No molestar encima de la hoja. Arreglado. [CSS]
- Android 11 a 360 dp (motor 83): nueve pantallas sin desbordar, sin textos de
menos de 12 px ni botones de menos de 40 px. La barra de cinco cabe (70 dp cada una).
- En emulador, Android 9 y 11 no arrancan la APK (ni la nuestra ni la oficial): el
node arm64 no corre en el traductor ARM de x86. En un móvil de verdad no pasa; falta
probarlo en uno físico con Android 9-11.
- Tableta y letra al 130 %: bien.
- Bienvenida, paso Tipo: había que elegir una tarjeta y bajar seis tarjetas grandes
hasta Guardar. Ahora cada opción es un botón: un toque y queda guardado, el paso se
marca y se abre el siguiente. Una fila por opción con miniatura (en rejilla de tres,
"Conversaciones" se partía a 360 px). En Ajustes, lo mismo, y la sección va justo
después del idioma para encontrarla fácil. [vista propia + una línea en welcome_view
y settings_view]
- Bienvenida: faltaba elegir para qué se usa Oasis (el backend del dev quita ese paso
en el móvil). Vuelve como paso "Perfil", el segundo, justo después de la identidad:
Activismo primero, luego Acampada y el resto, cada uno con una línea de para qué
sirve. Un toque y guardado. En Ajustes, la misma lista. [vista propia + una línea
en welcome_view y settings_view]
- Con el perfil Activismo (45 módulos) los botones Personal/Social de la barra de
arriba no cabían a 360 px y tapaban el logo. Ya no hacían falta (filtraban el
lanzador, que ahora está en la hoja del menú): quitados. [vista propia]

View file

@ -1,213 +0,0 @@
#!/usr/bin/env python3
"""Portada y esquemas del README.
Las capturas de movil (01..06) se hacen aparte, con la app arrancada en un nodo de
pruebas a 412x915 y escala 2, en español y con lo que trae de serie (perfil Minimo y
colores del dev; 03 a 06 con el perfil Activista). Este script compone 00-portada.png
a partir de ellas, con los colores de serie, y dibuja esquema-interfaz.png,
esquema-rama.png y esquema-llamadas.png (estos aun con la paleta de Acampada).
El texto vive aqui: se cambia y se relanza, nunca se retocan los PNG a mano.
python3 docs/capturas/generar_portada.py
"""
import math
from pathlib import Path
from PIL import Image, ImageDraw, ImageFont
HERE = Path(__file__).resolve().parent
ANTON = HERE.parents[1] / "src/client/assets/fonts/Anton-Regular.ttf"
SANS = "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"
BG, FG, NEON, MUTED = "#ffffff", "#000000", "#00e5ff", "#555555"
# colores de serie del movil (los de la APK oficial del dev)
DEV_BG, DEV_CARD, DEV_FG, DEV_ACCENT, DEV_MUTED = "#121212", "#1e1e1e", "#ffd700", "#ffa300", "#bbbbbb"
SANS_BOLD = "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf"
def font(path, size):
return ImageFont.truetype(str(path), size)
def phone(shot, width, fg=FG, neon=NEON):
"""Una captura dentro de un marco grueso con una sombra de color debajo."""
im = Image.open(HERE / shot).convert("RGB")
h = round(im.height * width / im.width)
im = im.resize((width, h), Image.LANCZOS)
pad, radius, shadow = 10, 34, 10
frame = Image.new("RGBA", (width + 2 * pad, h + 2 * pad + shadow), (0, 0, 0, 0))
d = ImageDraw.Draw(frame)
d.rounded_rectangle((0, shadow, width + 2 * pad - 1, h + 2 * pad + shadow - 1), radius, fill=neon)
d.rounded_rectangle((0, 0, width + 2 * pad - 1, h + 2 * pad - 1), radius, fill=fg)
mask = Image.new("L", im.size, 0)
ImageDraw.Draw(mask).rounded_rectangle((0, 0, width - 1, h - 1), radius - pad, fill=255)
frame.paste(im, (pad, pad), mask)
return frame
def portada():
W, H = 1600, 1100
out = Image.new("RGB", (W, H), DEV_BG)
d = ImageDraw.Draw(out)
d.text((80, 40), "OASIS MOBILE", font=font(SANS_BOLD, 84), fill=DEV_FG)
bottom = d.textbbox((80, 40), "OASIS MOBILE", font=font(SANS_BOLD, 84))[3]
d.rectangle((80, bottom + 12, 420, bottom + 18), fill=DEV_ACCENT)
d.text((80, 186), "Rama PRUEBAS del hacklab · base Oasis 1.3.5 · lo que sale al instalarla",
font=font(SANS, 27), fill=DEV_MUTED)
shots = [("01-actividad.png", "Actividad al abrir"), ("02-menu.png", "Menú, perfil Mínimo"),
("03-menu-activista.png", "Menú, perfil Activista"), ("04-tribu.png", "Una tribu")]
pw, gap, top = 320, 50, 260
x = (W - (4 * (pw + 20) + 3 * gap)) // 2
cap = font(SANS_BOLD, 26)
for shot, label in shots:
fr = phone(shot, pw, fg="#000000", neon=DEV_ACCENT)
out.paste(fr, (x, top), fr)
tw = d.textlength(label, font=cap)
d.text((x + (fr.width - tw) / 2, top + fr.height + 18), label, font=cap, fill=DEV_FG)
x += fr.width + gap
out.save(HERE / "00-portada.png", optimize=True)
def caja(d, box, titulo, lineas, relleno=BG):
x0, y0, x1, y1 = box
d.rounded_rectangle((x0, y0 + 6, x1, y1 + 6), 18, fill=FG)
d.rounded_rectangle(box, 18, fill=relleno, outline=FG, width=4)
t = font(ANTON, 30)
d.text(((x0 + x1 - d.textlength(titulo, font=t)) / 2, y0 + 18), titulo, font=t, fill=FG)
s = font(SANS, 18)
for i, l in enumerate(lineas):
d.text(((x0 + x1 - d.textlength(l, font=s)) / 2, y0 + 70 + i * 26), l, font=s, fill=FG)
def esquema():
W, H = 1400, 840
out = Image.new("RGB", (W, H), BG)
d = ImageDraw.Draw(out)
d.text((46, 26), "LA INTERFAZ DE MÓVIL, POR DENTRO", font=font(ANTON, 46), fill=FG)
bottom = d.textbbox((46, 26), "LA INTERFAZ DE MÓVIL, POR DENTRO", font=font(ANTON, 46))[3]
d.rectangle((46, bottom + 10, 260, bottom + 16), fill=FG)
d.text((46, bottom + 30), "Casi sesenta módulos no caben en una pantalla de teléfono. Lo primero es lo que pasa; el resto, en el menú.",
font=font(SANS, 18), fill=MUTED)
# el movil esquematico
px0, py0, px1, py1 = 110, 170, 470, 790
d.rounded_rectangle((px0, py0, px1, py1), 30, outline=FG, width=5)
d.rounded_rectangle((px0 + 16, py0 + 20, px1 - 16, py0 + 84), 14, fill=BG, outline=FG, width=3)
s = font(SANS, 15)
d.text((px0 + 46, py0 + 42), "logo · no molestar · avatar", font=s, fill=FG)
# en medio, la actividad: lo que ha pasado en la red
y = py0 + 110
d.text((px0 + 40, y), "ACTIVIDAD", font=font(ANTON, 22), fill=FG)
d.line((px0 + 40, y + 32, px1 - 40, y + 32), fill=FG, width=3)
y += 52
for i in range(4):
d.rounded_rectangle((px0 + 40, y, px1 - 40, y + 86), 12, fill=BG, outline=FG, width=3)
d.rectangle((px0 + 56, y + 18, px0 + 150, y + 30), fill=FG)
d.rectangle((px0 + 56, y + 44, px1 - 70, y + 52), fill=MUTED)
d.rectangle((px0 + 56, y + 62, px1 - 120, y + 70), fill=MUTED)
y += 104
d.rounded_rectangle((px0 + 16, py1 - 84, px1 - 16, py1 - 20), 14, fill=NEON, outline=FG, width=3)
d.text((px0 + 62, py1 - 62), "4 atajos que eliges tú · MENÚ", font=s, fill=FG)
bx0, bx1 = 560, 1320
cajas = [
((bx0, 190, bx1, 340), "ARRIBA — LA BARRA SUPERIOR",
["El logo vuelve al inicio y el avatar abre tu perfil.",
"En medio, el botón de No molestar del Teléfono.",
"Con muchos módulos activos salen también los filtros Personal y Social."], BG),
((bx0, 375, bx1, 565), "EN MEDIO — LA ACTIVIDAD",
["Al abrir la app se ve lo que ha pasado en la red, no un menú.",
"Dentro de cada módulo, los filtros van en fichas pequeñas",
"y el botón principal (crear tribu, crear sala…) siempre igual."], BG),
((bx0, 600, bx1, 750), "ABAJO — LOS ATAJOS Y EL MENÚ",
["Cuatro atajos a tu elección y una quinta pestaña fija, Menú,",
"que abre desde abajo una hoja con todos los módulos por secciones",
"(Comunidad, Biblioteca, Gobernanza…). Está en todas las pantallas."], NEON),
]
flechas = [((px1 + 10, py0 + 52), (bx0 - 12, 262)), ((px1 + 10, 440), (bx0 - 12, 470)),
((px1 + 10, py1 - 52), (bx0 - 12, 675))]
for (a, b) in flechas:
ang = math.atan2(b[1] - a[1], b[0] - a[0])
tip = lambda da, r: (b[0] - r * math.cos(ang + da), b[1] - r * math.sin(ang + da))
d.line((a, tip(0, 12)), fill=FG, width=4)
d.polygon([b, tip(0.45, 20), tip(-0.45, 20)], fill=FG)
for box, t, l, r in cajas:
caja(d, box, t, l, r)
out.save(HERE / "esquema-interfaz.png", optimize=True)
def flecha(d, a, b, ancho=4):
ang = math.atan2(b[1] - a[1], b[0] - a[0])
tip = lambda da, r: (b[0] - r * math.cos(ang + da), b[1] - r * math.sin(ang + da))
d.line((a, tip(0, 12)), fill=FG, width=ancho)
d.polygon([b, tip(0.45, 20), tip(-0.45, 20)], fill=FG)
def titulo(d, texto, sub):
d.text((46, 26), texto, font=font(ANTON, 46), fill=FG)
bottom = d.textbbox((46, 26), texto, font=font(ANTON, 46))[3]
d.rectangle((46, bottom + 10, 260, bottom + 16), fill=FG)
d.text((46, bottom + 30), sub, font=font(SANS, 18), fill=MUTED)
def esquema_rama():
"""Como va la rama encima de Oasis: lo del dev abajo, lo nuestro encima."""
W, H = 1400, 760
out = Image.new("RGB", (W, H), BG)
d = ImageDraw.Draw(out)
titulo(d, "CÓMO VA LA RAMA ENCIMA DE OASIS",
"Oasis lo cambia el dev, casi una versión al día. La rama solo cambia cómo se ve y se usa en el móvil.")
capa = [
((60, 210, 380, 400), "ASPECTO",
["mobile_base.css: la maqueta", "mobile_ux.css: márgenes, letra,", "botones y piezas nuevas",
"Acampada.css: el skin"], NEON),
((400, 210, 720, 400), "PANTALLAS",
["mobile_ui.js y las vistas", "mobile_*.js. En una vista", "del dev, como mucho una", "línea que las llama"], NEON),
((740, 210, 1060, 400), "RUTAS",
["fork_routes.js, delante", "del router del dev: lo", "que no atiende pasa al", "suyo"], NEON),
((1080, 210, 1340, 400), "ANDROID",
["la app del dev tal cual,", "con nuestro backend", "dentro y paquete", "propio"], NEON),
]
for box, t, l, r in capa:
caja(d, box, t, l, r)
d.text((60, 166), "LO DE LA RAMA", font=font(ANTON, 26), fill=FG)
for x in (220, 560, 900, 1210):
flecha(d, (x, 418), (x, 478))
caja(d, (60, 500, 1340, 690), "OASIS DEL DEV: NO SE TOCA",
["backend.js y su middleware · los modelos (lo que se publica y cómo se cuenta) · SSB y su arranque",
"las traducciones · style.css y mobile.css · los temas, también OasisMobile.css",
"Comparado con su APK oficial, la rama cambia 17 ficheros suyos y 561 líneas. Ver GUIA_DE_DESARROLLO.md"], BG)
out.save(HERE / "esquema-rama.png", optimize=True)
def esquema_llamadas():
"""Las llamadas son las del dev; en Android pasan por un puente nativo de la app."""
W, H = 1400, 700
out = Image.new("RGB", (W, H), BG)
d = ImageDraw.Draw(out)
titulo(d, "LLAMADAS: LAS DEL DEV",
"Teléfono y Salas son de Oasis. En Android el sonido y la cámara pasan por un puente nativo de la app.")
cajas = [
((50, 200, 360, 430), "TELÉFONO Y SALAS",
["De Oasis, tal cual.", "Llamadas de uno a uno", "con buzón de voz; salas", "de hasta 50, con vídeo,", "grabación y emisión."], BG),
((400, 200, 700, 430), "LOS ENCHUFES",
["phone_*_android.js", "(del dev): se conectan", "a un puerto local que", "deja la app en", ".audio_bridge"], BG),
((740, 200, 1040, 430), "EL PUENTE NATIVO",
["Dentro de la app:", "R graba el micro", "P reproduce", "V saca la cámara", "S emite un fichero"], NEON),
((1080, 200, 1350, 430), "EL MÓVIL",
["Micrófono, cámara", "y altavoz, con los", "permisos que se piden", "la primera vez."], BG),
]
for box, t, l, r in cajas:
caja(d, box, t, l, r)
for a, b in (((362, 315), (398, 315)), ((702, 315), (738, 315)), ((1042, 315), (1078, 315))):
flecha(d, a, b)
caja(d, (50, 480, 1350, 630), "DÓNDE ESTÁ CADA COSA",
["El puente está en la app Android del dev, que no tiene el código publicado. Por eso la APK de la",
"rama se monta sobre la oficial: su app tal cual, nuestro backend dentro y paquete propio."], BG)
out.save(HERE / "esquema-llamadas.png", optimize=True)
if __name__ == "__main__":
import sys
# sin argumentos, todo; con nombres, solo esos (p. ej. "portada")
for name in sys.argv[1:] or ["portada", "esquema", "esquema_rama", "esquema_llamadas"]:
globals()[name]()

View file

@ -1,7 +0,0 @@
# PDF de la guia para el dev
Fuente de Oasis_Mobile_Guia_de_desarrollo.pdf (5 paginas A4). Para sacarlo:
chromium --headless=new --no-pdf-header-footer --print-to-pdf=guia.pdf guia.html
Comprobar con `pdfinfo guia.pdf` que siguen siendo 5 paginas.

View file

@ -1,339 +0,0 @@
<!doctype html>
<html lang="es">
<head>
<meta charset="utf-8">
<title>Oasis Mobile · Guía de desarrollo</title>
<style>
@font-face { font-family: "Anton"; src: url("file:///home/sito/COFRE/CODERS/OASIS_MOBILE/src/client/assets/fonts/Anton-Regular.ttf"); }
@page { size: A4; margin: 0; }
:root { --fg: #0b0b0b; --muted: #5a5a5a; --line: #0b0b0b; --cyan: #00d7f0; --soft: #f1f4f5; --red: #c4003b; --amber: #c98a00; --green: #0a8f3c; }
* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; background: #fff; color: var(--fg); font-family: "Inter", "DejaVu Sans", sans-serif; font-size: 10.5pt; line-height: 1.45; }
.page { width: 210mm; height: 297mm; padding: 16mm 17mm 14mm; position: relative; page-break-after: always; overflow: hidden; }
.page:last-child { page-break-after: auto; }
h1, h2, h3 { font-family: "Anton", Impact, sans-serif; font-weight: 400; text-transform: uppercase; margin: 0; letter-spacing: .01em; line-height: 1; }
h1 { font-size: 54pt; }
h2 { font-size: 30pt; margin-bottom: 4mm; }
h2 + .rule { width: 34mm; height: 2.2mm; background: var(--fg); margin: 0 0 6mm; }
h3 { font-size: 14pt; margin: 0 0 2.5mm; }
p { margin: 0 0 3mm; }
.lead { font-size: 12pt; color: var(--muted); margin-bottom: 6mm; }
ul { margin: 0 0 3mm; padding-left: 5mm; }
li { margin: 0 0 1.6mm; }
li::marker { color: var(--fg); }
code { font-family: "DejaVu Sans Mono", monospace; font-size: 8.8pt; background: var(--soft); padding: .2mm 1.2mm; border-radius: 1mm; }
.foot { position: absolute; left: 17mm; right: 17mm; bottom: 8mm; display: flex; justify-content: space-between; font-size: 8pt; color: var(--muted); border-top: .4mm solid #ddd; padding-top: 2mm; }
.cols { display: grid; grid-template-columns: 1fr 1fr; gap: 7mm; }
.cols3 { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 5mm; }
.card { border: .6mm solid var(--fg); border-radius: 3mm; padding: 4.5mm 5mm; }
.card.cyan { background: #e2fbff; }
.tag { display: inline-block; font-family: "Anton"; text-transform: uppercase; font-size: 11pt; padding: .6mm 3mm; border-radius: 1.5mm; color: #fff; margin-bottom: 3mm; }
.tag.red { background: var(--red); } .tag.amber { background: var(--amber); } .tag.green { background: var(--green); }
.num { font-family: "Anton"; font-size: 34pt; line-height: 1; }
.kpi { display: grid; grid-template-columns: repeat(4, 1fr); gap: 4mm; margin: 6mm 0; }
.kpi div { border-top: 1.2mm solid var(--fg); padding-top: 2mm; }
.kpi span { display: block; font-size: 8.5pt; color: var(--muted); }
figure { margin: 0 0 5mm; }
figcaption { font-size: 8.5pt; color: var(--muted); margin-top: 1.5mm; }
svg text { font-family: "Inter", sans-serif; }
.cover { background: #fff; }
.cover .band { position: absolute; left: 0; right: 0; top: 0; height: 9mm; background: var(--cyan); }
.cover h1 { margin-top: 30mm; font-size: 64pt; }
.cover .sub { font-family: "Anton"; text-transform: uppercase; font-size: 24pt; margin-top: 4mm; }
.cover .meta { margin-top: 8mm; font-size: 11pt; color: var(--muted); }
.ask { border-left: 2.2mm solid var(--cyan); background: var(--soft); padding: 4mm 5mm; border-radius: 0 3mm 3mm 0; }
table { width: 100%; border-collapse: collapse; font-size: 9.5pt; }
th { text-align: left; font-family: "Anton"; font-weight: 400; text-transform: uppercase; font-size: 10pt; border-bottom: .6mm solid var(--fg); padding: 1.5mm 2mm; }
td { border-bottom: .3mm solid #ddd; padding: 1.6mm 2mm; vertical-align: top; }
table.tight td { padding: 1.1mm 2mm; }
</style>
</head>
<body>
<!-- 1. PORTADA -->
<section class="page cover">
<div class="band"></div>
<h1>Oasis Mobile</h1>
<div class="sub">Guía de desarrollo de la rama del hacklab</div>
<div class="meta">Propuesta para epsylon · base Oasis 1.3.3 · rama PRUEBAS · 19 de octubre de 2026</div>
<div class="kpi">
<div><div class="num">39</div><span>versiones de Oasis entre la 1.0.1 y la 1.3.3</span></div>
<div><div class="num">17</div><span>ficheros tuyos que la rama cambia, frente a tu APK oficial</span></div>
<div><div class="num">2</div><span>líneas nuestras en <code>backend.js</code></span></div>
<div><div class="num">0</div><span>cambios en modelos, middleware o traducciones</span></div>
</div>
<h3>Qué es esta rama</h3>
<ul>
<li>Una forma de usar Oasis en el móvil pensada para una acampada: tribus como comisiones, votaciones de asamblea, salas de voz.</li>
<li>No es otro Oasis. Es tu APK oficial con una capa encima: skins, unas pocas vistas, un router propio y textos.</li>
<li>Se instala junto a la oficial con otro paquete (<code>net.laenre.oasis</code>) y habla con la misma red.</li>
</ul>
<h3>Qué te pedimos</h3>
<ul>
<li>Que revises si la frontera que proponemos (página 3) te parece bien: qué es tuyo y no tocamos, y qué es nuestro.</li>
<li>Que nos digas si alguno de los puntos de la página 5 te interesa para Oasis, para dejar de llevarlo nosotros.</li>
</ul>
<figure style="margin-top:6mm">
<svg viewBox="0 0 700 150" width="100%">
<defs><marker id="ar0" markerWidth="10" markerHeight="10" refX="9" refY="5" orient="auto"><path d="M0,0 L10,5 L0,10 z" fill="#0b0b0b"/></marker></defs>
<rect x="2" y="20" width="200" height="110" rx="10" fill="#fff" stroke="#0b0b0b" stroke-width="2.5"/>
<text x="102" y="58" text-anchor="middle" font-size="17" font-weight="700">Tu APK oficial</text>
<text x="102" y="82" text-anchor="middle" font-size="12" fill="#5a5a5a">app Android, puente de</text>
<text x="102" y="98" text-anchor="middle" font-size="12" fill="#5a5a5a">audio, node_modules</text>
<rect x="250" y="20" width="200" height="110" rx="10" fill="#e2fbff" stroke="#0b0b0b" stroke-width="2.5"/>
<text x="350" y="58" text-anchor="middle" font-size="17" font-weight="700">+ capa de la rama</text>
<text x="350" y="82" text-anchor="middle" font-size="12" fill="#5a5a5a">skins, vistas mobile_*,</text>
<text x="350" y="98" text-anchor="middle" font-size="12" fill="#5a5a5a">router propio, textos</text>
<rect x="498" y="20" width="200" height="110" rx="10" fill="#0b0b0b" stroke="#0b0b0b" stroke-width="2.5"/>
<text x="598" y="58" text-anchor="middle" font-size="17" font-weight="700" fill="#fff">Oasis hacklab</text>
<text x="598" y="82" text-anchor="middle" font-size="12" fill="#ddd">otro paquete, misma red,</text>
<text x="598" y="98" text-anchor="middle" font-size="12" fill="#ddd">convive con la oficial</text>
<line x1="204" y1="75" x2="244" y2="75" stroke="#0b0b0b" stroke-width="2.5" marker-end="url(#ar0)"/>
<line x1="452" y1="75" x2="492" y2="75" stroke="#0b0b0b" stroke-width="2.5" marker-end="url(#ar0)"/>
</svg>
</figure>
<div class="foot"><span>Oasis Mobile · Guía de desarrollo</span><span>1 / 5</span></div>
</section>
<!-- 2. LA REGLA -->
<section class="page">
<h2>La regla</h2><div class="rule"></div>
<p class="lead">Oasis lo cambias tú. Nosotros cambiamos cómo se ve y se usa en el móvil, y lo hacemos en ficheros nuestros.</p>
<figure>
<svg viewBox="0 0 700 330" width="100%">
<defs><marker id="ar1" markerWidth="10" markerHeight="10" refX="9" refY="5" orient="auto"><path d="M0,0 L10,5 L0,10 z" fill="#0b0b0b"/></marker></defs>
<text x="0" y="16" font-size="13" font-weight="700" letter-spacing="1">LA CAPA DE LA RAMA</text>
<g font-size="12">
<rect x="0" y="28" width="166" height="96" rx="9" fill="#e2fbff" stroke="#0b0b0b" stroke-width="2"/>
<text x="83" y="54" text-anchor="middle" font-size="15" font-weight="700">Aspecto</text>
<text x="83" y="76" text-anchor="middle">mobile_base.css</text><text x="83" y="92" text-anchor="middle">mobile_ux.css</text><text x="83" y="108" text-anchor="middle">Acampada.css</text>
<rect x="178" y="28" width="166" height="96" rx="9" fill="#e2fbff" stroke="#0b0b0b" stroke-width="2"/>
<text x="261" y="54" text-anchor="middle" font-size="15" font-weight="700">Pantallas</text>
<text x="261" y="76" text-anchor="middle">mobile_ui.js</text><text x="261" y="92" text-anchor="middle">mobile_*_view.js</text><text x="261" y="108" text-anchor="middle">hive_views.js</text>
<rect x="356" y="28" width="166" height="96" rx="9" fill="#e2fbff" stroke="#0b0b0b" stroke-width="2"/>
<text x="439" y="54" text-anchor="middle" font-size="15" font-weight="700">Rutas</text>
<text x="439" y="76" text-anchor="middle">fork_routes.js</text><text x="439" y="92" text-anchor="middle">mobile_layer.js</text><text x="439" y="108" text-anchor="middle">fork_workflows.js</text>
<rect x="534" y="28" width="166" height="96" rx="9" fill="#e2fbff" stroke="#0b0b0b" stroke-width="2"/>
<text x="617" y="54" text-anchor="middle" font-size="15" font-weight="700">Textos y APK</text>
<text x="617" y="76" text-anchor="middle">i18n_fork.js</text><text x="617" y="92" text-anchor="middle">(11 idiomas)</text><text x="617" y="108" text-anchor="middle">sobre_oficial.sh</text>
</g>
<g stroke="#0b0b0b" stroke-width="2" marker-end="url(#ar1)">
<line x1="83" y1="130" x2="83" y2="176"/><line x1="261" y1="130" x2="261" y2="176"/><line x1="439" y1="130" x2="439" y2="176"/><line x1="617" y1="130" x2="617" y2="176"/>
</g>
<text x="270" y="158" font-size="11" fill="#5a5a5a">una línea de enganche, solo con OASIS_MOBILE=1</text>
<rect x="0" y="184" width="700" height="140" rx="10" fill="#fff" stroke="#0b0b0b" stroke-width="3"/>
<text x="350" y="216" text-anchor="middle" font-size="18" font-weight="700">OASIS: TU CÓDIGO, TAL CUAL</text>
<g font-size="12" text-anchor="middle">
<rect x="18" y="234" width="152" height="70" rx="7" fill="#f1f4f5"/><text x="94" y="262" font-weight="700">backend.js</text><text x="94" y="280" fill="#5a5a5a">y su middleware</text>
<rect x="188" y="234" width="152" height="70" rx="7" fill="#f1f4f5"/><text x="264" y="262" font-weight="700">modelos</text><text x="264" y="280" fill="#5a5a5a">qué se publica y se cuenta</text>
<rect x="358" y="234" width="152" height="70" rx="7" fill="#f1f4f5"/><text x="434" y="262" font-weight="700">SSB y servidor</text><text x="434" y="280" fill="#5a5a5a">phone_module, puente</text>
<rect x="528" y="234" width="154" height="70" rx="7" fill="#f1f4f5"/><text x="605" y="262" font-weight="700">traducciones y temas</text><text x="605" y="280" fill="#5a5a5a">oasis_*.js, style.css…</text>
</g>
</svg>
<figcaption>La rama son cuatro piezas propias encima de tu código. Lo único que entra en tus ficheros son líneas de enganche.</figcaption>
</figure>
<div class="cols">
<div>
<h3>Por qué así</h3>
<ul>
<li>Sacas versión casi cada día y cada una llega en un solo commit: no se pueden coger cambios sueltos.</li>
<li><code>backend.js</code> cambió en 34 de 39 versiones, <code>main_views.js</code> en 31, las traducciones en 31.</li>
<li>Cada línea nuestra en esos ficheros sería un conflicto en la siguiente integración.</li>
<li>Si tocáramos un modelo, nuestro móvil contaría distinto que tu escritorio (votos, tribus, gobierno).</li>
</ul>
</div>
<div>
<h3>Cómo se mide</h3>
<ul>
<li>La referencia para el móvil es el <code>nodejs-project</code> de tu APK oficial, no tu git: ahí van tus cambios de móvil.</li>
<li>Hoy la rama cambia 17 ficheros tuyos y 561 líneas; en <code>backend.js</code>, 2.</li>
<li>En cada versión tuya se repite la comparación y se revisa si ha desaparecido alguna clase CSS de la que tiramos.</li>
</ul>
</div>
</div>
<div class="foot"><span>Oasis Mobile · Guía de desarrollo</span><span>2 / 5</span></div>
</section>
<!-- 3. LA FRONTERA -->
<section class="page">
<h2>Qué se toca y qué no</h2><div class="rule"></div>
<p class="lead">Tres zonas. La de en medio es la única que entra en tus ficheros, y solo con una línea.</p>
<div class="cols3">
<div class="card">
<span class="tag red">No se toca</span>
<ul>
<li><code>backend.js</code>, rutas y cadena de middleware</li>
<li>Modelos de <code>src/models</code>: lo que se publica en el log y cómo se cuenta</li>
<li><code>src/server</code>: arranque de SSB, dependencias, <code>phone_module</code></li>
<li><code>request_guards.js</code> y la CSP: nada en línea</li>
<li>Traducciones <code>oasis_*.js</code></li>
<li><code>style.css</code>, <code>mobile.css</code> y los temas, también <code>OasisMobile.css</code></li>
</ul>
</div>
<div class="card">
<span class="tag amber">Una línea</span>
<ul>
<li>Tus vistas: <code>main_views.js</code>, <code>tribes_view.js</code>, <code>vote_view.js</code>, <code>settings_view.js</code>, <code>welcome_view.js</code></li>
<li>La lógica va en <code>mobile_ui.js</code>; en tu vista, una llamada</li>
<li>Siempre detrás de <code>OASIS_MOBILE === '1'</code>: en escritorio todo queda como lo dejas</li>
<li>Si reescribes la vista, se vuelve a poner la línea</li>
</ul>
</div>
<div class="card cyan">
<span class="tag green">Nuestro</span>
<ul>
<li>Hojas: <code>mobile_base.css</code>, <code>mobile_ux.css</code>, <code>Acampada.css</code></li>
<li>Vistas: <code>mobile_ui.js</code>, <code>mobile_modules_view.js</code>, <code>hive_views.js</code>, <code>mobile_icons.js</code></li>
<li>Router: <code>fork_routes.js</code> y <code>mobile_layer.js</code></li>
<li>Workflows: <code>fork_workflows.js</code></li>
<li>Textos: <code>i18n_fork.js</code></li>
<li>APK: <code>sobre_oficial.sh</code></li>
</ul>
</div>
</div>
<h3 style="margin-top:4mm">Ejemplos concretos</h3>
<table class="tight">
<tr><th>Lo que hacía falta</th><th>Cómo se ha hecho</th><th>Zona</th></tr>
<tr><td>Votar en semáforo o termómetro, como en asamblea</td><td>Tu servidor ya acepta <code>options</code>; solo se añade el selector en el formulario</td><td>Una línea</td></tr>
<tr><td>Quien crea una votación no puede votar</td><td>Es una regla de tu modelo: no se cambia, se explica en pantalla</td><td>Nuestro</td></tr>
<tr><td>Avisar de que una sección está apagada y encenderla</td><td>Nuestro router mira tu redirección a <code>/modules</code>, sin tocar <code>checkMod</code></td><td>Nuestro</td></tr>
<tr><td>Workflow para acampadas</td><td>Se registra <code>acampada</code> en tus arrays; <code>activists</code> queda como el tuyo</td><td>Nuestro</td></tr>
<tr><td>Llamadas y salas</td><td>Las tuyas, con tu puente de audio: la APK se monta sobre la oficial</td><td>No se toca</td></tr>
<tr><td>Borrar con confirmación</td><td>Función en <code>mobile_ui.js</code>, enganchada en <code>renderContentActions</code></td><td>Una línea</td></tr>
</table>
<div class="foot"><span>Oasis Mobile · Guía de desarrollo</span><span>3 / 5</span></div>
</section>
<!-- 4. CÓMO FUNCIONA -->
<section class="page">
<h2>Cómo funciona por dentro</h2><div class="rule"></div>
<h3>Una petición en el móvil</h3>
<figure>
<svg viewBox="0 0 700 175" width="100%">
<defs><marker id="ar2" markerWidth="10" markerHeight="10" refX="9" refY="5" orient="auto"><path d="M0,0 L10,5 L0,10 z" fill="#0b0b0b"/></marker></defs>
<g font-size="12" text-anchor="middle">
<rect x="0" y="40" width="110" height="80" rx="9" fill="#fff" stroke="#0b0b0b" stroke-width="2"/><text x="55" y="76" font-weight="700">WebView</text><text x="55" y="94" fill="#5a5a5a">de tu app</text>
<rect x="140" y="40" width="130" height="80" rx="9" fill="#f1f4f5" stroke="#0b0b0b" stroke-width="2"/><text x="205" y="70" font-weight="700">Tu middleware</text><text x="205" y="88" fill="#5a5a5a">contexto, idioma,</text><text x="205" y="102" fill="#5a5a5a">CSP</text>
<rect x="300" y="40" width="130" height="80" rx="9" fill="#e2fbff" stroke="#0b0b0b" stroke-width="2"/><text x="365" y="70" font-weight="700">fork_routes</text><text x="365" y="88" fill="#5a5a5a">rutas propias o</text><text x="365" y="102" fill="#5a5a5a">next() al tuyo</text>
<rect x="460" y="40" width="110" height="80" rx="9" fill="#f1f4f5" stroke="#0b0b0b" stroke-width="2"/><text x="515" y="76" font-weight="700">Tus rutas</text><text x="515" y="94" fill="#5a5a5a">y vistas</text>
<rect x="600" y="40" width="100" height="80" rx="9" fill="#e2fbff" stroke="#0b0b0b" stroke-width="2"/><text x="650" y="70" font-weight="700">mobile_ui</text><text x="650" y="88" fill="#5a5a5a">si la vista</text><text x="650" y="102" fill="#5a5a5a">lo llama</text>
</g>
<g stroke="#0b0b0b" stroke-width="2" marker-end="url(#ar2)">
<line x1="112" y1="80" x2="134" y2="80"/><line x1="272" y1="80" x2="294" y2="80"/><line x1="432" y1="80" x2="454" y2="80"/><line x1="572" y1="80" x2="594" y2="80"/>
</g>
<text x="440" y="168" text-anchor="middle" font-size="11" fill="#5a5a5a">Lo que contestan tus rutas no se mira ni se cambia</text>
<text x="0" y="22" font-size="11" fill="#5a5a5a">Nuestro router va justo delante del tuyo: una sola línea en backend.js lo monta.</text>
</svg>
</figure>
<div class="cols">
<div>
<h3>Hojas de estilo, en este orden</h3>
<figure>
<svg viewBox="0 0 330 200" width="100%">
<g font-size="12">
<rect x="0" y="0" width="330" height="34" rx="6" fill="#f1f4f5" stroke="#0b0b0b"/><text x="12" y="22">1 · style.css y mobile.css (tuyas)</text>
<rect x="0" y="40" width="330" height="34" rx="6" fill="#e2fbff" stroke="#0b0b0b"/><text x="12" y="62">2 · mobile_base.css: maqueta de la rama</text>
<rect x="0" y="80" width="330" height="34" rx="6" fill="#fff" stroke="#0b0b0b"/><text x="12" y="102">3 · skin: Acampada.css o tu OasisMobile.css</text>
<rect x="0" y="120" width="330" height="34" rx="6" fill="#e2fbff" stroke="#0b0b0b"/><text x="12" y="142">4 · activista.css (solo con Acampada)</text>
<rect x="0" y="160" width="330" height="34" rx="6" fill="#0b0b0b"/><text x="12" y="182" fill="#fff">5 · mobile_ux.css: piezas comunes, la última</text>
</g>
</svg>
</figure>
<ul>
<li>Con tu skin OasisMobile se ve tu tema tal cual, encima de nuestra maqueta.</li>
<li>Colores por variables <code>--ux-*</code>: valen para los dos skins.</li>
</ul>
</div>
<div>
<h3>La APK, montada sobre la tuya</h3>
<figure>
<svg viewBox="0 0 330 200" width="100%">
<defs><marker id="ar3" markerWidth="10" markerHeight="10" refX="9" refY="5" orient="auto"><path d="M0,0 L10,5 L0,10 z" fill="#0b0b0b"/></marker></defs>
<g font-size="12">
<rect x="0" y="0" width="330" height="30" rx="6" fill="#fff" stroke="#0b0b0b"/><text x="12" y="20">tu APK oficial de la misma versión</text>
<rect x="0" y="42" width="330" height="30" rx="6" fill="#f1f4f5" stroke="#0b0b0b"/><text x="12" y="62">se desmonta (apktool, versión fija)</text>
<rect x="0" y="84" width="330" height="30" rx="6" fill="#e2fbff" stroke="#0b0b0b"/><text x="12" y="104">backend de la rama + tu node_modules</text>
<rect x="0" y="126" width="330" height="30" rx="6" fill="#e2fbff" stroke="#0b0b0b"/><text x="12" y="146">paquete net.laenre.oasis</text>
<rect x="0" y="168" width="330" height="30" rx="6" fill="#0b0b0b"/><text x="12" y="188" fill="#fff">se monta, se alinea y se firma</text>
</g>
<g stroke="#0b0b0b" stroke-width="1.6" marker-end="url(#ar3)"><line x1="300" y1="31" x2="300" y2="40"/><line x1="300" y1="73" x2="300" y2="82"/><line x1="300" y1="115" x2="300" y2="124"/><line x1="300" y1="157" x2="300" y2="166"/></g>
</svg>
</figure>
<ul>
<li>Tu app tal cual: puente de audio, notificaciones, permisos.</li>
<li>Se cambian también los authorities, el permiso propio y las acciones de llamada, para no cruzarse con la tuya.</li>
</ul>
</div>
</div>
<div class="foot"><span>Oasis Mobile · Guía de desarrollo</span><span>4 / 5</span></div>
</section>
<!-- 5. TRABAJAR JUNTOS -->
<section class="page">
<h2>Cómo seguimos</h2><div class="rule"></div>
<div class="cols">
<div>
<h3>Antes de cada commit</h3>
<ul>
<li>Nada de la zona roja en el diff; en tus vistas, solo líneas de enganche.</li>
<li><code>node --check</code> y el servidor local responde en las pantallas principales.</li>
<li>Visto a 360 y 412 px, en tableta, con la letra al 130 % y en un WebView viejo.</li>
<li>Textos nuevos en los 11 idiomas, en <code>i18n_fork.js</code>.</li>
<li>Nada en línea: la CSP lo bloquea.</li>
</ul>
<h3>Cuando sacas versión</h3>
<ul>
<li>Se coge la tuya tal cual; para el móvil, la de tu APK.</li>
<li>Se vuelven a poner los enganches en tus vistas.</li>
<li>Se repite la medición y se revisan las clases CSS.</li>
<li>Se monta la APK sobre tu oficial de esa versión.</li>
</ul>
</div>
<div>
<h3>Navegación en el móvil</h3>
<ul>
<li>Al abrir se ve la actividad, no un menú.</li>
<li>Abajo, cuatro atajos que elige cada cual y una pestaña Menú que abre todos los módulos desde abajo.</li>
<li>Probado en el emulador: dos toques a cualquier módulo desde cualquier pantalla.</li>
</ul>
<h3>Probado</h3>
<ul>
<li>Android 16 (APK real): 412 y 360 dp, tableta de 800 dp, letra al 130 %, gestos y tres botones.</li>
<li>Motores viejos: WebView 69 (Android 9) y 83 (Android 11), con alternativas donde falta <code>:has</code>, <code>gap</code> o <code>inset</code>.</li>
<li>Llamadas: en una sala el micrófono graba y se corta al salir.</li>
</ul>
</div>
</div>
<h3 style="margin-top:2mm">Lo que podría pasar a Oasis</h3>
<p>Cosas que hoy llevamos en tus ficheros y que nos gustaría que fueran tuyas, para dejar de mantenerlas aparte:</p>
<table>
<tr><th>Qué</th><th>Dónde está ahora</th><th>Por qué te puede interesar</th></tr>
<tr><td>Limpiar metadatos de las fotos también en Android</td><td><code>blobHandler.js</code> (190 líneas)</td><td>Sin <code>sharp</code> en el móvil, hoy las fotos suben con el GPS dentro</td></tr>
<tr><td>Varias identidades en el mismo móvil</td><td><code>ssb_config.js</code> (30 líneas)</td><td>Una persona, varias cuentas, con aviso si dos comparten clave</td></tr>
<tr><td>DNS del sistema primero</td><td><code>main.js</code> (45 líneas)</td><td>Respeta la VPN y el DNS privado de Android</td></tr>
<tr><td>Selector de idioma con el idioma actual</td><td>nuestro, en <code>mobile_ui.js</code></td><td><code>settings_view</code> lee <code>selectedLanguage</code> al cargar y siempre marca English</td></tr>
</table>
<div class="ask" style="margin-top:3mm">
<h3>Para aprobar</h3>
<ul style="margin:0">
<li>¿Te parece bien la frontera de la página 3?</li>
<li>¿Te parece bien que la APK de la rama se monte sobre tu oficial, con otro paquete?</li>
<li>¿Quieres alguno de los cuatro puntos de arriba en Oasis?</li>
</ul>
</div>
<div class="foot"><span>Oasis Mobile · Guía de desarrollo · hacklab · gitea.laenre.net/hacklab/OASIS_MOBILE</span><span>5 / 5</span></div>
</section>
</body>
</html>