DropApp – PR2
1. Ideació i Evolució del Projecte
Concepte:
DropApp evoluciona d’un temporitzador passiu (PR1) a un assistent oftalmològic contextual. La sequedat ocular depèn directament de la humitat ambiental. Per tant, l’App no només avisa quan posar-se les gotes, sinó que informa l’usuari de l’estat del seu entorn immediat.
Selecció d’APIs (Estratègia de Dades):
Per oferir una experiència completa, he optat per combinar dues APIs complementàries:
- BigDataCloud (Reverse Geocoding): Permet traduir les coordenades GPS fredes (ex: 41.38, 2.17) a un nom de ciutat comprensible per a l’usuari (ex: “Barcelona”).
- Open-Meteo (Temps): Aporta la dada mèdica rellevant (% Humitat Relativa) sense necessitat de registre ni claus d’API.


Esbós preliminar de l’app
2. Desenvolupament Tècnic amb Capacitor
2.1. Arquitectura Híbrida
L’aplicació segueix el patró Bridge:
-
Capa Lògica (main.js): Actua com a orquestrador. Gestiona la geolocalització nativa i encadena les peticions a les dues APIs.
-
Capa Visual (sketch.js): Rep les dades processades (Nom de ciutat i Humitat) a través d’un objecte global compartit window.eyeWeather i les pinta al canvas
2.2. Integració de les APIs (Flux en Cascada)
S’ha implementat una lògica asíncrona robusta (async/await) al fitxer main.js per connectar el dispositiu amb les dues APIs externes:
-
GPS Natiu (Geolocation): El plugin
@capacitor/geolocationobté la latitud i longitud precises quan l’usuari té activat el mode “Ubicació GPS (Automàtic)”. S’utilitzaenableHighAccuracy: truei untimeoutde 10 segons per evitar bloquejos. -
Identificació (API 1 – BigDataCloud): Amb les coordenades rebudes, es consulta l’endpoint
reverse-geocode-clientde BigDataCloud amb el paràmetrelocalityLanguage=ca, prioritzant el nom de ciutat/localitat en català sempre que sigui possible. -
Dades Climàtiques (API 2 – Open-Meteo): A partir de la mateixa latitud/longitud es consulta Open-Meteo per obtenir la temperatura actual i la humitat relativa (
temperature_2m,relative_humidity_2m), sense necessitat de registres ni claus d’API. -
Persistència i flux manual: Si l’usuari desactiva el GPS i selecciona una ciutat manualment, les coordenades escollides es desen a
localStoragei es reutilitzen a les crides següents, mantenint el flux de dades encara que el GPS estigui aturat. -
Fallback per IP: Si no disposem de coordenades (primera obertura, error de GPS) l’app consulta de nou BigDataCloud, però en aquest cas mitjançant geolocalització per IP, per obtenir unes coordenades aproximades i un nom de ciutat genèric com a reserva.
-
Sincronització i estats UX: Mentre esperem resposta de les APIs, l’objecte global
window.eyeWeathermostra un estat de “????️ Cercant GPS” per indicar activitat. Si alguna de les crides falla, l’App entra en “Mode Offline (Sense dades)”, però evita trencar el flux visual del temporitzador i manté un estat de seguretat per a l’usuari.
2.3. Funcionalitats Natives
A part de la geolocalització, s’han integrat tres funcionalitats hardware per a l’experiència d’alerta:
-
-
Llanterna (
@capawesome/capacitor-torch): Des desketch.jses controla una màquina d’estats (0 = off, 1 = llum fixa, 2 = estroboscòpic) i, en funció del temps restant, es fan crides awindow.setFlashMode()exposada permain.js. Aquest pont és el que activa o desactiva realment el maquinari de la llanterna al dispositiu. -
Haptics (
@capacitor/haptics):main.jsencapsula el patró de vibració en bucle (setInterval) quan sona l’alarma final, isketch.jshi accedeix mitjançantwindow.dropappVibrate()per donar feedback tàctil immediat en prémer els botons. -
Local Notifications: Per garantir l’avís fins i tot si el dispositiu entra en repòs profund (Doze Mode), main.js programa l’alarma en el moment d’iniciar el comptador utilitzant la propietat allowWhileIdle i canals prioritaris. Això delega la responsabilitat al sistema operatiu, assegurant la puntualitat exacta independentment de l’estat de l’app.
-
Integració final mostrant ubicació i dades ambientals.


