====== Cordova ====== Apache Cordova (inicialment PhoneGap) son unes llibreries per al desenvolupament d'apps mòbils utilitzant [[JavaScript]], [[HTML]] i [[CSS]]. La principal avantatge és que permet utilitzar un llenguatge web genèric (JS) i compilar l'app per a diverses plataformes diferents, d'entre les que destaquem les majoritàries [[Android]] i [[iOS]]. El seu funcionament es basa en tenir una aplicació nadiua amb una simple WebView que ocupa tota la pantalla sencera. Implementant codi JavaScript dins la WebView ens permetrà controlar la interfície gràfica. Per accedir a les funcionalitats pròpies del dispositiu mòbil disposarem de certes llibreries (càmera, filesystem, acceleròmetre, etc). {{ cordova.png?400 }} {{tag> #Dam #DamMp08 #DamMpDual #DawMpDual Android iOS Cordova HTML JavaScript }} ===== Introducció i requisits ===== Per poder seguir aquest article se suposa que has de tenir coneixements de [[JavaScript]], [[HTML]] i [[CSS]]. Si tens coneixements molt bàsics també t'interessaria [[https://www.w3schools.com/js/js_htmldom.asp|repassar els conceptes del DOM o Document Object Model]]. Abans de començar a treballar amb Cordova caldrà que tinguem instal·lats els entorns de desenvolupament: llegeix [[Java]] i [[Android]] primerament. Per instal·lar Cordova necessites un bon plegat d'eines. De forma general necessitaràs: - **Android Studio** que ens facilitarà les eines de l'Android SDK. - **Java JDK** per compilar la part base de la app (en Java). Ull amb les versions: >11 i < =18 - **NodeJS** que ens facilitarà el gestor de paquets NPM amb el què podrem instal·lar Cordova - **Gradle** en la darrera versió. Segueix les indicacions de l'article [[Cordova install]] per posar a punt tot l'entorn. \\ ====== Construint una App amb Cordova ====== Si ja tens l'entorn preparat, es tracta de desenvolupar una app com si es tractés d'una aplicació web (com si fos pel //browser//) amb JavaScript. Podem aplicar llibreries típiques com jQuery o AngularJS, i a més disposarem d'algunes mes per a poder accedir al //hardware// del mòbil. \\ ===== Primera App ===== La seqüència típica per iniciar un projecte és com s'indica a continuació: - Crear projecte: $ cordova create proj1 - Entrar al directori del projecte: $ cd proj1 - Afegir plataformes (android/ios/browser): $ cordova platform add android - Compilar per Android: $ cordova build android - Arrencar el projecte a l'emulador: $ cordova run android Tingues en compte que per desenvolupar resulta molt còmode treballar amb la ''platform browser'' i les eines de desenvolupador del propi navegador. Mira l'article [[Introducció Javascript]] i realitza l'exercici de //login// i registre d'usuaris dins d'una aplicació Cordova. \\ ===== Afegint jQuery ===== jQuery és una biblioteca de codi que opera sobre el DOM de JavaScript i facilita moltes operacions. Podeu aprendre més sobre [[jQuery]] <- en aquesta wiki. \\ ==== Exercici Tasklist v0.1 ==== Implementarem una //tasklist// clàssica. Inicialment farem una versió senzilla que permeti afegir elements i prou. Tasklist: - Inicia un projecte Cordova. - Crea una llista de tasques d'exemple amb un senzill OL o UL. - Implementa el **botó d'afegir tasca**. * Necessitaràs conèixer [[jQuery|com utilitzar el DOM amb jQuery]]. * Afegir elements. El text el podem agafar amb: - El [[https://developer.mozilla.org/es/docs/Web/API/Window/prompt|prompt del navegador]] (és el més senzill). - Un [[https://jqueryui.com/dialog/#modal-form|modal form de jQuery UI]], que quedarà més vistós :) * Tingues en compte la documentació oficial de la jQuery UI. Cal fer-ho correctament per tal que ens apliqui els estils CSS adequats quan insertem un nou element. \\ ==== Exercici Tasklist v0.2 ==== {{tasklist2.png?200}} {{tasklist1.png?200}} Continua de l'anterior exercici Tasklist v0.1: - Implementa un **botó Elimina** dins de cada element de la llista que esborri aquell element. Convé fer-ho en la pròpia creació de l'element ''jQuery''. * Enlloc d'assignar la //callback// en el ''onclick="elimina()"'' del botó, assigneu-la d'aquesta manera, que és la bona pràctica recomanada en general:$(".boto_eliminar").click(elimina);On ''elimina'' és la funció que cridarem per esborrar l'element. * Per eliminar un element del DOM caldrà saber **//quin// element està cridant (caller)**. [[https://stackoverflow.com/questions/945201/how-to-get-the-caller-event-and-dom-element-id|Aquest post explica com obtenir el caller]]:function elimina(event){ var caller = event.target || event.srcElement; console.log( caller ); } * Busca la funció jQuery per a eliminar un element. * Busca la funció jQuery per a accedir al pare d'un element per poder esborrar el ''
  • '' (si no, esborraràs el botó i prou). - Implementa un **botó Edita** dins de cada element de la llista. - La funció d'edició la podem realitzar de diverses formes: - La més elegant: utilitzar el ''Dialog'' de jQuery UI per demanar l'usuari que entri el nou text. - Buidant l'element ''
  • '' que volem editar i creant a dins un '''' i botó de OK com a la imatge. Després caldrà agafar el text i ficar-ho dins l'element de la tasca. - Demanant amb el //prompt//, que de fet és la opció més senzilla (però també la menys vistosa). - És probable (depenent de la implementació que hagis fet) que quan vulguis editar el text de la tasca (està dintre d'un ) et trobis que et posa també el text dels elements fills (botons d'esborrar i/o editar). [[https://stackoverflow.com/questions/3442394/using-text-to-retrieve-only-text-not-nested-in-child-tags|En aquest post s'explica una solució molt eleganṭ]] per eliminar el text dels botons i deixar el de la task sola. \\ ===== Local Storage ===== ''localStorage'' és un objecte //built-in// de [[JavaScript]] dintre dels //browsers// que ens permet emmagatzemar dades persistents en una BD interna del tipus //KEY-VALUE//. Les aplicacions web no estaven pensades per emmagatzemar dades, només per ser visualitzades, i en tot cas per interaccionar amb el servidor. Per això aquest recurs va trigar en implementar-se en els navegadors. A més, cal tenir en compte que, per raons de seguretat, l'accés al disc dur en un navegador sempre s'ha limitat molt (JavaScript no té accés al //filesystem// per seguretat de les dades de l'usuari). És molt senzill d'utilitzar, al capdavall una DB //key-value// no deixa de ser un diccionari. En principi en tindreu prou amb els mètodes: localStorage.setItem("clau","valor"); var valor = localStorage.getItem("clau"); Mira't la [[https://www.w3schools.com/jsref/prop_win_localstorage.asp|doc de W3Schools]] per mes detalls. ==== Objectes a localStorage ==== ''localStorage'' només emmagatzema //strings//. **Si volem guardar objectes complexes, com diccionaris o arrays, caldrà utilitzar algun mètode de serialització**. Les funcions típiques que s'empren per a això son: - [[https://www.w3schools.com/js/js_json_stringify.asp|JSON.stringify()]] ens transforma l'objecte en un string per poder ser insertat al ''localStorage''. - [[https://www.w3schools.com/js/js_json_parse.asp|JSON.parse()]] ens permet recuperar un string serialitzat i el restitueix com a objecte. \\ ==== Exercici Tasklist v0.3 ==== Aplicarem tot l'exposat fins aquí en el següent exercici: Fes la //tasklist// persistent utilitzant el ''localStorage'', tal i com s'explica més amunt. \\ ===== Seguretat en Cordova ===== Cordova ens deixa crear qualsevol pàgina HTML amb el codi que ens sembli bé. Però si ens fixem en la plantilla al crear de zero un projecte, ens trobem algunes particularitats que cal parar atenció, sobretot el **tag META amb el Content-Security-Policy**: Aquest tag META bàsicament ens limita certes accions de l'HTML que podrien fer vulnerable el nostre codi. Entre d'altres, no ens deixa connectar a webs externes si no les validem dins del tag. I tampoc ens permet posar codi //inline//. Típicament podríem fer una aplicació en JavaScript en una pàgina HTML i podríem posar botons que cridin a funcions del nostre codi amb un //onclick// (el què coneixem per crida //inline//). Doncs bé, Cordova no ens deixa fer //inlines// com aquest: Aquesta solució funcionaria en una web normal, però **no funciona si posem el tag META de Content-Security-Policy**: Forma de solventar-ho: fer els botons més senzills, sense //onclick// (però amb //id//): I després, en el nostre ''index.js'', ja li afegim el //onclick// per cridar la funció ''addElem'': document.getElementyById('boto1').onclick(addElem); $('#boto1').click(addElem); \\ ===== Inicialitzacions ===== ==== Inicialitzacions HTML ==== Abans d'executar codi JavaScript sol convenir esperar a que estigui carregat tot el document. Tenim 2 estratègies bàsiques: - Posar el tag '' La línia del HTML que carrega ''cordova.js'' dispararà un event "ondeviceready" propi de Cordova. Al nostre codi podem començar a realitzar accions quan es rebi aquest event: document.addEventListener('deviceready', onDeviceReady, false); function onDeviceReady() { // aquí ja podem fer accions amb la càmera, arxius, etc. } Fes una **versió v0.4 de la Tasklist** aplicant el tag META de seguretat i les inicialitzacions pertinents. \\ ===== Framework CSS : MaterializeCSS ===== MaterializeCSS és un //framework// CSS per crear interfícies atractives i //responsives//. És molt important comptar amb una interfície gràfica clara i agradable si volem que la nostra app tingui una bona experiència d'usuari (UX). La //responsiveness// és la capacitat de les interfícies d'amotllar-se a diferents grandàries de pantalles, per exemple, un mòbil, una tablet, depenent de si estan en vertical (//portrait//) o apaïsades (//landscape//). És una de les principals utilitats dels //frameworks// CSS. Segueix aquest [[Materialize|tutorial sobre Materialize]] per iniciar un nou projecte Cordova amb aquesta eina. \\ ===== Utilitzant APIs amb AJAX ===== Treballar aplicacions en HTML+JS accedir a dades externes és molt còmode utilitzant crides asíncrones amb AJAX. Una **API web, també conegut com a //web service//** és una interfície via web per demanar dades, així que es podria considerar una crida remota, d'aquí que en diguem API, com les llibreries de programació. Explorarem la API externa per accedir a dades de www.musicbrainz.org ==== Queries a la API de musicbrainz.org ==== musicbrainz.org és una web amb informació sobre grups musicals que disposa d'una API pública o //web service//. Ens anirà molt bé per fer els primers exemples d'accés a dades remotes. Documentació de musicbrainz: * [[https://musicbrainz.org/doc/Development/XML_Web_Service/Version_2|Doc oficial API Musicbrainz]]. Els exemples ens venen en XML. * [[https://musicbrainz.org/doc/Development/JSON_Web_Service|Instruccions per obtenir resultats en format JSON]]. === Exemples === Obtenir els **resultats en XML** dels grups amb "queen" al seu nom: curl https://musicbrainz.org/ws/2/artist?query=queen Per obtenir els **resultats en format JSON** cal afegir un //header// a la //request// de la query, per exemple: curl https://musicbrainz.org/ws/2/artist?query=queen -H "Accept:application/json" Si volem mostrar els resultats de forma mes clara en la //shell//, podem utilitzar una **eina per tenir una correcta indentació**: curl ...url_query... | python -m json.tool Un altre exemple per cercar la informació d'un artista concret. De qui estem parlant?... curl -H 'Accept:application/json' http://musicbrainz.org/ws/2/artist/5b11f4ce-a62d-471e-81fc-a69a8278c7da | python3 -m json.tool ==== AJAX amb jQuery ==== Aquestes mateixes les podem demanar des d'un document/aplicació HTML+JS amb crides asíncrones, el que solem conèixer per AJAX. Utilitzarem [[http://api.jquery.com/jquery.ajax/|AJAX amb jQuery]] que ens ajudarà a simplificar el codi. Els exemples estan a la mateixa documentació, però us poso aquí un d'essencial per fer la crida i que ens retorni un objecte JSON de la API de musicbrainz.org : $.ajax({ method: "GET", url: "https://musicbrainz.org/ws/2/artist?query=queen", dataType: "json", // necessitem això pq ens retorni un objecte JSON }).done(function (msg) { for(let item in msg.artists) { console.log(msg.artists[item]); // aquí caldría fer mes coses, of course... // ... }; }).fail(function () { alert("ERROR"); }); Cal tenir en compte que les URLs a les que cridis amb AJAX han de tenir habilitada la capçalera de CORS (Cross-Origin Resource Sharing). Les APIs solen tenir-ho habilitat. Totes les APIs públiques ho tenen activat. Pots veure una demo i explicació de com funciona a http://ieti.cat/cors/ [[https://ahorasomos.izertis.com/solidgear/lidiando-con-el-cors/|Aquí s'explica els orígens de CORS]] força correctament. Dins la nostra aplicació cal autoritzar l'accés a les dades externes del domini en qüestió. Si no ho fem, ens apareixerà un error a la consola similar a: Content Security Policy: Els paràmetres de la pàgina han blocat la càrrega d'un recurs a https://musicbrainz.org/ws/2/ («default-src»). Per tal d'autoritzar la petició, caldrà que a la capçalera META amb la política de seguretat validem la font de dades als següents apartats: * ''data: https://musicbrainz.org'' : URLs de les APIs de les que vulgui rebre info. * ''img-src: *'' : ens permetrà carregar imatges de qualsevol web. ==== Exericici ==== Rock band searcher: - Crea un nou projecte Cordova - Implementa una plantilla amb [[Materialize]]. - Accedeix a la API de musicbrainz.org i fes una recerca de grups i de cançons, tal i com s'explica més amunt. - Primer prova-ho amb ''curl'' - Després implementa-ho en el HTML del projecte Cordova - Mostra els resultats de la cerca en una llista HTML ==== Public API lists ==== [[https://github.com/public-apis/public-apis|En aquest repositori hi ha un llistat de public APIs]] per poder fer aplicacions amb les seves dades. [[https://api.spaceflightnewsapi.net/v4/docs/|Mira't la documentació d'aquesta de space news]] ja que és força clara per començar. Fes-li un cop d'ull i troba el teu //hobby// en una API! \\ ==== Exercici ==== Aprofitant el codi que hem fet amb l'API de musicbrainz.com , fes una nova app que utilitzi la [[https://api.spaceflightnewsapi.net/v3/documentation|API de spaceflight news]]: * Al **tab1** posa un botó que carregui una llista de notícies (mostrant només el títol). * Utilitza les [[https://materializeweb.com/collections.html|Collections de MaterializeCSS]] per donar-li una formatació adequada. Cada ítem de la llista ha de tenir un //link// que carregui les dades complertes al tab2 i hi salti al clicar. * Per saltar al //tab2// necessitaràs consultar la [[https://materializeweb.com/tabs.html|doc dels tabs de Materialize i anar a la secció "Methods"]] i cridar el mètode "select". Segons la doc es faria així: var elems = document.getElementsByClassName('tabs'); var instance = M.Tabs.getInstance(elems[0]); instance.select('test-swipe-2'); * Al **tab2** posa la notícia complerta amb la imatge. Formata-la adequadament. Probablement hauràs de fer una altra crida a la API per carregar els detalls de l'article concret, cercant-lo per ID. * Poseu //links// a les notícies originals. El problema és que salta dins de la pròpia app. En realitat el què voldrem és que l'obri al navegador del mòbil, això ho podem aconseguir així: Notícia original. * Al **tab3** carregarem la llista de notícies amb [[https://materializeweb.com/cards.html|Materialize Cards]] o bé utilitzant la [[https://materializeweb.com/grid.html|Grid de Materialize amb containers, row i col]], l'exemple "Example Promotion Table" ens anirà molt bé. \\ ===== Plugins ===== * **cordova-plugin-camera**: permet capturar fotos amb la càmera del dispositiu mòbil (o del //browser//). * [[https://github.com/apache/cordova-plugin-camera|cordova-camera-plugin doc a Github.com]] * [[https://www.npmjs.com/package/cordova-plugin-camera|cordova-camera-plugin doc a NPM]] * [[https://cordova.apache.org/docs/en/latest/reference/cordova-plugin-camera/|cordova-camera-plugin doc a cordova.org]] DEPRECATED * [[https://github.com/apache/cordova-plugin-camera/issues/761|Solució a l'error "Not allowed to load..."]]. Necessitaràs també el [[https://cordova.apache.org/docs/en/latest/reference/cordova-plugin-file/index.html|file plugin]]. * **cordova-plugin-qrscanner**: per capturar codis QR * [[https://github.com/bitpay/cordova-plugin-qrscanner|doc oficial de cordova-plugin-qrscanner]] * Necessitaràs [[https://github.com/bitpay/cordova-plugin-qrscanner/issues/356|parxejar el codi del plugin]] pq funcioni amb Cordova >= v10.0 Pots explorar-ne més a la [[https://cordova.apache.org/docs/en/latest/#plugins|documentació de plugins de Cordova]]. \\ ====== Depuració (debug) ====== Per depurar el més fàcil és utilitzar al nostre codi missatges via la consola del //browser//: console.log("missatge...") Per poder mostrar aquests missatges dependrà de la plataforma que utilitzem: ===== Browser ===== * Firefox: CTRL + SHIFT + K * Chrome: F12 (inspector) -> Pestanya "Console". ===== Android ===== [[https://developer.chrome.com/docs/devtools/remote-debugging/webviews/|Chrome disposa d'eines de connexió remota]]. La consola de la WebView que utilitza la nostra app Cordova és accessible des de Chrome excecutat a la màquina host. Obrim Chrome i anem a: chrome://inspect/#devices Des d'aquí hauriem de veure el nostre //device// i accedir-hi amb un ''inspect''. S'obrirà una pantalla on veurem la consola de JavaScript. \\