Temporitzador acabat, moment on està activat el flash, la vibració i on apareix la notificació
2.4. Mode de proves (interval 0)
Durant el desenvolupament ha estat necessari verificar el comportament del temporitzador i de les alertes sense haver d’esperar una hora real entre dosis. Per això, al fitxer sketch.js he implementat un mode prova:
-
L’usuari pot configurar les “Hores entre dosis” a
0mitjançant la finestra de configuració. -
Si el valor llegit des de
localStorageés0, l’aplicació interpreta que es tracta d’un mode debug i força l’interval intern a 1 minut (60 * 1000 ms) en lloc d’1 hora (60 * 60 * 1000 ms). -
Això permet testejar totes les fases de l’alarma (pre-avís, estroboscopi, vibració i notificació) de forma ràpida, mantenint la configuració normal per a l’ús mèdic real.
D’aquesta manera puc comprovar el flux complet de l’app (UI p5.js + hardware + notificacions) sense haver d’esperar períodes llargs.
2.5. Resiliència i Gestió del Segon Pla
Per garantir que l’usuari rebi l’avís de la gota encara que el sistema operatiu “congeli” el JavaScript de l’App per estalviar bateria (comú en Android), he implementat una Estratègia Híbrida de Resurrecció:
-
Alarmes Exactes Nativament: He configurat els permisos SCHEDULE_EXACT_ALARM i USE_EXACT_ALARM a l’AndroidManifest. Això permet que el sistema natiu de notificacions desperti el dispositiu en el moment precís, independentment de l’estat de l’App.
-
Sincronització al Resume: Al fitxer main.js, he programat un listener del cicle de vida (resume). Quan l’usuari obre l’App des de la notificació, el codi compara immediatament el Timestamp actual amb l’hora objectiu emmagatzemada a localStorage.
-
Feedback Immediat: Si el temps s’ha esgotat durant el segon pla, l’App reactiva instantàniament les funcions de hardware (flash estroboscòpic i vibració) i l’estat d’alarma visual a p5.js, garantint una transició fluida i sense errors entre el segon pla i el primer pla.
3. Decisions Preses i Solució de Problemes
Un aspecte clau del disseny va ser la interpretació visual de les dades (Data Viz). No volia que l’usuari hagués d’interpretar xifres fredes. Per això, vaig implementar una lògica reactiva: si l’API d’Open-Meteo detecta una humitat inferior al 35% (llindar de risc per ull sec), l’app canvia automàticament el text d’estat a “Ambient SEC (Risc sequetat ocular)” i tenyeix la gota central de color ambre/groc. Això ofereix un diagnòstic visual instantani sense necessitat de lectura atenta.

Un altre repte va ser la gestió de noms de ciutat llargs al canvas. Ciutats com ” Colinas del Campo de Martín Moro” es tallaven visualment, així que al sketch.js vaig optar per utilitzar caixes de text delimitades que permeten el salt de línia automàtic (text wrapping), mantenint el text sempre centrat i llegible.

La latència de les APIs també va aparèixer com a problema: com que es fan dues crides externes (BigDataCloud + Open-Meteo), el temps de resposta podia allargar-se. Per evitar la sensació que l’app s’havia penjat, vaig afegir un estat intermedi “Cercant GPS…” a l’objecte global window.eyeWeather, de manera que l’usuari rep feedback immediat mentre arriba la informació.
Pel que fa als permisos de GPS, a Android 13 l’app fallava si no tenia ACCESS_FINE_LOCATION declarat al AndroidManifest.xml. A més d’afegir el permís, vaig introduir un timeout de 10 segons a la crida de geolocalització per evitar bloquejos en entorns amb mala cobertura de satèl·lits.
També es va detectar un crash relacionat amb les notificacions per falta del recurs gràfic de la icona. La solució va passar per generar el recurs ic_stat_dropapp dins de res/drawable i referenciar-lo a la configuració de LocalNotifications.schedule(), assegurant que Android disposa sempre d’una icona vàlida per mostrar l’avís.
Finalment, per garantir que l’alerta fos efectiva en entorns sorollosos o quan el dispositiu no és a la vista, es va decidir combinar tres canals: l’efecte visual de la llanterna i el fons “d’alarma”, la vibració repetida amb @capacitor/haptics i la notificació de sistema. Aquesta decisió reforça l’ús del maquinari natiu i millora l’accessibilitat general de l’app.
Objecte global:
window.eyeWeather: Sincronitza dades API entre main.js (productor) i sketch.js (consumidor), amb estats UX (“Cercant GPS…”, “Mode Offline”).


Finestra flotant de la configuració i cerca de GPS
4. Pròxims Passos: Publicació i Beta
Per convertir aquest MVP en una versió final preparada per a la Google Play Store, els passos següents són:
-
Optimització de Processos en Background: Tot i que l’estratègia actual de resurrecció és efectiva, el següent pas tècnic seria implementar @capacitor/background-runner. Això permetria que l’App seguís consultant l’API meteorològica de forma invisible per a l’usuari, actualitzant el diagnòstic climàtic fins i tot si l’App no s’obre en tot el dia.
-
Signatura de l’Aplicació (Keystore): Cal generar un Android App Bundle (.aab) signat criptogràficament des d’Android Studio. Aquest és un requisit indispensable de seguretat per identificar-me com a desenvolupador oficial davant de Google.
-
Privacitat i GDPR: Atès que l’App fa ús de geolocalització precisa i APIs de tercers (BigDataCloud/Open-Meteo), és necessari redactar i enllaçar una Política de Privacitat formal. En ella s’ha d’especificar que les coordenades GPS s’utilitzen de manera efímera i no s’emmagatzemen en servidors externs.
-
Proves en més dispositius: Tot i que el funcionament en Android 15 (Xiaomi) és correcte, caldria testejar l’App en versions anteriors d’Android (11 i 12) per assegurar que la gestió de permisos natius es comporta de manera consistent.
5. Referències i Recursos Utilitzats
Serveis de Dades (APIs)
-
Open-Meteo Weather API: Servei de dades meteorològiques (Humitat Relativa) sota llicència Creative Commons (CC BY 4.0). [https://open-meteo.com/]
-
BigDataCloud Reverse Geocoding: API client-side per a la transformació de coordenades a toponímia. Versió gratuïta per a desenvolupadors. [https://www.bigdatacloud.com/]
Tecnologies i Llibreries
-
Nucli: Capacitor (v6) per a la compilació nativa Android.
-
Visualització: p5.js (Processing Foundation) per al renderitzat del canvas i animacions.
-
Plugins de Sistema:
-
@capacitor/geolocation: Accés al GPS natiu.
-
@capacitor/local-notifications: Gestió d’avisos en segon pla.
-
@capacitor/haptics: Motor de vibració del dispositiu.
-
@capawesome/capacitor-torch: Control del maquinari de la llanterna (Flash).
-
Repositori del Projecte
-
Codi Font: Repositori públic disponible a GitHub: [https://github.com/Javime85/DropApp-PR2]


Aquest és un espai de treball personal d'un/a estudiant de la Universitat Oberta de Catalunya. Qualsevol contingut publicat en aquest espai és responsabilitat del seu autor/a.