tag:blogger.com,1999:blog-23480721732081352602024-03-14T11:51:20.783+01:00Anomena i desa...Informació tecnològica en català, anomenada i desada al bloc. Per a mi, per a tothom.Jordihttp://www.blogger.com/profile/10269271669040475159noreply@blogger.comBlogger168125tag:blogger.com,1999:blog-2348072173208135260.post-80847485112687741782023-02-26T19:11:00.000+01:002023-02-26T19:11:30.657+01:00chatGPT : qué és i què pot fer ?<p>Segurament haureu sentit a parlar de <a href="https://chat.openai.com/chat" rel="nofollow" target="_blank">chatGPT</a>, una AI (intel·ligència artificial) que es pot utilitzar lliurament. He tingut ocasió de provar-la i crec que pot ser molt útil com a eina d'ajuda als programadors, però vull que sigui ella mateixa qui es presenti.</p><p>Si voleu provar-la, simplement connecteu a <a href="https://chat.openai.com/" rel="nofollow" target="_blank">https://chat.openai.com/</a>. Si no teniu un compte de Microsoft, el primer cop caldrà crear-ne un (jo vaig utilitzar les credencials de <a href="https://github.com/" rel="nofollow" target="_blank">GitHub</a>). En tot cas, us demanarà un telèfon per verificar les vostres dades.</p><p>Un cop fet, s'arriba a una senzilla pàgina i ja podeu preguntar el que vulgueu :</p>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiuA_6kOxWYRctjR6baeEyWzEL2oCdJEENa9A9vAE_C5GESPAfAWHgOVt1TabtS_Ev-fbROqp2IFiEJJBwLsQY7e1H3rrJLsWx2h1mrLVPCcEDAuCnxGRhOrPfXWxGlFiwbAaZFFf-ykzqE5oM1mCsErsU5oJuFKtWtuBZUxa70S_jJDgMf4h-cOWApzw/s2422/chatGPT-pantalla.png" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="1322" data-original-width="2422" height="349" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiuA_6kOxWYRctjR6baeEyWzEL2oCdJEENa9A9vAE_C5GESPAfAWHgOVt1TabtS_Ev-fbROqp2IFiEJJBwLsQY7e1H3rrJLsWx2h1mrLVPCcEDAuCnxGRhOrPfXWxGlFiwbAaZFFf-ykzqE5oM1mCsErsU5oJuFKtWtuBZUxa70S_jJDgMf4h-cOWApzw/w640-h349/chatGPT-pantalla.png" width="640" /></a></div>
<br /><p><br /><br /></p><p>I dit això, us transcric literalment la conversa que hem tingut:<br /><br /></p>
<p><b>[Jo]: Hola, entens el català ?</b></p>
<p><i>[chatGPT]: Sí, entenc català. Com puc ajudar-te?</i></p>
<p><b>No ho tinc molt clar. Qué pots fer ?</b></p>
<p><i>Com a assistent virtual, puc ajudar-te en diverses coses, com ara:</i></p><p></p><ul style="text-align: left;"><li><i>Respondre a preguntes i proporcionar informació sobre diferents temes. </i></li><li><i>Oferir-te suggeriments i consells sobre diversos assumptes. </i></li><li><i>Realitzar tasques com ara programar reunions, establir recordatoris i enviar correus electrònics.</i></li><li><i>Fer càlculs i conversió d'unitats, com ara convertir monedes o mesures. </i></li><li><i>Realitzar traduccions a diferents idiomes, entre ells català. </i></li></ul><i>Fes-me saber en què puc ajudar-te específicament i intentaré ajudar-te de la millor manera possible.</i><p></p>
<p><b>Pots escriure una presentació sobre tu, d'unes 200 paraules que pugui utilitzar en un blog ?</b></p>
<p><i>Sí, és clar que sí. Aquí tens una presentació de 200 paraules sobre mi:</i></p><p><i>Sóc ChatGPT, un assistent virtual creat per OpenAI. Em considero una eina útil per a totes aquelles persones que necessiten ajuda en diferents àmbits. Amb mi pots conversar en diversos idiomes, com ara català, espanyol, anglès, francès, entre altres.</i></p><p><i>La meva funció principal és respondre a preguntes i proporcionar informació sobre diferents temes. Em baso en una extensa base de dades per oferir respostes precisos i actualitzades. També puc fer càlculs i conversió d'unitats, com ara convertir monedes o mesures.</i></p><p><i>A més, em considero un assistent personal virtual que pot realitzar tasques com programar reunions, establir recordatoris i enviar correus electrònics. Em preocupo per la privacitat dels meus usuaris, i les converses amb mi es mantenen segures i confidencials.</i></p><p><i>En resum, sóc una eina útil i versàtil que pot ajudar-te en molts aspectes de la teva vida quotidiana. Si tens alguna pregunta o necessites ajuda en alguna cosa, no dubtis a preguntar-me. Estic aquí per ajudar-te.
</i></p>
<p><b>Gràcies ! Com puc descarregar aquesta conversa per inserir-la en el bloc ?</b></p>
<p><i>Pots copiar la conversa i enganxar-la en el teu blog. També pots fer servir una captura de pantalla de la conversa i afegir-la al teu bloc com una imatge. Si vols descarregar la conversa com a document, depèn del programa que estiguis utilitzant per al bloc, però normalment pots seleccionar tota la conversa, copiar-la i enganxar-la en un document de text o processador de textos com Microsoft Word o Google Docs i desar-lo en el teu ordinador.
</i></p>
<p><br /><br />Ho hem deixat aquí, que per una primera impressió ja serveix. Tot i que segurament en tornaré a parlar en el futur.</p><p>Si teniu curiositat, no deixeu de provar-la i comenteu-me què us ha semblat.</p>
Jordihttp://www.blogger.com/profile/10269271669040475159noreply@blogger.com0tag:blogger.com,1999:blog-2348072173208135260.post-76052321277378326192022-01-30T18:19:00.001+01:002022-01-30T18:19:21.786+01:00Error: Chromium no carrega a Ubuntu 20.04 [Solucionat]<p>Després de solucionar el <a href="https://anomenaidesa.blogspot.com/2022/01/error-shutdown-waiting-for-process.html">problema de l'apagada del meu ordinador</a>, m'he trobat que el navegador Chromium no arrencava correctament. Hi havia processos corrent, però cap finestra :-(</p><p>He buscat per internet i els motius poden ser diversos, així que he optat per la solució clàssica de d'informàtica: suprimir i tornar a instal·lar. En l'Ubuntu 20.04 Chromium s'instal·la via <a href="https://snapcraft.io/">paquets snap</a>, així que les comandes per fer-ho son les següents :</p>
<pre class="prettyprint">$ sudo snap remove chromium
$ sudo snap install chromium</pre>
<p>I, com sol passar, a partir d'aquí tot ha tornat a la normalitat !</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjAlt6FRQXP6S_2uvPVhJ84BGsH7grepYj8e3bzdkov2e5rsjXD-RT5HK7oB_aimRA-xA6Ywyc9SsKTS_79Y01L-UI77DOdoIltPfdAAIyZCq-yU0CJMvjPmIB59oep7vGZtzWi4hBZUzDR3Z4kTog1UtCSIxTgCg7KF3z2Id7dpKn3CxGqJNdpKQt9Uw=s1376" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="1108" data-original-width="1376" height="517" src="https://blogger.googleusercontent.com/img/a/AVvXsEjAlt6FRQXP6S_2uvPVhJ84BGsH7grepYj8e3bzdkov2e5rsjXD-RT5HK7oB_aimRA-xA6Ywyc9SsKTS_79Y01L-UI77DOdoIltPfdAAIyZCq-yU0CJMvjPmIB59oep7vGZtzWi4hBZUzDR3Z4kTog1UtCSIxTgCg7KF3z2Id7dpKn3CxGqJNdpKQt9Uw=w640-h517" width="640" /></a></div><br /><p><br /></p>Jordihttp://www.blogger.com/profile/10269271669040475159noreply@blogger.com0tag:blogger.com,1999:blog-2348072173208135260.post-15136728821981393472022-01-30T18:05:00.000+01:002022-01-30T18:05:25.884+01:00Error: shutdown waiting for process contained-shim [Solucionat]<p> Fa un temps que arrossegava un problema amb el meu Ubuntu 20.04: es quedava penjat a l'apagar-lo amb un error similar a (perdoneu si no és exacte, l'escric de memòria):</p>
<pre class="prettyprint">shutdown: waiting for process contained-shim to terminate</pre>
<p>El problema és que es quedava "penjat" durant uns minuts, així que normalment l'acabava apagant forçant-lo (mantenint el botó d'apagat durant uns segons). A part que és emprenyador, sempre hi ha el risc de perdre dades...</p><p>Total, que avui he tingut un moment per mirar de corregir-ho. En principi, ho he relacionat amb els meus experiments amb <a href="https://anomenaidesa.blogspot.com/search/label/Docker">Docker</a>, així que l'he desinstal·lat. Malauradament, això no ha corregit el problema.</p><p>Llavors he recordat que també havia fet petites coses amb <a href="https://kubernetes.io/">Kubernetes</a> i, efectivament, aquest era el culpable. Com que ara mateix no el necessito, la solució ha estat eliminar-lo:</p>
<pre class="prettyprint">snap remove microk8s</pre>
<p>I problema resolt: el PC s'apaga ens uns segons un altre cop !</p>Jordihttp://www.blogger.com/profile/10269271669040475159noreply@blogger.com0tag:blogger.com,1999:blog-2348072173208135260.post-45003304239438203522021-05-21T00:54:00.000+02:002021-05-21T00:54:09.707+02:00Wifi ON/OFF amb un botó a RouterOS (Mikrotik hAP ac^2)<p>Si heu seguit el meu bloc, potser sabreu que m'agrada apagar la xarxa sense fils quan ningú l'utilitza, ja sigui amb un botó (<a href="http://anomenaidesa.blogspot.com/2013/02/wifi-onoff-amb-un-boto-gargoyle-tp-link.html">TP-Link TL-WR14032ND</a> o <a href="http://anomenaidesa.blogspot.com/2013/07/tp-link-wdr3600-wifi-onoff-amb-un-boto.html">TP-Link WDR3600</a>) o automàticament per temps si no hi ha ningú connectat (<a href="http://anomenaidesa.blogspot.com/2013/07/wdr3600-apagar-la-xarxa-sense-fils-quan.html">TP-Link WDR3600</a>). En tots aquests casos, l'encaminador funcionava amb un sistema <a href="http://anomenaidesa.blogspot.com/search/label/Openwrt">Openwrt</a>.</p><p>El meu nou encaminador (<i>router</i>) és un model <a href="https://mikrotik.com/product/hap_ac2">Mikrotik hAP ac^2</a> que treballa amb RouterOS (vegeu les <a href="http://anomenaidesa.blogspot.com/2021/05/mikrotik-hap-ac2-amb-routeros-primeres.html">primeres impressions</a>) i lògicament, no podia ser menys i hi havia d'afegir aquesta funcionalitat.</p><p>L'encaminador disposa d'un botó (programable) en la part superior, així que anem a veure com ho podem fer. En primer lloc, creem un <i>script</i> per commutar l'estat de la xarxa sense fils. El codi pot ser similar a:</p><pre class="prettyprint lang-sh">:log info message=("> Mode button was pressed");
:local i
:if ( [/int wir get 0 disabled ] = true ) do={
:log info message=("--> Wifi ON");
:foreach i in= [ /int wir find ] do={ :int wir enable $i };
} else={
:log info message=("-- > Wifi OFF");
:foreach i in= [ /int wir find ] do={ :int wir disable $i };
}</pre><p>L'hem d'afegir usant el menú "<i>System -> Scripts -> Add New</i>", donant-li un nom i verificant les prioritats tal com es veu en la imatge:</p><div class="separator" style="clear: both; text-align: left;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhGEKoK1ElTMXHpy35B12zMe29bNOvtjljhxvimAnpmov1o2rW8N_i7307j-MdSBiOz3nfTvkxRwlntXWZgAappOuqjtBqmRKiNOCXE0JOp3Ltqpap7sMAFjZyo8yXIPHwcqc8XpqIOJO7b/s1696/Script_wifi_on_off.png" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="1382" data-original-width="1696" height="522" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhGEKoK1ElTMXHpy35B12zMe29bNOvtjljhxvimAnpmov1o2rW8N_i7307j-MdSBiOz3nfTvkxRwlntXWZgAappOuqjtBqmRKiNOCXE0JOp3Ltqpap7sMAFjZyo8yXIPHwcqc8XpqIOJO7b/w640-h522/Script_wifi_on_off.png" width="640" /></a></div><br /><p>En aquest moment podem executar-lo amb "<i>Run Script</i>" i verificar el seu bon funcionament: la xarxa sense fils s'ha d'apagar/engegar amb cada execució, i un missatge ha d'aparèixer si accedim al menú "<i>Log</i>". </p><p>Ara ja només queda associar-lo al botó. Per això accedim al menú "Terminal" (o connectem per ssh) i teclegem la comanda següent:</p><pre class="prettyprint lang-sh">/system routerboard mode-button set on-event=wifi_on_off enabled=yes</pre><p>Amb això tenim el botó per apagar/engegar la xarxa sense fils configurat! Ja em direu què us sembla...</p><p><br /></p>Jordihttp://www.blogger.com/profile/10269271669040475159noreply@blogger.com0tag:blogger.com,1999:blog-2348072173208135260.post-51225095980435824262021-05-17T20:29:00.002+02:002021-05-17T20:31:06.166+02:00Mikrotik hAP ac^2 amb RouterOS: primeres proves<p> Ja fa un temps que pensava canviar l'encaminador (<i>router</i>) de casa. Les últimes millores en la línia (ara amb 600MB simètrics) han fet que el meu antic <a href="https://anomenaidesa.blogspot.com/search/label/TP-Link%20WDR3600">TP-Link WDR3600</a> amb <a href="https://anomenaidesa.blogspot.com/search/label/Openwrt">Openwrt/LEDE</a>, no acabi <a href="https://anomenaidesa.blogspot.com/2020/06/openwrt-lede-1907-amb-software-flow.html">de treure'n tot el profit possible</a>.</p><p>Buscava un encaminador amb les següents característiques :</p><p></p><ul style="text-align: left;"><li>Prou potent per aconseguir la màxima velocitat de la línia.</li><li>Fiable.</li><li>Que pogués suportar Openwrt (mínim 8MB/64MB de memoria Flash/RAM respectivament).</li><li>Que permetés algun tipus de bloquejador d'anuncis (quan t'hi acostumes, no hi ha marxa enrere).</li><li>No molt car.</li></ul>De fa temps, tenia molt bones referències dels encaminadors de la marca <a href="https://mikrotik.com/">Mikrotik</a>, més orientats als sistemes professionals que als domèstic, però fins ara els havia descartat per què eren cars, funcionaven amb un sistema propi (RouterOS) i eren complexos de configurar.<p></p><p>Doncs bé, per fer-ho curt, m'he decidit a provar el model <a href="https://mikrotik.com/product/hap_ac2">Mikrotik hAP ac2</a> :</p><p></p><ul style="text-align: left;"><li>És un quàdruple-core, amb potència de sobres.</li><li>Té 16MB de Flash i 128MB de RAM.</li><li><a href="https://openwrt.org/toh/mikrotik/mikrotik_hap_ac2">Suporta Openwrt</a>, tot i que en principi és millor deixar-lo amb el seu sistema propi, RouterOS.</li><li>Es pot trobar per menys de 65€.</li><li>Té un USB 3.0 per expandir-lo i un botó configurable.</li></ul>Amb la tranquil·litat de saber que el puc passar a Openwrt si calgués, he començat a instal·lar-lo. No és trivial, ja que els menús avançats poder espantar una mica, però amb un xic de coneixement i consultant els fòrums habituals, tampoc no ha resultat cap drama.<p></p><p>I arriba l'hora de la veritat :</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg4fVPfiycFQqN_RFsnOzB1fbC-omy0Y6F9wM_mdTdN9jTpdNyPh49QdzM_Qh-lqyQew66Var5DRG9pi27vgM5aN2pahL8_AIAB6_By3RbRlESlQ7zQEmILRuLWGOPVmON4IVclTWdwyjDV/s1859/Mikrotik_hAP_ac2_test.png" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="1415" data-original-width="1859" height="488" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg4fVPfiycFQqN_RFsnOzB1fbC-omy0Y6F9wM_mdTdN9jTpdNyPh49QdzM_Qh-lqyQew66Var5DRG9pi27vgM5aN2pahL8_AIAB6_By3RbRlESlQ7zQEmILRuLWGOPVmON4IVclTWdwyjDV/w640-h488/Mikrotik_hAP_ac2_test.png" width="640" /></a></div><br /><p><br /></p><p><br /></p><p>Tal com es pot veure, treballa amb els 600MB simètrics sense problemes. I tot plegat amb un 5%-10% de la CPU de l'encaminador !!<br /></p><p>De moment, molt content amb la compra 😀</p>
Jordihttp://www.blogger.com/profile/10269271669040475159noreply@blogger.com0tag:blogger.com,1999:blog-2348072173208135260.post-63850619677822904612020-07-08T22:45:00.000+02:002020-07-08T22:45:12.090+02:00Ionic 5 amb AngularFa una mica més de 2 anys, vaig presentar una <a href="https://anomenaidesa.blogspot.com/search/label/Sunrise">sèrie d'articles per explicar pas a pas com fer una aplicació multi-plataforma amb Ionic</a>.
Es tractava d'indicar a quina hora surt i s'amaga el sol en un lloc determinat, en un dia determinat. Ha passat el temps i un parell de versions d'Ionic, així que anem a veure què ha canviat, reformulant la mateixa idea, però ara amb Ioniv 5.<br />
<br />
En primer lloc, instal·lem o actualitzem la versió de Ionic:<br />
<pre class="prettyprint">npm install -g @ionic/cli</pre>
Tot seguit, creem una aplicació base, amb pestanyes:<br />
<pre class="prettyprint">ionic start SunriseSunset tabs</pre>
Passarem per diverser preguntes senzilles:<br />
<ul>
<li>com a <i>Framework</i>, escollim <i>Angular</i> </li>
<li>escollim també integrar l'aplicació amb <i>Capacitor</i>.</li>
<li>no cal crear un compte a Ionic.</li>
</ul>
Tot seguit podem anar al directori de l'aplicació i engegar-la per veure el seu aspecte:<br />
<pre class="prettyprint">cd ./SunriseSunset
ionic serve</pre>
Amb això l'aplicació serà compilada i s'obrirà automàticament en el navegador, mostrant una pantalla similar a:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi3-v9f0TWofe9Iu2iKd5dnc16POP9ayE6-QwB6TV1WuHN9t_ksLZ26I75l34dqstRbCzlcNI7nxkZqO8RwQkcpFmOmOyc2SVC59RF68Azw9C5MQfUEyTmJHGkAPWUX65qm6wfw0mEiokco/s1600/Ionic5_tabs.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="615" data-original-width="933" height="420" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi3-v9f0TWofe9Iu2iKd5dnc16POP9ayE6-QwB6TV1WuHN9t_ksLZ26I75l34dqstRbCzlcNI7nxkZqO8RwQkcpFmOmOyc2SVC59RF68Azw9C5MQfUEyTmJHGkAPWUX65qm6wfw0mEiokco/s640/Ionic5_tabs.png" width="640" /></a></div>
<br />
Res gaire espectacular de moment, però almenys tenim una base per on començar !!
<br />
<br />
Si mirem l'estructura del projecte, veurem que cada pàgina està definida en el seu propi mòdul, i amb el seu fitxer de <i>routing</i>. També s'inclou el fitxer de <i>specs</i> per poder testejar la pàgina. És important mantenir cada pàgina en el seu propi directori, ja que això facilitarà l'organització del projecte.<br />
<br />Jordihttp://www.blogger.com/profile/10269271669040475159noreply@blogger.com0tag:blogger.com,1999:blog-2348072173208135260.post-32839989830054093982020-07-04T12:52:00.000+02:002020-07-04T12:52:38.917+02:00ERROR: node-sass errors.missingBinary() [SOLUCIONAT]Us ha passat mai que en anar a executar la vostre aplicació <a href="https://anomenaidesa.blogspot.com/search/label/Ionic" target="_blank">Ionic</a>/<a href="https://anomenaidesa.blogspot.com/search/label/Angular" target="_blank">Angular</a> us salti un error del tipus ?<br />
<ul>
<li>Node Sass could not find a binding for your current environment</li>
</ul>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj0rfWhKv67wOw3f_lEDtSr1D4DRlQ3W9slGBJHG9T2F52fLpvSsN89oWl9TkkenWkZtNzuUuhvcMfYVTTDxT2wqDtS-Mc1XQ2l_5FPB2nKVYsy4LDVquot4i4KjJ3oMWf7dM_gpFQZLmGP/s1600/error-node-sass.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="696" data-original-width="1436" height="310" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj0rfWhKv67wOw3f_lEDtSr1D4DRlQ3W9slGBJHG9T2F52fLpvSsN89oWl9TkkenWkZtNzuUuhvcMfYVTTDxT2wqDtS-Mc1XQ2l_5FPB2nKVYsy4LDVquot4i4KjJ3oMWf7dM_gpFQZLmGP/s640/error-node-sass.png" width="640" /></a></div>
<br />
I que un <i>npm instal</i>l clàssic tampoc no ho solucioni ?<br />
<br />
No patiu, que no és tan greu. L'error apareix típicament quan hem canviat la versió del node. Llavors, simplement cal recompilar el paquet <i>node-sass</i> a mà:<br />
<pre class="prettyprint">npm rebuild node-sass</pre>
I tal com podeu veure en la imatge següent, després ja podrem executar l'aplicació normalment:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-jwESFzvgkeboz9-FQncmVw7Eh_1m5ijYBhYtExXTMF1Jdg2mhX0BkvxfdE_vs3iCJq2qU2NX8J6Z0Le7f2nGDteV7-O3QwwgTOqLuJG80IvjljvgJ6zwzQxvKSXP_IzLiDV6t3kdTfx3/s1600/node-sass-rebuild.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="516" data-original-width="1094" height="300" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-jwESFzvgkeboz9-FQncmVw7Eh_1m5ijYBhYtExXTMF1Jdg2mhX0BkvxfdE_vs3iCJq2qU2NX8J6Z0Le7f2nGDteV7-O3QwwgTOqLuJG80IvjljvgJ6zwzQxvKSXP_IzLiDV6t3kdTfx3/s640/node-sass-rebuild.png" width="640" /></a></div>
<br />
Ja em comentareu si us ha servit !Jordihttp://www.blogger.com/profile/10269271669040475159noreply@blogger.com0tag:blogger.com,1999:blog-2348072173208135260.post-5430477045492895272020-06-30T21:02:00.001+02:002020-06-30T21:02:10.439+02:00Openwrt (LEDE) 19.07 amb "Software flow offloading"Ja fa temps que tenia pendent actualitzar el <i>firmware</i> del meu encaminador (<i>router</i>) <a href="http://anomenaidesa.blogspot.com/2013/07/gargoyle-en-un-tp-link-wdr3600.html" target="_blank">TP-Link WDR3600</a>. A part de la facilitat de "<a href="http://anomenaidesa.blogspot.com/search/label/Openwrt" target="_blank">trastejar</a>" en general, jo destacaria la possibiltat de <a href="https://anomenaidesa.blogspot.com/2017/07/lede-amb-adblock-adeu-als-anuncis-no.html">bloquejar anuncis</a> com una de les principals avantatges d'un <i>firmware</i> <a href="https://openwrt.org/">Openwrt/LEDE</a> sobre un de fàbrica.<br />
<br />
Com que havia passat temps, vaig haver de fer una actualització sense preservar la configuració. La versió instal·lada és la 19.07.3 i, utilitzant com a referència la còpia de seguretat anterior, no vaig tenir gaires dificultats en posar-la en marxa. De seguida vaig percebre millores tan a nivell visual com de gestió. En general, es percep una versió molt polida, molt recomanable.
<br />
<br />
Ara, el complement <a href="https://anomenaidesa.blogspot.com/2017/07/lede-amb-adblock-adeu-als-anuncis-no.html">adblock</a> per bloquejar els anuncis es pot afegir des de l'entorn gràfic, i es configura de manera molt senzilla. En cas de dubte, utilitzeu els comentaris per preguntar i, si puc, us donaré un cop de mà :-)<br />
<br />
Tot seguit, vaig anar a fer una prova de velocitat, i els resultats no van ser gaire bons:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjfZuARIJh5LAD3a3WK4Knhze7zgBxCzXFKHjGkuEFHWQ46xG88B7UCQIvV3ghvMfI0iQ30OcN0RRE0ZRHkC3UUy3Dc1GG38IW121WAcMB8zzKLBzTtOdrU5qaSUZ19qt6jRuJZpDSZ5lhb/s1600/HW_offloading_OFF_Test.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="755" data-original-width="981" height="491" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjfZuARIJh5LAD3a3WK4Knhze7zgBxCzXFKHjGkuEFHWQ46xG88B7UCQIvV3ghvMfI0iQ30OcN0RRE0ZRHkC3UUy3Dc1GG38IW121WAcMB8zzKLBzTtOdrU5qaSUZ19qt6jRuJZpDSZ5lhb/s640/HW_offloading_OFF_Test.png" width="640" /></a></div>
<br />
<br />
Fa uns anys, hauria signat aquests valors sense pensar-ho, però la meva connexió hauria de ser 600MB simètrics... Ja m'esperava que podia tenir problemes amb la velocitat, ja que tenia un <i>firmware</i> personalitzat amb <a href="https://anomenaidesa.blogspot.com/2017/07/lede-amb-fast-path-activat-300-mb.html">fastpath per assolir la màxima velocitat</a>.
<br />
<br />
Però no em va costar gaire trobar les opcions per aconseguir el mateix resultat (ara ja disponible en el <i>firmware</i> oficial). Efectivament, n'hi ha prou d'activar l'opció de <b><i>"Software flow offloading"</i></b>:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiiVCxX5HJYlqePXKgXuKeWTdGLLsw6imUWLxsvQOfbwbYy-2HMpzSimI9iDgb8nWVU87NMlLpH3QSlprtVRoU7QuAazLxOelG4w7bFCiZksKJd8jykS8RlNXMI_L_F-GbFyzXe0NbujWrb/s1600/Firewall-offloading-settings-ON.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="881" data-original-width="957" height="588" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiiVCxX5HJYlqePXKgXuKeWTdGLLsw6imUWLxsvQOfbwbYy-2HMpzSimI9iDgb8nWVU87NMlLpH3QSlprtVRoU7QuAazLxOelG4w7bFCiZksKJd8jykS8RlNXMI_L_F-GbFyzXe0NbujWrb/s640/Firewall-offloading-settings-ON.png" width="640" /></a></div>
<br />
<br />
Simplement amb aquest canvi, els resultat milloren dràsticament:
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgDz9pLO7Y23iN0cFPaJuxL7Vh1SkQT8bjXHWRg_Zn1WNP4JRvGLqg6nYc7GBVLvYGu1CXbsGXGxkEN0z1WMVDdm1T3SAJ2D1VhKh3tvHJG9YrZUNCghpdTxng5WPxhaxEQfnNqxi3kBiGk/s1600/HW_offloading_ON_Test.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="758" data-original-width="981" height="492" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgDz9pLO7Y23iN0cFPaJuxL7Vh1SkQT8bjXHWRg_Zn1WNP4JRvGLqg6nYc7GBVLvYGu1CXbsGXGxkEN0z1WMVDdm1T3SAJ2D1VhKh3tvHJG9YrZUNCghpdTxng5WPxhaxEQfnNqxi3kBiGk/s640/HW_offloading_ON_Test.png" width="640" /></a></div>
<br />
Com es pot observar, s'obtenen un 500MB simètrics, que ja s'apropen força al contractat. Els 100MB aprox. que falten poden ser deguts a l'ús de la resta d'usuaris en aquell moment, a que el proveidor no estava donant tot l'ample de banda, o molt més probablement, a que l'encaminador ja està en els seus límits (al cap i a la fi, ja té uns bons anys).
<br />
<br />
Sigui com sigui, estic content del canvi. Més fàcil i ràpid del que havia previst !Jordihttp://www.blogger.com/profile/10269271669040475159noreply@blogger.com0tag:blogger.com,1999:blog-2348072173208135260.post-48503308770781634132020-05-23T20:50:00.002+02:002020-05-23T20:50:51.493+02:00Instal·lar Docker a Ubuntu<a href="https://www.docker.com/" target="_blank">Docker</a> és possiblement el gestor de contenidors de programari més utilitzat actualment. Un contenidor ens permet obtenir els mateixos avantatges que una màquina virtual, però funcionant directament sobre el sistema operatiu hoste. Això permet que siguin més lleugers i eficients en general.<br />
<br />
Per instal.lar Docker a <a href="https://ubuntu.com/" target="_blank">Ubuntu</a>, seguim el procediment descrit en la seva <a href="https://docs.docker.com/engine/install/ubuntu/" rel="" target="_blank">pàgina</a>. De les possibles opcions, la més senzilla, sobretot de cara a possibles actualitzacions, és utilitzar un repositori. Per tant, primer instal·lem el repositori de Docker: <br />
<pre class="prettyprint">sudo apt-get update
sudo apt-get install apt-transport-https ca-certificates curl \
gnupg-agent software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
sudo apt-key add -
sudo add-apt-repository "deb [arch=amd64] \
https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
</pre>
<br />
I tot seguit ja podem instal·lar el <i>Docker Engine</i>:
<br />
<pre class="prettyprint">sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io
</pre>
<br />
Si tot és correcte, ja estem en condicions de provar el nostre primer contenidor:
<br />
<pre class="prettyprint">sudo docker run hello-world
</pre>
El resultat ha de ser similar al de l'imatge:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhMAP-Up7oZwoYQzKF1SVHe-0CpoOKDZcSpsLh__50qyS7nJfZPG1a8Zg6yhySs64SpCeH8nKDxNTr-S5WVcbbDRy1g4sqrLCM_3MUeUoSN8ROyyLZzLW0YhlK5TBDZe2nyTVaVw4N4ydPO/s1600/docker_install_ok.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="592" data-original-width="761" height="496" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhMAP-Up7oZwoYQzKF1SVHe-0CpoOKDZcSpsLh__50qyS7nJfZPG1a8Zg6yhySs64SpCeH8nKDxNTr-S5WVcbbDRy1g4sqrLCM_3MUeUoSN8ROyyLZzLW0YhlK5TBDZe2nyTVaVw4N4ydPO/s640/docker_install_ok.png" width="640" /></a></div>
Amb això, ja tenim l'entorn preparat per fer les primeres passes amb contenidors Docker. Tot a punt per divertir-se !!Jordihttp://www.blogger.com/profile/10269271669040475159noreply@blogger.com0tag:blogger.com,1999:blog-2348072173208135260.post-37447025455062703472019-04-10T23:35:00.002+02:002019-05-14T20:26:22.159+02:00slang-ed: editor senzill de fitxers de traduccions [V0.2.0]Fa un temps vaig parlar de com podem <a href="https://anomenaidesa.blogspot.com/2018/03/ionic-aplicacio-per-saber-lhora-de_24.html">traduir la nostra aplicació Ionic/Angular a diversos idiomes</a>. En concret, vàrem veure que gràcies a utilitzar la llibreria <a href="http://www.ngx-translate.com/">ngx-translate</a>, en teníem prou d'editar uns fitxers JSON amb les traduccions en cada idioma, per a poder utilitzar-les després en el nostre codi.<br />
<br />
Si us interessa el seu ús real en una aplicació Ionic/Angular/Electron començant des de zero, podeu seguir la sèrie completa <a href="https://anomenaidesa.blogspot.com/search/label/Sunrise">aquí</a> (o descarregar el codi des del seu <a href="https://github.com/jrierab/SunriseSunset">repositori github</a>).<br />
<br />
L'edició "a mà" d'aquests fitxers de traduccions no és difícil, però és pesada i subjecta a errades i oblits. Si heu de fer-ho professionalment, us aconsello la proposta de pagament <a href="https://www.codeandweb.com/babeledit">BabelEdit</a>. Alternativament, podeu utilitzar lliurement l'editor que he anat fent a estones lliures: <a href="https://github.com/jrierab/slang-ed">slang-ed</a>.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_PP-7rVtmtCzPQlZqZu5AmQmZ3OmF9ZE-UDM5IEGQfcl-GoainzF2p6YkPFKGuDmZ0FEqWUMHllEthFMTIAUSG4K7RCKy71Hm7nDS2EBxih3fstVMs19zzQ0tANMyEqZLRm4Ix90IGap1/s1600/slang-ed_01.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="851" data-original-width="1024" height="530" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_PP-7rVtmtCzPQlZqZu5AmQmZ3OmF9ZE-UDM5IEGQfcl-GoainzF2p6YkPFKGuDmZ0FEqWUMHllEthFMTIAUSG4K7RCKy71Hm7nDS2EBxih3fstVMs19zzQ0tANMyEqZLRm4Ix90IGap1/s640/slang-ed_01.png" width="640" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<b>slang-ed: Editor senzill de fitxers d'idioma</b><br />
<br />
Es tracta d'una aplicació Electron/Ionic 4 per a editar fitxers i18n de traduccions d'idioma. Està pensat per facilitar la traducció del vostre contingut a diferents idiomes quan s'utilitza <a href="http://www.ngx-translate.com/" rel="nofollow">ngx-translate</a> com a llibreria d'internacionalització (i18n) per Angular. Permet l'edició dels fitxers JSON de les traduccions de forma senzilla.<br />
<br />
<b>Crear un Nou Projecte:</b><br />
<ol>
<li>"<i>Inicia des de...</i>" per obrir un directori Angular/Ionic ja preparat:</li>
<ul>
<li>busca una estructura de directoris típica "<i>src/assets/i18n"</i></li>
<li>els fitxers d'idioma han de ser JSON amb un nom que segueixi l'<a href="https://ca.wikipedia.org/wiki/ISO_639-1">abreviació amb 2 caràcters</a> (per exemple, 'ca.json').</li>
</ul>
<li>“<em>Guardar com Nou Projecte</em>” per a crear un fitxer de projecte slang-ed.</li>
</ol>
<div>
<b>Treballant en un Projecte:</b></div>
<div>
<ol>
<li>"<i>Obrir Projecte</i>" per carregar les traduccions actuals.</li>
<li>Tot seguit edita els identificadors i les traduccions tant com calgui.</li>
<li>"<i>Guardar Traduccions</i>" per actualitzar els fitxers d'idioma i18n.</li>
<li>"<i>Guardar Projecte</i>" per actualitzar el fitxer de projecte amb les noves traduccions.</li>
</ol>
<div>
<b>Editar Traduccions:</b></div>
<div>
<ul>
<li>"<i>Afegir Arrel</i>" per afegir un node al nivell de l'arrel.</li>
<li>"<i>Afegir Node</i>" per afegir un node intermig al nivell actual.</li>
<li>"<i>Afegir paraula</i>" per afegir una paraula a traduir en el nivell actual.</li>
<li>"<i>Esborrar</i>" per esborrar el node/paraula actual.</li>
<li>"<i>Desfés" / "Refés</i>" per recuperar en cas d'errada.</li>
</ul>
</div>
</div>
<div>
<br /></div>
<b>Pendent (To Do):</b><br />
<ul>
<li><span class="tlid-translation translation" lang="ca"><span class="" title=""><strike>Gestionar els fitxers del projecte: crear, obrir, desar (això permetrà emmagatzemar informació "extra" a les definicions d'idioma).</strike></span></span><span class="tlid-translation translation" lang="ca"><span title=""> [V0.2.0]</span></span></li>
<li><span class="tlid-translation translation" lang="ca"><span title="">Suport per a propietats en les definicions d'idioma: <i>comentari</i> (per a traductors), <i>aprovat</i> (traducció verificada), <i>foundInSrc</i> (clau present en el codi font), <i>conserva</i> (no eliminar la clau, encara que no es trobi en el codi font).</span></span><span class="tlid-translation translation" lang="ca"><span title=""> </span></span></li>
<li><span class="tlid-translation translation" lang="ca"><span title="">Afegir / eliminar idiomes en el projecte actual.</span></span><span class="tlid-translation translation" lang="ca"><span title=""> </span></span></li>
<li><span class="tlid-translation translation" lang="ca"><span title="">Desar els fitxers i18n en el format d'objecte (en lloc del format de cadena de <i>clau completa</i> que s'utilitza ara).</span></span><span class="tlid-translation translation" lang="ca"><span title=""> </span></span></li>
<li><span class="tlid-translation translation" lang="ca"><span title="">Edició de múltiples paraules alhora (per exemple, tots els descendents d’un determinat node).</span></span><span class="tlid-translation translation" lang="ca"><span title=""> </span></span></li>
<li><span class="tlid-translation translation" lang="ca"><span title="">Indicar les traduccions, aprovacions o comentaris pendents...</span></span><span class="tlid-translation translation" lang="ca"><span title=""> </span></span></li>
<li><span class="tlid-translation translation" lang="ca"><span title="">Altres: els vostres suggeriments seran sempre benvinguts ;-)</span></span></li>
</ul>
<b>Descàrrega:</b><br />
<br />
Actualment hi ha disponible la V0.2.0. Sense gaire floritures, permet fer la feina. Podeu descarregar-la directament del <a href="https://github.com/jrierab/slang-ed">repositori de github</a>:<br />
<ul>
<li>Linux <a href="https://github.com/jrierab/slang-ed/releases/download/V0.1.0/slang-ed.0.2.0.AppImage">AppImage</a> </li>
<li>Linux <a href="https://github.com/jrierab/slang-ed/releases/download/V0.1.0/slang-ed_0.2.0_amd64.snap">snap</a> </li>
<li>Windows <a href="https://github.com/jrierab/slang-ed/releases/download/V0.1.0/slang-ed.Setup.0.2.0.exe">instal·lador</a> </li>
</ul>
Si us animeu a provar <a href="https://github.com/jrierab/slang-ed">slang-ed</a>, ja em comentareu què us sembla !Jordihttp://www.blogger.com/profile/10269271669040475159noreply@blogger.com0tag:blogger.com,1999:blog-2348072173208135260.post-24185414986565353172019-04-07T20:38:00.001+02:002019-04-10T20:16:23.392+02:00Linux: distribucions lleugeres per a programadors (2019) Ja fa temps que vaig posar Ubuntu a un <a href="http://anomenaidesa.blogspot.com/2011/05/posant-ubuntu-un-hp-dm1-3120es-1-fent.html">HP dm1-3120es</a>. Però han passat els anys, les distribucions són cada vegada més pesades i, ja per començar, l'ordinador tampoc no era cap meravella. En resum, que és lent.<br />
<br />
Per poder continuar treballant amb ell, l'he actualitzat amb un disc dur SSD de 64GB que he "reciclat" d'un antic sistema. I com que bàsicament el vull per poder programar a estones mortes, quan estic lluny del meu equip principal, m'he decidit a mirar quines distribucions linux lleugeres hi ha disponibles en el 2019. Això sí, han de poder suportar <a href="https://github.com/creationix/nvm">NVM</a> (amb <a href="https://nodejs.org/ca/">Node.js</a>), <a href="https://code.visualstudio.com/">Visual Studio Code</a> i <a href="https://www.gitkraken.com/">Gitkraken</a>. I tot plegat amb un entorn gràfic correcte i sense massa dificultats d'instal·lació.<br />
<br />
He provat tres distribucions, i tot seguit vull compartir els resultats obtinguts.<br />
<br />
<b>Bodhi Linux</b><br />
La primera distribució que he provat és <a href="https://www.bodhilinux.com/">Bodhi Linux</a> (V5.0.0). Està basada en la darrera versió d'Ubuntu LTS (18.04), però amb el gestor de finestres lleuger <i><a href="https://www.enlightenment.org/">Enlightenment</a></i>.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEim8XubGxdkbRH4MJXbOi5A4gf6iiJU7Vaiyg1EosyjgRkTIm3jHXILuZdWMWCgn8Z-y2otmCWd56yxOqTiX02L9bfVZNKOfrxweVUCvJoSvoGDqn0INMtuNv56QjSImHj0csqzrTBI3j3q/s1600/bodhi01.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="450" data-original-width="800" height="360" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEim8XubGxdkbRH4MJXbOi5A4gf6iiJU7Vaiyg1EosyjgRkTIm3jHXILuZdWMWCgn8Z-y2otmCWd56yxOqTiX02L9bfVZNKOfrxweVUCvJoSvoGDqn0INMtuNv56QjSImHj0csqzrTBI3j3q/s640/bodhi01.png" width="640" /></a></div>
Jo he instal·lat la versió <i>AppPack</i>, pel que acaba ocupant uns 9GB de disc (la versió més bàsica hauria d'ocupar uns 5GB), mentre que la memòria RAM no arriba als 300MB. Cap problema per instal·lar la xarxa sense fils ni la resta de paquets de programació (el teclat ja era correcte després de la instal·lació). Gràficament, es veu molt polit i és agradable treballar amb ell.<br />
<br />
<b>antiX Linux</b><br />
La segona distribució és <a href="https://antixlinux.com/">antiX Linux</a> (V17.4.1). Està basada en Debian <i>stable</i> i treballa per defecte amb el gestor de finestres <a href="https://ice-wm.org/">icewm</a>.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-JbDx3FggvD02WiH4d3hyphenhyphen-TgD3HioSERiLE0LNaPjwbleIND1_KJUqNS2AddAduma_UvwdC-UPM60EqyyhmtRZ1su8641b6TXZRDMT_8YKwggtaXNozHuaTGTylI0nCExg6vRTJjnC-C_/s1600/antix01.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="450" data-original-width="800" height="360" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-JbDx3FggvD02WiH4d3hyphenhyphen-TgD3HioSERiLE0LNaPjwbleIND1_KJUqNS2AddAduma_UvwdC-UPM60EqyyhmtRZ1su8641b6TXZRDMT_8YKwggtaXNozHuaTGTylI0nCExg6vRTJjnC-C_/s640/antix01.png" width="640" /></a></div>
<br />
La versió completa ocupa uns 4GB de disc, mentre que la memòria RAM inicialment es situa entorn als 200MB. El teclat no venia configurat i, en general, ha estat la distribució on més dificultats he trobat per instal·lar tot el necessari. Personalment, l'apartat gràfic no m'acabava de convèncer (tot i que si es canvia pel <a href="https://download.tuxfamily.org/antix/docs-antiX-17/FAQ/fluxbox.html">rox-Flubox</a> millora força), però per la resta, funciona perfectament.<br />
<br />
<b>Sparky Linux</b><br />
La tercera distribució és <a href="https://sparkylinux.org/">Sparky Linux</a> (V4.9.2 LTS). També està basada en Debian <i>stable</i> i treballa per defecte amb el gestor de finestres <a href="https://lxde.org/">LXDE</a>.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj6KSCrOuSRmdtSblh1GLJEiSxLrKjIPAVh6evgiKagF0BVmWt4EtKxU52C15ZSaGGt8mOCw4kSSCbZt4bO8HLlBWGmxctXeNV6xPtNNEZj7OGF2KXvQ71npoCOdM3aBPG_etcvDWrp0hJb/s1600/sparky01.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="450" data-original-width="800" height="360" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj6KSCrOuSRmdtSblh1GLJEiSxLrKjIPAVh6evgiKagF0BVmWt4EtKxU52C15ZSaGGt8mOCw4kSSCbZt4bO8HLlBWGmxctXeNV6xPtNNEZj7OGF2KXvQ71npoCOdM3aBPG_etcvDWrp0hJb/s640/sparky01.png" width="640" /></a></div>
<br />
La versió <i>Home</i> amb LXDE ocupa uns 7GB de disc, mentre que no arriba als 300MB de memòria RAM inicial. El teclat no venia configurat, però és senzill de configurar, a l'igual que la xarxa sense fils. En l'apartat gràfic, estaria a mig camí entre les dues anteriors.<br />
<br />
<b>Prestacions</b><br />
Fetes les presentacions, anem a veure les prestacions de cadascuna d'elles (s'ha fet un mínim de tres execucions en cada distribució).<br />
<table border="1" cellpadding="2" cellspacing="0">
<thead>
<tr>
<th></th>
<th>Bodhi</th>
<th>antiX</th>
<th>Sparky</th>
<th>Ubuntu*</th>
</tr>
</thead>
<tbody>
<tr>
<th>Temps de càrrega (de <i>grub</i> a <i>login</i>)</th>
<th>20s</th>
<th>24s</th>
<th><span style="color: blue;">18s</span></th>
<th>33s</th>
</tr>
<tr>
<th>Temps d'arribada a l'escriptori (des del <i>login</i>)</th>
<th><span style="color: blue;">5s</span></th>
<th><span style="color: blue;">5s</span></th>
<th>6s</th>
<th>20s</th>
</tr>
<tr>
<th>Temps de càrrega d'una aplicació Angular/Electron</th>
<th>138s</th>
<th>110s</th>
<th><span style="color: blue;">108s</span></th>
<th>140s</th>
</tr>
<tr>
<th>Memòria ocupada (amb aplicació Angular/Electron carregada)</th>
<th>1.11GB</th>
<th><span style="color: blue;">1.05GB</span></th>
<th>1.16GB</th>
<th>1.84GB</th>
</tr>
</tbody>
</table>
*: <i>Ubuntu 18.04 LTS, indicada per referència i comparació. </i><br />
<br />
Tal com es pot comprovar pels resultats de la taula, qualsevol de les tres opcions és vàlida, especialment pel que fa als temps de càrrega i el consum de memòria. Ara bé, pensant en dedicar l'ordinador a programar, el temps de càrrega de l'aplicació seria el factor més important, cosa que deixaria antiX i Sparky com les dues millors alternatives. Considerant també els temps de càrrega i la meva preferència gràfica, llavors la guanyadora és clarament: <a href="https://sparkylinux.org/">Sparky Linux</a>.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj6RTVX1_M71vgcaGokrcilQ20u30ySoaF69k2zyq612TyIoxRBL5DBhtA28k06a43nF-ZFkMmgHnRYugFEmVFgqRaIDHw8_oQnS0jUjbNI6a2vt7txBDWFVi82e5zlAiZVczTFw4utNcSO/s1600/sparky02.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="450" data-original-width="800" height="360" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj6RTVX1_M71vgcaGokrcilQ20u30ySoaF69k2zyq612TyIoxRBL5DBhtA28k06a43nF-ZFkMmgHnRYugFEmVFgqRaIDHw8_oQnS0jUjbNI6a2vt7txBDWFVi82e5zlAiZVczTFw4utNcSO/s640/sparky02.png" width="640" /></a></div>
<br />
Finalment, voldria indicar que per a un ús més general, la distribució <a href="https://www.bodhilinux.com/">Bodhi linux</a> pot ser una gran alternativa a un Ubuntu tradicional, especialment en ordinadors antics.<br />
<br />
Hi esteu d'acord? Coneixeu alguna alternativa millor? Podeu comentar el que creiu convenient en els comentaris !<br />
<br />Jordihttp://www.blogger.com/profile/10269271669040475159noreply@blogger.com0tag:blogger.com,1999:blog-2348072173208135260.post-67026873129273080862019-03-30T21:06:00.000+01:002019-03-30T21:06:11.377+01:00Ionic/Angular: Tests unitaris amb ngx-translateSi treballeu amb <a href="https://ionicframework.com/">Ionic</a> o <a href="https://angular.io/">Angular</a> possiblement utilitzeu la llibreria d'internacionalització <a href="http://www.ngx-translate" target="_blank">ngx-translate.com</a>. I si no ho feu encara, us ho recomano, ja que facilitat la traducció a diversos idiomes i el canvi de llenguatge sense haver de reiniciar l'aplicació. Podeu veure com fer-ho <a href="https://anomenaidesa.blogspot.com/2018/03/ionic-aplicacio-per-saber-lhora-de_24.html">en aquest article</a>, on detallo com afegir un servei de traducció a una aplicació.<br />
<br />
El cas és que Ionic v4 facilita la realització de tests unitaris, però no es fàcil comprovar un mòdul que utilitzi els serveis de traducció: <i>pipes</i>, carregadors, dependències... Com gestionar-ho, tot plegat?<br />
<br />
La informació no és molt clara i hi ha diverses alternatives, algunes de força complicades. Per això vull compartir aquí la que m'ha semblat més senzilla.<br />
<br />
En primer lloc, instal·lem un parell de llibreries que ens serviran de suport:<br />
<pre class="prettyprint"> npm install ngx-translate-testing --save-dev
npm install ngx-translate-messageformat-compiler messageformat --save-dev
</pre>
Llavors ja podem utilitzar la llibreria en els nostres test unitaris:<br />
<pre class="prettyprint"><code class="language-javascript" style="font-size: 85%;"> ...
import { TranslateTestingModule } from 'ngx-translate-testing';
import { TranslateMessageFormatCompiler } from 'ngx-translate-messageformat-compiler';
describe('ElMeuComponent', () => {
const ENGLISH_LANGUAGE = 'en';
const ENGLISH_TRANSLATIONS = require('../../assets/i18n/en.json');
...
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ElMeuComponent],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
imports: [
TranslateTestingModule
.withTranslations(ENGLISH_LANGUAGE, ENGLISH_TRANSLATIONS)
.withCompiler(new TranslateMessageFormatCompiler())
]
}).compileComponents();
}));
...
});
</code>
</pre>
<br />
I el resultat:
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZ1Z3wKIwujHv-3E5oLfRMwML-LAERiRIWlHo0RXmCEjPops0B_gqo6QJkUbzt5khyphenhyphenzUgdU3QML_uzbAjLMPUQ0Q2cg0DjB5l6njszzEjp_IfO-ciEZVI_bSnZjDlPAv2-NUEZybKwZKeT/s1600/ngx-translate-TU.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="164" data-original-width="1178" height="88" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZ1Z3wKIwujHv-3E5oLfRMwML-LAERiRIWlHo0RXmCEjPops0B_gqo6QJkUbzt5khyphenhyphenzUgdU3QML_uzbAjLMPUQ0Q2cg0DjB5l6njszzEjp_IfO-ciEZVI_bSnZjDlPAv2-NUEZybKwZKeT/s640/ngx-translate-TU.png" width="640" /></a></div>
Com es pot veure, és força senzill. Ara ja no hi ha cap excusa per no comprovar els mòduls que utilitzin la <i>pipe</i> <i>translate</i> !!!<br />
Jordihttp://www.blogger.com/profile/10269271669040475159noreply@blogger.com0tag:blogger.com,1999:blog-2348072173208135260.post-49840923762221291692019-02-10T20:39:00.000+01:002019-02-10T20:39:18.388+01:00GitKraken: Inotify Limit Error [Solucionat]Si desenvolupeu en Ubuntu i treballeu amb <a href="https://git-scm.com/">git</a> és possible que ja utilitzeu el <a href="https://www.gitkraken.com/">GitKraken</a>. I si encara no ho feu, proveu-lo ! Segur que us agradarà: us deixo una imatge a sota per fer dentetes...<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEilT-BzIfAv3s-ElNQC4Cfh0WVTAOYdkboXymzkCn8qr5iXrBWXaEPn8rt-CNHRLOnjtvQIOdj_X2EC_kgbNTkIDV2AHKV3vUkymCDnNHK-Cwt8AguNxOkofjhDVg9Zet0Jd0x-wTMRpZnT/s1600/GitKraken.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="628" data-original-width="1258" height="318" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEilT-BzIfAv3s-ElNQC4Cfh0WVTAOYdkboXymzkCn8qr5iXrBWXaEPn8rt-CNHRLOnjtvQIOdj_X2EC_kgbNTkIDV2AHKV3vUkymCDnNHK-Cwt8AguNxOkofjhDVg9Zet0Jd0x-wTMRpZnT/s640/GitKraken.png" width="640" /></a></div>
<br />
En tot cas, si treballeu en Angular o en projectes amb molts fitxers, us pot sortir el següent error en obrir el projecte:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgUsRfoIuk-drHupqqrib6b7uiVVnp-CiM11QTFslVU3OYsHCAEVhEINB4Yp9PMwEdfaRRbw_LRz7Q-iOJWHo4hznQa_XD6XqCwOCw-Vri5yOkcIUGuBCfxApTnVpVrZ3Ex-ECLMShfaBpC/s1600/inotify_1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="325" data-original-width="394" height="262" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgUsRfoIuk-drHupqqrib6b7uiVVnp-CiM11QTFslVU3OYsHCAEVhEINB4Yp9PMwEdfaRRbw_LRz7Q-iOJWHo4hznQa_XD6XqCwOCw-Vri5yOkcIUGuBCfxApTnVpVrZ3Ex-ECLMShfaBpC/s320/inotify_1.png" width="320" /></a></div>
<br />
Per evitar-lo només cal augmentar el nombre màxim de fitxers a supervisar (per exemple, seguint les indicacions de: <a href="https://github.com/guard/listen/wiki/Increasing-the-amount-of-inotify-watchers">https://github.com/guard/listen/wiki/Increasing-the-amount-of-inotify-watchers</a>).<br />
<pre class="prettyprint">$ cat /proc/sys/fs/inotify/max_user_watches
$ echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf
$ sudo sysctl -p
</pre>
<br />
I problema resolt !
Jordihttp://www.blogger.com/profile/10269271669040475159noreply@blogger.com0tag:blogger.com,1999:blog-2348072173208135260.post-47296697231383979672018-04-02T20:25:00.000+02:002018-04-02T20:25:07.702+02:00Electron: aplicacions d'escriptori amb tecnologia webDarrerament, he publicat una <a href="https://anomenaidesa.blogspot.com.es/search/label/Sunrise">sèrie d'articles</a> sobre com fer una aplicació híbrida, per Android, iOS i web utilitzant typescript (javascript), html i CSS3.<br />
<br />
En aquest article, veurem com amb aquesta mateixa base de codi podem construir també una aplicació d'escriptori gràcies a <a href="https://electronjs.org/">Electron</a>. Bàsicament, el que fa és empaquetar l'aplicació Ionic que ja teníem per poder construir una aplicació d'escriptori, que s'executa sobre una pàgina Chrome personalitzada. La part bona és que <b>Electron</b> s'ocupa de tot, així que els canvis a fer són mínims.<br />
<br />
Ho mostrarem amb l'aplicació per saber l'hora de sortida i posta del sol que vàrem fer pas a pas:<br />
<ul>
<li>En <a href="https://anomenaidesa.blogspot.com/2018/02/ionic-aplicacio-per-saber-lhora-de.html">la primera part</a> vàrem preparar el projecte inicial.</li>
<li>En <a href="https://anomenaidesa.blogspot.com/2018/02/ionic-aplicacio-per-saber-lhora-de_24.html">la segona part</a> vàrem crear l'aplicació, fixant el lloc.</li>
<li>En <a href="https://anomenaidesa.blogspot.com/2018/02/ionic-aplicacio-per-saber-lhora-de_27.html">la tercera part</a> vàrem afegir-hi geolocalització.</li>
<li>En <a href="https://anomenaidesa.blogspot.com/2018/03/ionic-aplicacio-per-saber-lhora-de.html">la quarta part</a> vàrem afegir-hi un calendari per escollir la data.</li>
<li>En <a href="https://anomenaidesa.blogspot.com/2018/03/ionic-aplicacio-per-saber-lhora-de_7.html">la cinquena part</a> vàrem afegir-hi Google Maps per escollir el lloc.</li>
<li>En <a href="https://anomenaidesa.blogspot.com/2018/03/ionic-aplicacio-per-saber-lhora-de_24.html">la sisena part</a> vàrem afegir-hi traduccions a diversos idiomes.</li>
<li>En <a href="https://anomenaidesa.blogspot.com/2018/03/ionic-aplicacio-per-saber-lhora-de_29.html">la setena part</a> vàrem afegir-hi personalització d'icones i tipus de lletra.</li>
</ul>
<br />
El codi de l'aplicació Ionic final està disponible en el Github: <a href="https://github.com/jrierab/SunriseSunset/releases/tag/v1.0">v1.0</a>.<br />
<br />
Per desgràcia, no hi ha encara una plantilla per a un projecte Electron amb Ionic. Hi ha informació diversa, però és difícil extreure'n el gra de la palla. A mi m'han servit molt els articles del bloc de <a href="https://robferguson.org/blog/2017/11/09/build-a-desktop-application-with-ionic-3-and-electron/">Rob Ferguson</a>. Tot seguit us resumeixo el procés d'adaptar una aplicació Ionic que ja tenim funcionant per afegir-hi Electron i poder generar una aplicació d'escriptori.<br />
<br />
En primer lloc, instal·lem electron:<br />
<pre class="prettyprint"> npm install -g electron</pre>
<br />
Tot seguit creem un directori <i>electron</i> a l'arrel del projecte hi hi posem la pàgina principal d'electron, <i>main.js</i>, modificada per carregar l'aplicació Ionic:
<br />
<pre class="prettyprint linenums"><code class="language-javascript" style="font-size: 85%;">const electron = require('electron');
// Module to control application life.
const app = electron.app;
// Module to create native browser window.
const BrowserWindow = electron.BrowserWindow;
const path = require('path');
const url = require('url');
// Module for file manipulations
const fs = require('fs');
// config json
let config_file = path.dirname(process.execPath)+'/config_keys.json';
if(!fs.existsSync(config_file)) {
config_file = app.getAppPath()+'/config_keys.json';
}
const config = JSON.parse(fs.readFileSync(config_file, 'utf-8'));
process.env.GOOGLE_API_KEY = config.GOOGLE_API_KEY; // = "YOUR_API_KEY";
// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let mainWindow;
function createWindow() {
// Create the browser window.
mainWindow = new BrowserWindow({width: 1024, height: 768});
// and load the index.html of the app.
const startUrl = process.env.ELECTRON_START_URL || url.format({
pathname: path.join(__dirname, '/../www/index.html'),
protocol: 'file:',
slashes: true
});
mainWindow.loadURL(startUrl);
// mainWindow.loadURL("http://localhost:8100");
// Open the DevTools.
// mainWindow.webContents.openDevTools();
// Emitted when the window is closed.
mainWindow.on('closed', function () {
// Dereference the window object, usually you would store windows
// in an array if your app supports multi windows, this is the time
// when you should delete the corresponding element.
mainWindow = null
})
}
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', createWindow);
// Quit when all windows are closed.
app.on('window-all-closed', function () {
// On OS X it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
if (process.platform !== 'darwin') {
app.quit()
}
});
app.on('activate', function () {
// On OS X it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (mainWindow === null) {
createWindow()
}
});
// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.
</code></pre>
<br />
El contingut és bàsicament el d'un projecte inicial d'Electron. Els canvis que s'han fet són:<br />
<ul>
<li>[10-22] Es llegeix un fitxer de configuració <i>config_keys.json</i> que inclou el codi de la clau de Google per a les API de les aplicacions de Maps i Geolocation. Sense aquesta clau, l'aplicació d'escriptori no té permisos per utilitzar geolocalització.</li>
<li>[32-37] S'indica a Electron d'on ha de carregar la pàgina inicial. Justament és la de l'aplicació Ionic.</li>
</ul>
El fitxer de claus <i>config_keys.json</i> es guarda a l'arrel del projecte, i el seu contingut serà com segueix (lògicament, canviant la clau per una de bona):<br />
<pre class="prettyprint"> {
"GOOGLE_API_KEY": "You_API_Key_here"
}</pre>
<br />
Al fitxer <i>src/index.html</i> d'Ionic eliminem la referència a <i>cordova.js</i>, que no és necessària en una aplicació d'escriptori:<br />
<pre class="prettyprint"><code class="language-html" style="font-size: 85%;"> <!-- cordova.js required for cordova apps (remove if not needed)
<script src="cordova.js"></script>
--></code></pre>
<br />
Finalment, editem el <i>package.json</i> per afegir-hi les noves dependències i instruccions de com treballar amb <i>electron</i>:
<br />
<pre class="prettyprint"> {
...
"scripts": {
...
"start": "ionic serve --no-open",
"electron": "electron .",
"dist": "electron-builder",
...
},
...
"devDependencies": {
"@angular/cli": "^1.5.4",
"@angular/router": "^5.0.3",
"@ionic/app-scripts": "3.1.8",
"@types/node": "^8.0.53",
"concurrently": "^3.5.0",
"electron": "^1.7.9",
"electron-builder": "^19.45.1",
"ionic-mocks": "^1.0.4",
"typescript": "2.4.2",
"wait-on": "^2.0.2"
},
"config": {
"ionic_source_map_type": "source-map"
},
"main": "electron/main.js",
"build": {
"appId": "com.blogspot.anomenaidesa.sunsetsunrise",
"files": [
"electron/main.js",
"www/**/*"
],
"extraFiles": [
"config_keys.json"
],
"mac": {
"category": "productivity"
},
"linux": {
"category": "Utility"
}
},
...
}</pre>
<br />
Ara només ens cal instal·lar les dependències amb:
<br />
<pre class="prettyprint"> npm install</pre>
<br />
I provar l'aplicació executant primer en un terminal:
<br />
<pre class="prettyprint"> npm start</pre>
<br />
I, quan acabi de preparar l'aplicació, en un altre terminal:
<br />
<pre class="prettyprint"> npm run electron</pre>
<br />
Un cop comprovat que obtenim el mateix funcionament que abans amb Ionic, ja podem generar l'aplicació d'escriptori. És tan senzill com fer:
<br />
<pre class="prettyprint"> npm run dist</pre>
<br />
En Linux, el resultat és una aplicació distribuïble, <i>SunriseSunset-1.1.0-x86_64.AppImage</i>, que encapsula totes les dependències i que es pot executar directament. El resultat és el següent:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh6X-50LBxrS0cw_6gkM2Yj7KxEa1wJccrxNOeIQGO8eqKxcVDBs_OoXjVf4z39qosaB_LVDJ_KNNDYUrX9kPtVtjPUTxt7OMqPOdiPeLbLdyXFtNWZs7DgYv7dDIlgqMHLwLiV-1BczT9E/s1600/IonicApp_sunrise_21.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="796" data-original-width="1024" height="496" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh6X-50LBxrS0cw_6gkM2Yj7KxEa1wJccrxNOeIQGO8eqKxcVDBs_OoXjVf4z39qosaB_LVDJ_KNNDYUrX9kPtVtjPUTxt7OMqPOdiPeLbLdyXFtNWZs7DgYv7dDIlgqMHLwLiV-1BczT9E/s640/IonicApp_sunrise_21.png" width="640" /></a></div>
<br />
Tal com es pot veure, té l'aspecte natiu d'una aplicació Ubuntu i el funcionament és idèntic al que teníem en l'aplicació Ionic original.<br />
<br />
Noteu com <b>una mateixa base de codi typescript/html/css3 permet generar aplicacions mòbils, web i d'escriptori</b>. Si no necessitem les prestacions addicionals d'una aplicació nativa, la flexibilitat d'aquesta proposta resulta extremadament atractiva, no us sembla?<br />
<br />
Com sempre, si voleu practicar, teniu el codi final a la vostre disposició en el meu Github: <a href="https://github.com/jrierab/SunriseSunset/releases/tag/v1.1.0">v1.1.0</a>.<br />
<br />
De fet, fins i tot podeu descarregar i provar directament l'aplicació d'escriptori: <a href="https://github.com/jrierab/SunriseSunset/releases/download/v1.1.0/SunriseSunset-1.1.0-x86_64.AppImage">SunriseSunset-1.1.0-x86_64.AppImage</a> (per Ubuntu, Debian o altres variants compatibles).<br />
<br />
Ja em comentareu què us ha semblat !<br />
<br />Jordihttp://www.blogger.com/profile/10269271669040475159noreply@blogger.com0tag:blogger.com,1999:blog-2348072173208135260.post-58825772050486803472018-03-29T15:36:00.000+02:002018-03-31T18:50:02.979+02:00Ionic: aplicació per saber l'hora de sortida/posta del sol (VII)<b>Part VII: Personalització d'icones i tipus de lletra</b><br />
<br />
Aquest article ens mostrarà com canviar el tipus de lletra que utilitza una aplicació i com afegir icones personalitzades.<br />
<br />
Fins ara hem fet una aplicació per saber l'hora de sortida i posta del sol:<br />
<ul>
<li>En <a href="https://anomenaidesa.blogspot.com/2018/02/ionic-aplicacio-per-saber-lhora-de.html" target="_blank">la primera part</a> vàrem preparar el projecte inicial.</li>
<li>En <a href="https://anomenaidesa.blogspot.com/2018/02/ionic-aplicacio-per-saber-lhora-de_24.html">la segona part</a> vàrem crear l'aplicació, fixant el lloc.</li>
<li>En <a href="https://anomenaidesa.blogspot.com/2018/02/ionic-aplicacio-per-saber-lhora-de_27.html">la tercera part</a> vàrem afegir-hi geolocalització. </li>
<li>En <a href="https://anomenaidesa.blogspot.com/2018/03/ionic-aplicacio-per-saber-lhora-de.html">la quarta part</a> vàrem afegir-hi un calendari per escollir la data. </li>
<li>En <a href="https://anomenaidesa.blogspot.com/2018/03/ionic-aplicacio-per-saber-lhora-de_7.html">la cinquena part</a> vàrem afegir-hi Google Maps per escollir el lloc.</li>
<li>En <a href="https://anomenaidesa.blogspot.com/2018/03/ionic-aplicacio-per-saber-lhora-de_24.html">la sisena part</a> vàrem afegir-hi traduccions a diversos idiomes. </li>
</ul>
Ara anem a personalitzar-la una mica, amb icones i tipus de lletra diferents.<br />
<br />
Comencem pel tipus de lletra. En tenim moltes de disponibles a <a href="https://fonts.google.com/">Google fonts</a>. N'escollim una que s'ajusti a les necessitats (<a href="https://fonts.google.com/specimen/Poppins?selection.family=Poppins">Poppins</a> en el nostre cas) i descomprimim els fitxer <i>*.ttf</i> a <i>assets/fonts</i>. Tot seguit declarem la nova font i la posem per defecte, editant el fitxer <i>theme/variables.scss</i> per afegir:<br />
<pre class="prettyprint"> ...
// Fonts
// --------------------------------------------------
//@import "roboto";
//@import "noto-sans";
@font-face {
font-family: "Poppins";
//font-style: normal;
//font-weight: 400;
src: url($font-path+'/Poppins-Regular.ttf');
}
@font-face {
font-family: "Poppins";
src: url($font-path+'/Poppins-Bold.ttf');
font-weight: bold;
}
@font-face {
font-family: "Poppins";
src: url($font-path+'/Poppins-Italic.ttf');
font-style: italic;
}
@font-face {
font-family: "Poppins";
src: url($font-path+'/Poppins-BoldItalic.ttf');
font-weight: bold;
font-style: italic;
}
$font-family-base: "Poppins";
$font-family-md-base: "Poppins";
$font-family-ios-base: "Poppins";
$font-family-wp-base: "Poppins";
</pre>
<br />
Pel que fa a les icones, si n'hem d'utilitzar moltes seria convenient declarar un nou tipus de lletra, però si són poques, és molt molt senzill afegir-les directament. Per això, escollim icones lliures, per exemple a <a href="https://www.flaticon.com/">Flaticon</a>, procurant que siguin d'un sol color i en format SVG. Llavors les afegim a <i>app/app.scss</i>:<br />
<pre class="prettyprint"> ...
// --- Custom icons -------------------------------------------------------------------
//
// https://stackoverflow.com/a/44575053/1581368
// To generate a font : https://yannbraga.com/2017/06/28/how-to-use-custom-icons-on-ionic-3/
ion-icon {
&[class*="custom-"] {
// Instead of using the font-based icons we're applying SVG masks
mask-size: contain;
mask-position: 50% 50%;
mask-repeat: no-repeat;
background: currentColor;
width: 1em;
height: 1em;
}
// custom icons
&[class*="custom-sunrise"] {
mask-image: url(../assets/icon/sunrise.svg);
}
&[class*="custom-sunset"] {
mask-image: url(../assets/icon/sunset.svg);
}
}
</pre>
<br />
I llavors ja els podem utilitzar normalment a <i>home.html</i>:
<br />
<pre class="prettyprint"><code class="language-html" style="font-size: 85%;"> ...
<ion-item-divider color="light">
{{ "APP.timetable" | translate }}
</ion-item-divider>
<ion-item>
<ion-icon name="custom-sunrise" color="orange" item-left></ion-icon>
<ion-note item-right>{{ sunrise }}</ion-note>
</ion-item>
<ion-item>
<ion-icon name="custom-sunset" color="blue" item-left></ion-icon>
<ion-note item-right>{{ sunset }}</ion-note>
</ion-item>
...</code>
</pre>
<br />
El resultat es pot veure a continuació:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgYkzTZUqGg1sHI-qlJ94MV93WXxE_OpG3iPcg_jYfi5qy0KNp37NHTZnhDZAzGR0bwAuw9wlpsf-4sW4bYpmswiGBxbrE4FUdtkrcqqLp_m6a-7_Iu9OKV44Ue33nGuSG6bnGqqekl5TvW/s1600/IonicApp_sunrise_20.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="834" data-original-width="1072" height="496" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgYkzTZUqGg1sHI-qlJ94MV93WXxE_OpG3iPcg_jYfi5qy0KNp37NHTZnhDZAzGR0bwAuw9wlpsf-4sW4bYpmswiGBxbrE4FUdtkrcqqLp_m6a-7_Iu9OKV44Ue33nGuSG6bnGqqekl5TvW/s640/IonicApp_sunrise_20.png" width="640" /></a></div>
<br />
Observem en el lateral com s'ha carregat correctament el fitxer de fonts (les versions normal i negreta, que són les que s'utilitzen).<br />
<br />
I d'aquesta manera tan senzilla podem donar-li un aspecte més polit a una aplicació. Per acabar-ho d'arrodonir, completem la pàgines de contacte, de crèdits i afegim informació al repositori de Github (en el fitxer <i>README.md</i>).<br />
<br />
Com sempre, teniu tot el codi a la vostre disposició en el Github: <a href="https://github.com/jrierab/SunriseSunset/releases/tag/v1.0">v1.0</a>.<br />
<br />
I amb aquests retocs finals podem donar l'aplicació per completada. Espero que hagi resultat útil. Si teniu alguna idea per millorar-la, traduccions a altres idiomes o qualsevol comentari, no dubteu a comentar/contactar.<br />
<br />Jordihttp://www.blogger.com/profile/10269271669040475159noreply@blogger.com0tag:blogger.com,1999:blog-2348072173208135260.post-46095850390400234522018-03-24T18:31:00.000+01:002018-03-31T18:55:13.887+02:00Ionic: aplicació per saber l'hora de sortida/posta del sol (VI)<b>Part VI: Traducció a diversos idiomes</b><br />
<br />
Aquest article ens mostrarà com permetre diversos llenguatges en la nostre aplicació.<br />
<br />
Fins ara hem fet una aplicació per saber l'hora de sortida i posta del sol:<br />
<ul>
<li>En <a href="https://anomenaidesa.blogspot.com/2018/02/ionic-aplicacio-per-saber-lhora-de.html" target="_blank">la primera part</a> vàrem preparar el projecte inicial.</li>
<li>En <a href="https://anomenaidesa.blogspot.com/2018/02/ionic-aplicacio-per-saber-lhora-de_24.html">la segona part</a> vàrem crear l'aplicació, fixant el lloc.</li>
<li>En <a href="https://anomenaidesa.blogspot.com/2018/02/ionic-aplicacio-per-saber-lhora-de_27.html">la tercera part</a> vàrem afegir-hi geolocalització. </li>
<li>En <a href="https://anomenaidesa.blogspot.com/2018/03/ionic-aplicacio-per-saber-lhora-de.html">la quarta part</a> vàrem afegir-hi un calendari per escollir la data. </li>
<li>En <a href="https://anomenaidesa.blogspot.com/2018/03/ionic-aplicacio-per-saber-lhora-de_7.html">la cinquena part</a> vàrem afegir-hi Google Maps per escollir el lloc.</li>
</ul>
Per introduir la possibilitat d'utilitzar diversos idiomes, utilitzarem el paquet <a href="http://www.ngx-translate.com/">ngx-translate</a>. En primer lloc l'instal·lem:<br />
<pre class="prettyprint"> npm install @ngx-translate/core --save
npm install @ngx-translate/http-loader --save</pre>
<br />
Seguint les <a href="https://github.com/ngx-translate/core#usage">instruccions</a> d'ús, afegim a <i>app.module.ts</i> els mòduls i serveis necessaris:<br />
<pre class="prettyprint"><code class="language-javascript" style="font-size: 85%;"> ...
import { SplashScreen } from '@ionic-native/splash-screen';
// ngx-translate
import {TranslateModule, TranslateLoader} from '@ngx-translate/core';
import {TranslateHttpLoader} from '@ngx-translate/http-loader';
import { HttpClientModule, HttpClient } from '@angular/common/http';
...
import { LangService } from '../providers/lang-service';
// Translations
export function createTranslateLoader(http: HttpClient) {
return new TranslateHttpLoader(http, './assets/i18n/', '.json');
}
@NgModule({
...
imports: [
...
CalendarModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: (createTranslateLoader),
deps: [HttpClient]
}
}),
IonicModule.forRoot(MyApp)
],
...
providers: [
...
Network,
LangService
]
})
...</code>
</pre>
<br />
Observem com hem utilitzem un servei <i>LangService</i> per gestionar la detecció de l'idioma i el canvi de llenguatge. Per això creem un fitxer <i>providers/lang-service.ts</i> amb el següent contingut:<br />
<pre class="prettyprint linenums"><code class="language-javascript" style="font-size: 85%;"> import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { TranslateService } from '@ngx-translate/core';
@Injectable()
export class LangService {
onLang : BehaviorSubject<string> = new BehaviorSubject("");
lang_sets : Array<string> = [];
constructor(public translate: TranslateService) {
console.log('### LangService');
}
init(lang_default: string, lang_sets: Array<string>, lang_fallback?: string) {
// Verify that some sets are present
if(lang_sets.length==0) {
console.error("[LangService] error: no lang_sets defined");
return;
} else {
this.lang_sets = lang_sets;
}
// Verify default is present in lang_sets
if(this.lang_sets.indexOf(lang_default) == -1) {
console.log("[LangService] Warning: trying to use default lang '"+
lang_default+"' which is not present in lang_sets");
console.log("[LangService] Warning: setting default to first"
"language in set");
lang_default = this.lang_sets[0];
}
// this language will be used as a fallback when a translation
// isn't found in the current language
if(!lang_fallback) lang_fallback = lang_default;
this.translate.setDefaultLang(lang_fallback);
let langRegex = new RegExp(this.lang_sets.join("|"), 'gi');
// get browser language by default
let userLang = navigator.language.split('-')[0];
userLang = langRegex.test(userLang) ? userLang : lang_default;
this.setLanguage(userLang);
// ----------------------------------------------------------------
}
getCurrentLang() {return this.translate.currentLang};
setLanguage(lang: string) {
// Verify default is present in lang_sets
if(this.lang_sets.indexOf(lang) == -1) {
console.log("[LangService] Warning: trying to use lang '"+lang+
"' which is not present in lang_sets");
return;
}
// the lang to use, if the lang isn't available, it will use
// the current loader to get them
this.translate.use(lang);
console.log("[LangService] Using language: "+
this.translate.currentLang);
// Inform to all subscriptors
this.onLang.next(lang);
}
}</string></string></string></code>
</pre>
<br />
Observem com el servei:<br />
<ul>
<li>[8] Declara un <a href="http://reactivex.io/documentation/subject.html">BehaviorSubject</a> <i>onLang</i> que permetrà notificar un canvi d'idioma a la resta de l'aplicació.</li>
<li>[16-48] La funció <i>init</i> permet indicar quin idioma s'utilitza per defecte en l'aplicació (<i>lang_default</i>), el conjunt de llenguatges possibles (<i>lang_sets</i>), i quin és el llenguatge que s'utilitzarà si una traducció no existeix en un idioma (normalment, serà el llenguatge que utilitza com a primari el programador). La funció fa comprovacions d'errors i llavors estableix com a llenguatge per defecte a usar, el que tingui definit l'usuari en el seu dispositiu/navegador.</li>
<li>[50] La funció <i>getCurrentLang</i> simplement retorna el llenguatge actualment actiu en l'aplicació.</li>
<li>[52-68] La funció <i>setLanguage</i> estableix el llenguatge a utilitzar en l'aplicació, si existeix en el conjunt de disponibles. En cas contrari, agafa el que hi hagi per defecte en l'aplicació.</li>
</ul>
<br />
Aquest servei s'inicialitza a <i>app.component.ts</i>:
<br />
<pre class="prettyprint"><code class="language-javascript" style="font-size: 85%;"> ...
import { LangService } from '../providers/lang-service';
...
constructor( ..., langService : LangService )
{
platform.ready().then(() => {
...
langService.init("en", ["en", "ca"]);
});
}
...</code>
</pre>
<br />
Podem veure com l'aplicació estarà en anglès i català. Per això, cal posar els fitxers d'idioma a <i>assets/i18n/ca.json</i> i <i>assets/i18n/es.json</i>. El nom es correspon al codi d'idioma i el contingut és un JSON senzills amb les traduccions. Per exemple, pel català:<br />
<pre class="prettyprint"><code class="language-json" style="font-size: 85%;"> {
"APP.title": "Sortida/posta de Sol",
"APP.place": "Lloc",
"APP.lat": "Latitud",
...
}</code>
</pre>
<br />
Ara el que ens queda és utilitzar les traduccions en els missatges del programa, i permetre el canvi d'idioma. Modificarem la pàgina <i>home.html</i>:<br />
<br />
<pre class="prettyprint linenums"><code class="language-html" style="font-size: 85%;"> <ion-header>
<ion-navbar>
<ion-title>{{ "APP.title" | translate }}</ion-title>
</ion-navbar>
</ion-header>
<ion-content padding>
<ion-list>
<ion-item-divider color="light">
{{ "APP.language" | translate }}
</ion-item-divider>
<ion-item>
<ion-label>{{ "APP.lang-current" | translate }}</ion-label>
<ion-select [(ngModel)]="lang" (ionChange)="doChangeLang()" item-right>
<ion-option value="ca">Català</ion-option>
<ion-option value="en">English</ion-option>
</ion-select>
</ion-item>
...</code>
</pre>
<br />
Observem que:<br />
<ul>
<li> [3, 10, 13] S'han canviat els missatges de text pel seu identificador en el fitxer d'idioma i demanant la seva traducció.</li>
<li>[14-17] S'ha afegit una opció de canvi d'idioma que crida la funció <i>doChangeLang()</i>.</li>
</ul>
<br />
La funció <i>doChangeLang()</i> s'implementa a <i>home.ts</i>, que s'ha modificat de la següent manera:<br />
<pre class="prettyprint"><code class="language-javascript" style="font-size: 85%;"> ...
import {TranslateService} from '@ngx-translate/core';
import { LangService } from '../../providers/lang-service';
...
constructor( ...,
public translate: TranslateService,
public langService : LangService,
)
...
ionViewDidLoad() {
...
this.dateMsg = "APP.today";
this.langService.onLang.subscribe(lang=> {
this.lang = this.translate.currentLang;
console.log("[HomePage] Current lang: "+this.lang);
moment.locale(this.lang);
});
}
selectDate() {
const options: CalendarModalOptions = {
title: this.translate.instant("APP.date-choose"),
canBackwardsSelected: true,
closeLabel: this.translate.instant('APP.Cancel'),
doneLabel: this.translate.instant('APP.Done'),
...
}
...
doChangeLang() {
this.langService.setLanguage(this.lang);
}
}</code>
</pre>
<br />
On podem veure com:<br />
<ul>
<li>Assignem codis de traducció (p.e. a <i>dateMsg</i>) per mostrar-los traduïts en un html.</li>
<li>Utilitzem <i>this.translate.instant()</i> per traduir directament.</li>
</ul>
<ul>
<li>Es crida al servei <i>langService</i> per canviar l'idioma globalment.</li>
<li>Ens subscribim a <i>langService.onLang</i> per canviar el format del dia/hora segons el llenguatge actual.</li>
</ul>
També cal modificar <i>location-select.html</i> per traduir els missatges, tal com ho hem fet amb <i>home.html</i>.<br />
<br />
Però la part més delicada és modificar l'idioma de GoogleMaps. El llenguatge es fixa en crear el mapa, i fins on jo sé, l'API no permet canviar-lo després. Per això, ha calgut modificar el codi de <i>google-maps-service.ts</i> de la següent manera:<br />
<br />
<pre class="prettyprint"><code class="language-javascript" style="font-size: 85%;"> ...
import { LangService } from '../providers/lang-service';
...
constructor( ...,
public langService : LangService,
)
{
this.langService.onLang.subscribe(lang=> {
if(this.googleMapScriptElement) {
console.log("[GoogleMapsService] Current lang: "+lang);
this.updateScriptSrc();
}
});
}
...
updateScriptSrc() {
let lang = this.langService.getCurrentLang();
console.log("[GoogleMapsService].updateScriptSrc with lang: "+lang);
if(this.googleMapScriptElement) {
let head = document.getElementsByTagName('head')[0];
let scripts = Array.prototype.slice.call(head.getElementsByTagName('script'));
let styles = Array.prototype.slice.call(head.getElementsByTagName('style'));
scripts.forEach(s=> {
if(s.src.indexOf("://maps.google") != -1) head.removeChild(s);
else if(!s.src || s.src.indexOf("cordova.js") != -1 ||
s.src.indexOf("ion-dev.js") != -1) {}
else console.log("Skip removing <script src='"+s.src+"'");
});
styles.forEach(s=> {
if(s.textContent.indexOf(".gm-style") != -1) head.removeChild(s);
else console.log("Skip removing <style>", s);
});
document.body.removeChild(this.googleMapScriptElement);
}
let script = document.createElement("script");
script.id = "googleMaps";
if(this.apiKey){
script.src = 'http://maps.google.com/maps/api/js?key=' + this.apiKey +
'&callback=mapInit&libraries=places&language='+lang;
} else {
script.src = 'http://maps.google.com/maps/api/js?callback=mapInit&'
'amp;libraries=places&language='+lang;
}
this.googleMapScriptElement = document.body.appendChild(script);
}
...</code>
</pre>
<br />
Bàsicament, importem el <i>LangService</i> per subscriure'ns al canvi d'idioma. Quan es produeix, s'actualitza GoogleMaps, eliminant els scripts i estils que afegeix i recreant el mapa amb el nou llenguatge.<br />
<br />
Amb tot plegat, podem veure el resultat en la imatge:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg2dvGH_1AI2uj4ddGYbgfjJ_EhgqVuOjFVww0EDgqFqGYehLbrzT-M_dCOT3-wsV98OA-37vFCHMKA-WjepFWawUQM2hUWtK6blEDWH1ogBU07HoV75zhA6eTPXmhWOkDaatIICFbL2-yC/s1600/IonicApp_sunrise_19.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="834" data-original-width="1072" height="496" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg2dvGH_1AI2uj4ddGYbgfjJ_EhgqVuOjFVww0EDgqFqGYehLbrzT-M_dCOT3-wsV98OA-37vFCHMKA-WjepFWawUQM2hUWtK6blEDWH1ogBU07HoV75zhA6eTPXmhWOkDaatIICFbL2-yC/s640/IonicApp_sunrise_19.png" width="640" /></a></div>
<br />
<br />
<br />
Podem observar en la consola els missatges del <i>LangService</i> quan es detecta l'idioma i quan es canvia. També el nou control per canvi d'idioma en la pantalla.<br />
<br />
<i></i>
Si ens fixem en la consola, podem veure que la recreació del mapa de GoogleMaps no és del tot perfecte. Tot i eliminar els scripts i estils per evitar duplicats, probablement encara queda alguna cosa que és detectada en carregar el nou mapa. A la llarga, això aniria consumint memòria, però tampoc no canviarem d'idioma constantment, no?<br />
<br />
Com sempre, teniu tot el codi a la vostre disposició en el Github: <a href="https://github.com/jrierab/SunriseSunset/releases/tag/v0.5">v0.5</a> (amb traducció a diversos idiomes).<br />
<br />
Ara ja tenim una aplicació funcional i en diversos idiomes, però podem fer-hi encara alguns canvis estètics, amb icones personalitzades i tipus de lletra diferents; i finalment, no oblidem completar el projecte donant les dades de contacte i afegint-hi una pàgina per donar crèdit als projectes i persones que l'han fet possible.<br />
<br />
Seguiu l'evolució de l'aplicació:<br />
<ul>
<li>En <a href="https://anomenaidesa.blogspot.com/2018/03/ionic-aplicacio-per-saber-lhora-de_29.html">la setena part</a> personalitzarem icones i tipus de lletra.</li>
</ul>
Jordihttp://www.blogger.com/profile/10269271669040475159noreply@blogger.com0tag:blogger.com,1999:blog-2348072173208135260.post-215602612673214732018-03-14T22:27:00.001+01:002018-03-14T22:27:37.192+01:00Múltiples espais de treball (escriptoris) a UbuntuFa temps que m'he acostumat a treballar amb múltiples espais de treball (escriptoris). En concret, jo utilitzo una configuració de 2x4.<br />
<br />
Normalment, ho configuro amb alguna aplicació, però sempre em costa trobar el lloc concret on es defineix. Així que he buscat com fer-ho usant la línia de comanda i <a href="https://askubuntu.com/a/370048/206191">aquí teniu la solució</a>:<br />
<pre class="prettyprint"> $ gsettings set \
org.compiz.core:/org/compiz/profiles/unity/plugins/core/ vsize 2
$ gsettings set \
org.compiz.core:/org/compiz/profiles/unity/plugins/core/ hsize 4</pre>
<br />
I el resultat:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjuzkUaQ32y1stMjvwQe6XN_Av38zkV4VCCGy4DOd0Zo7BTTV1v_pBDEJ37aloBrnGHNgX-Qroik7m95JnJWdqcYdb2-sGudj6P5omFVC7vjIquq-I8mYbGAyrwRVGng2DYkwtF8OIJHlJC/s1600/n_workspaces.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="480" data-original-width="768" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjuzkUaQ32y1stMjvwQe6XN_Av38zkV4VCCGy4DOd0Zo7BTTV1v_pBDEJ37aloBrnGHNgX-Qroik7m95JnJWdqcYdb2-sGudj6P5omFVC7vjIquq-I8mYbGAyrwRVGng2DYkwtF8OIJHlJC/s640/n_workspaces.png" width="640" /></a></div>
<br />
Observeu al centre l'intercanviador d'escriptoris de 2x4 !!<br />
<br />
Fàcil i ràpid, eh?Jordihttp://www.blogger.com/profile/10269271669040475159noreply@blogger.com0tag:blogger.com,1999:blog-2348072173208135260.post-82125275756849672802018-03-07T19:52:00.000+01:002018-03-31T18:53:53.100+02:00Ionic: aplicació per saber l'hora de sortida/posta del sol (V)<b>Part V: Versió amb Google Maps</b><br />
<br />
Aquest article
ens mostrarà com utilitzar Google Maps per escollir el lloc en l'aplicació
per saber l'hora de sortida i posta de sol.<br />
<br />
Fins ara hem vist:<br />
<ul>
<li>En <a href="https://anomenaidesa.blogspot.com/2018/02/ionic-aplicacio-per-saber-lhora-de.html" target="_blank">la primera part</a> vàrem preparar el projecte inicial.</li>
<li>En <a href="https://anomenaidesa.blogspot.com/2018/02/ionic-aplicacio-per-saber-lhora-de_24.html">la segona part</a> vàrem crear l'aplicació, fixant el lloc.</li>
<li>En <a href="https://anomenaidesa.blogspot.com/2018/02/ionic-aplicacio-per-saber-lhora-de_27.html">la tercera part</a> vàrem afegir-hi geolocalització. </li>
<li>En <a href="https://anomenaidesa.blogspot.com/2018/03/ionic-aplicacio-per-saber-lhora-de.html">la quarta part</a> vàrem afegir-hi un calendari. </li>
</ul>
<br />
Ara utilitzarem Google Maps per poder escollir el lloc. Tot i que hi ha molta informació sobre com utilitzar Google Maps, la informació sol estar incompleta o no acabar-se d'adaptar a les nostres necessitats concretes.<br />
<br />
En general, una de les meves primeres fonts d'informació són els articles de <a href="https://www.joshmorony.com/#about">Josh Morony</a>. Són acurats i força actualitzats. Tota una referència. I just podem trobar-hi el que busquem: <a href="https://www.joshmorony.com/location-select-page-with-google-maps-and-ionic/">"com fer una pàgina de selecció de lloc amb Google Maps i Ionic"</a>. Seguirem el tutorial i fem els canvis necessaris per afegir la funcionalitat a la nostra aplicació.<br />
<br />
Afegim el mòdul de <a href="https://ionicframework.com/docs/native/network">xarxa</a>, que permetrà verificar quan tenim connexió de dades, i les definicions de tipus per Google Maps:<br />
<pre class="prettyprint"> $ ionic cordova plugin add cordova-plugin-network-information
$ npm install --save @ionic-native/network
$ npm install @types/google-maps --save</pre>
<br />
Seguint el tutorial, afegim la pàgina nova (<i>LocationSelect</i>) i els nous proveïdors a <i>app.module.ts</i>:<br />
<pre class="prettyprint"><code class="language-javascript" style="font-size: 85%;"> ...
import { CalendarModule } from "ion2-calendar";
import { LocationSelect } from '../pages/location-select/location-select';
import { Connectivity } from '../providers/connectivity-service';
import { GoogleMapsService } from '../providers/google-maps-service';
import { Network } from '@ionic-native/network';
...
declarations: [
...
HomePage,
LocationSelect,
TabsPage
],
...
entryComponents: [
...
HomePage,
LocationSelect,
TabsPage
],
providers: [
...
Geolocation,
Connectivity,
GoogleMapsService,
Network
]
...</code>
</pre>
<br />
Ara afegim les pàgines dels proveïdors de servei, segons el tutorial. En primer lloc, <i>providers/connectivity-service.ts</i>:
<br />
<pre class="prettyprint linenums"><code class="language-javascript" style="font-size: 85%;"> import { Injectable } from '@angular/core';
import { Network } from '@ionic-native/network';
import { Platform } from 'ionic-angular';
@Injectable()
export class Connectivity {
onDevice: boolean;
constructor(public platform: Platform, public network: Network) {
this.onDevice = this.platform.is('cordova');
}
isOnline(): boolean {
if(this.onDevice && this.network.type){
return this.network.type != 'none';
} else {
return navigator.onLine;
}
}
isOffline(): boolean {
if(this.onDevice && this.network.type){
return this.network.type == 'none';
} else {
return !navigator.onLine;
}
}
watchOnline(): any { return this.network.onConnect(); }
watchOffline(): any { return this.network.onDisconnect(); }
}</code></pre>
<br />
I tot seguit, <i>providers/google-maps-service.ts</i>:
<br />
<pre class="prettyprint linenums"><code class="language-javascript" style="font-size: 85%;"> import { Injectable } from '@angular/core';
import { Connectivity } from './connectivity-service';
import { Geolocation } from '@ionic-native/geolocation';
@Injectable()
export class GoogleMapsService {
mapElement: any;
pleaseConnect: any;
map: any;
mapInitialised: boolean = false;
mapLoaded: any;
mapLoadedObserver: any;
currentMarker: any;
apiKey: string; // = "YOUR_API_KEY";
lat: number = null;
lon: number = null;
constructor( public connectivityService: Connectivity,
public geolocation: Geolocation )
{
}
init( mapElement: any, pleaseConnect: any,
lat: number, lon: number
): Promise<any>
{
this.mapElement = mapElement;
this.pleaseConnect = pleaseConnect;
this.lat = lat;
this.lon = lon;
return this.loadGoogleMaps();
}
loadGoogleMaps(): Promise<any> {
return new Promise((resolve) => {
if(typeof google=="undefined" || typeof google.maps=="undefined") {
console.log("Google maps JavaScript needs to be loaded.");
this.disableMap();
if(this.connectivityService.isOnline()){
window['mapInit'] = () => {
this.initMap().then(() => {
resolve(true);
});
this.enableMap();
}
let script = document.createElement("script");
script.id = "googleMaps";
if(this.apiKey){
script.src = 'http://maps.google.com/maps/api/js?key=' +
this.apiKey + '&callback=mapInit&libraries=places';
} else {
script.src = 'http://maps.google.com/maps/api/js' +
'?callback=mapInit&libraries=places';
}
document.body.appendChild(script);
}
} else {
if(this.connectivityService.isOnline()){
this.initMap();
this.enableMap();
} else {
this.disableMap();
}
resolve(true);
}
this.addConnectivityListeners();
});
}
initMap(): Promise<any> {
this.mapInitialised = true;
return new Promise((resolve) => {
if(this.lat && this.lon) {
this.initMapPos(this.lat, this.lon);
resolve(true);
} else {
this.geolocation.getCurrentPosition().then((pos) => {
this.initMapPos(pos.coords.latitude, pos.coords.longitude);
resolve(true);
}).catch((error) => {
console.log('Error getting location'+JSON.stringify(error));
return Promise.reject("Unable to get location");
});
}
});
}
initMapPos(lat: number, lon: number) {
let latLng = new google.maps.LatLng(lat, lon);
let mapOptions = {
center: latLng,
zoom: 15,
mapTypeId: google.maps.MapTypeId.ROADMAP
}
this.map = new google.maps.Map(this.mapElement, mapOptions);
}
disableMap(): void {
if(this.pleaseConnect){
this.pleaseConnect.style.display = "block";
}
}
enableMap(): void {
if(this.pleaseConnect){
this.pleaseConnect.style.display = "none";
}
}
addConnectivityListeners(): void {
this.connectivityService.watchOnline().subscribe(() => {
setTimeout(() => {
if(typeof google=="undefined" || typeof google.maps=="undefined") {
this.loadGoogleMaps();
} else {
if(!this.mapInitialised) {
this.initMap();
}
this.enableMap();
}
}, 2000);
});
this.connectivityService.watchOffline().subscribe(() => {
this.disableMap();
});
}
}</any></any></any></code></pre>
<br />
En aquest fitxer hem fet alguns canvis:<br />
<ul>
<li>[6] Hem canviat el nom de la classe a <i>GoogleMapsService</i>.</li>
<li>[14] Hem eliminat l'<i>apiKey</i>. Se n'hauria de generar una per poder-la utilitzar en l'aplicació però, per fer proves, podem treballar sense.</li>
<li>[15-16] Declarem les variables per poder inicialitzar el mapa a partir d'una posició concreta, enlloc de la posició del nostre dispositiu.</li>
<li>[24-31] Afegim paràmetres per la posició desitjada a la funció <i>init</i> i els traslladem als corresponents camps de la classe. </li>
<li>[59] Hem afegit el paràmetre que faltava a la petició: <i>'&libraries=places'</i>.</li>
<li>[79-109] En la funció <i>initMap</i>, utilitzem la posició obtinguda durant la inicialització, o si està buida, usem el servei de geolocalització. També hem separat el codi d'inicialització de Google Maps en una nova funció <i>initMapPos</i> per no repetir codi.</li>
</ul>
<br />
Pel que fa a la pàgina <i>LocationSelect</i>, el fitxer d'estils <i>location-select/location-select.scss</i> no s'ha modificat, en el fitxer <i>location-select/location-select.html</i> només s'han traduït els textos al català, i el fitxer controlador <i>location-select/location-select.ts</i> s'ha modificat per poder passar la localització actual al servei <i>GoogleMapsService</i>:
<br />
<pre class="prettyprint linenums"><code class="language-javascript" style="font-size: 85%;"> ...
import { NavParams } from 'ionic-angular';
import { GoogleMapsService } from '../../providers/google-maps-service';
...
export class LocationSelect {
...
location: any;
lat: number = 42.582104;
lon: number = 1.0008419;
constructor( public navCtrl: NavController,
public params: NavParams,
public maps: GoogleMapsService,
public platform: Platform,
public geolocation: Geolocation,
public viewCtrl: ViewController)
{
this.searchDisabled = true;
this.saveDisabled = true;
this.lat = params.get('lat');
this.lon = params.get('lon');
}
ionViewDidLoad(): void {
this.maps.init( this.mapElement.nativeElement,
this.pleaseConnect.nativeElement,
this.lat, this.lon).then(() => {
this.autocompleteService =
new google.maps.places.AutocompleteService();
this.placesService =
new google.maps.places.PlacesService(this.maps.map);
this.searchDisabled = false;
});
}
...</code></pre>
<br />
On:<br />
<ul>
<li>[2] S'ha afegit la classe <i>NavParams</i> per poder passar paràmetres a la pàgina.</li>
<li>[8-9] S'han afegit membres a la classe per a guardar la posició inicial.</li>
<li>[12] S'utilitza el controlador <i>NavParams</i> en la classe, per a rebre els paràmetres.</li>
<li>[21-22] Es llegeixen i es guarden els paràmetres relacionats amb la posició.</li>
<li>[28] Els paràmetres de la posició s'utilitzen en la crida <i>init</i> del servei <i>GoogleMapsService</i>.</li>
</ul>
<br />
I finalment només ens queda modificar la pàgina <i>HomePage</i> per afegir la funcionalitat. En el controlador <i>home.ts</i>:
<br />
<pre class="prettyprint linenums"><code class="language-javascript" style="font-size: 85%;"> ...
import { LocationSelect } from '../location-select/location-select';
...
export class HomePage {
...
launchLocationPage() {
let modal = this.modalCtrl.create( LocationSelect,
{lat: this.lat, lon: this.lon});
modal.onDidDismiss((location) => {
if(location) {
console.log("Nou lloc: ", location);
this.lat = location.lat;
this.lon = location.lng;
this.posMsg = location.name;
this.posError = false;
this.getSunriseSunsetFromApi();
}
});
modal.present();
}
}</code></pre>
<br />
Hem afegit:<br />
<ul>
<li>[2] La importació de la nova pàgina per a seleccionar el lloc.</li>
<li>[6-22] La funció <i>launchLocationPage</i> per mostrar la finestra de selecció de lloc. Observem com passem els paràmetres de la posició actual a la nova pàgina [8], i com s'actualitzen les dades i missatges en cas d'haver escollit un lloc, en el retorn de la pàgina [13-17].</li>
</ul>
<br />
En la part de visualització <i>home.html</i>, simplement activem la crida a la funció anterior:
<br />
<pre class="prettyprint linenums"><code class="language-javascript" style="font-size: 85%;"> ...
<ion-item color="light" *ngIf="posError" (click)="launchLocationPage()">
<h2 text-wrap>No s'ha pogut obtenir la posició actual.</h2>
<p text-wrap>Verifica el GPS i/o els permisos.</p>
<ion-icon name="warning" color="danger" item-left></ion-icon>
</ion-item>
<ion-item (click)="launchLocationPage()">
<ion-label>Latitud</ion-label>
<ion-note item-right>{{ lat | number: "1.1-7" }}</ion-note>
</ion-item>
<ion-item (click)="launchLocationPage()">
<ion-label>Longitud</ion-label>
<ion-note item-right>{{ lon | number: "1.1-7" }}</ion-note>
</ion-item>
...</code></pre>
<br />
I ja ho tenim tot a punt ! Provem de seguida la nova funcionalitat:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjCXGAas-KkjTFUDOF8DEdnyDjpQZS5drlHsf3g0-NoKzQ4NFErRyEa9ffrf4KUuyllQLSEhDZTzzwjTHJ9VRIC93nOP0AAJhRuKCQuSvlOOZgvs0tZrPMHceiC09NZIIfNix_JxXTs9vkC/s1600/IonicApp_sunrise_17.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="705" data-original-width="772" height="584" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjCXGAas-KkjTFUDOF8DEdnyDjpQZS5drlHsf3g0-NoKzQ4NFErRyEa9ffrf4KUuyllQLSEhDZTzzwjTHJ9VRIC93nOP0AAJhRuKCQuSvlOOZgvs0tZrPMHceiC09NZIIfNix_JxXTs9vkC/s640/IonicApp_sunrise_17.png" width="640" /></a></div>
<br />
Podem veure com el mapa s'obre en una nova finestra superposada, si té prou espai, i com permet escollir un lloc, auto-completant les opcions. En escollir-ne una, el mapa es posició en ella i, en guardar-la, s'utilitza per calcular les hores de sortida/posta del sol en la nostra aplicació:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjzRyW4_FdeoIwYo8bzP7eYTgTTbSleC9FQUeW7qbpbMpC9OgoJiKfZ1uqrYdDR5RW8rkZIQfoYWXPsiS9ScpdTPec6lxNO2H1J_VFLGCpPbjjd_1ts8Fsh1yN8llvnmoUJ6jX2oDd6Tevu/s1600/IonicApp_sunrise_18.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="591" data-original-width="1103" height="342" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjzRyW4_FdeoIwYo8bzP7eYTgTTbSleC9FQUeW7qbpbMpC9OgoJiKfZ1uqrYdDR5RW8rkZIQfoYWXPsiS9ScpdTPec6lxNO2H1J_VFLGCpPbjjd_1ts8Fsh1yN8llvnmoUJ6jX2oDd6Tevu/s640/IonicApp_sunrise_18.png" width="640" /></a></div>
<br />
Podem veure en la consola l'objecte retornat pel servei de Google Maps. Les seves propietats s'utilitzen per actualitzar les dades en la pàgina principal.
<br />
Com sempre, teniu tot el codi a la vostre disposició en el Github: <a href="https://github.com/jrierab/SunriseSunset/releases/tag/v0.4">v0.4</a> (amb Google Maps).<br />
<br />
Ara ja tenim una aplicació funcional, però encara la podem completar en alguns aspectes. Per exemple, podríem afegir-hi traducció a diversos idiomes; també es poden fer alguns canvis estètics, amb icones personalitzades i tipus de lletra diferents; i finalment, completar el projecte donant les dades de contacte i afegint una pàgina per donar crèdit als projectes i persones que l'han fet possible.<br />
<br />
Seguiu l'evolució de l'aplicació:<br />
<ul>
<li>En <a href="https://anomenaidesa.blogspot.com/2018/03/ionic-aplicacio-per-saber-lhora-de_24.html">la sisena part</a> afegirem traduccions a diversos idiomes.</li>
<li>En <a href="https://anomenaidesa.blogspot.com/2018/03/ionic-aplicacio-per-saber-lhora-de_29.html">la setena part</a> personalitzarem icones i tipus de lletra.</li>
</ul>
<br />Jordihttp://www.blogger.com/profile/10269271669040475159noreply@blogger.com0tag:blogger.com,1999:blog-2348072173208135260.post-46843045007163218782018-03-02T18:12:00.000+01:002018-03-31T18:52:32.884+02:00Ionic: aplicació per saber l'hora de sortida/posta del sol (IV)<b>Part IV: Versió amb calendari</b><br />
<br />
Aquest article ens mostrarà com afegir un calendari per escollir el dia en l'aplicació per saber l'hora de sortida i posta de sol.<br />
<br />
Fins ara hem vist:<br />
<ul>
<li>En <a href="https://anomenaidesa.blogspot.com/2018/02/ionic-aplicacio-per-saber-lhora-de.html" target="_blank">la primera part</a> vàrem preparar el projecte inicial.</li>
<li>En <a href="https://anomenaidesa.blogspot.com/2018/02/ionic-aplicacio-per-saber-lhora-de_24.html">la segona part</a> vàrem crear l'aplicació, fixant el lloc.</li>
<li>En <a href="https://anomenaidesa.blogspot.com/2018/02/ionic-aplicacio-per-saber-lhora-de_27.html">la tercera part</a> vàrem afegir-hi geolocalització. </li>
</ul>
<br />
Ara afegirem un calendari senzill per poder escollir el dia. Tenim diferents opcions de calendari per Ionic i Angular. Si no necessitem res molt especial, el millor sol ser anar per una opció que estigui actualitzada, ben documentada i, si pot ser, amb una demostració que permeti veure bé la seva funcionalitat.<br />
<br />
Afegim el mòdul de <a href="https://www.npmjs.com/package/ion2-calendar">calendari ion2-calendar</a>, que sembla complir tot l'anterior, al projecte. Escollim aquest perquè permet obrir una finestra per escollir la data:<br />
<pre class="prettyprint"> $ npm install ion2-calendar moment --save</pre>
<br />
Seguint la documentació, afegim el mòdul a <i>app.module.ts</i>:<br />
<pre class="prettyprint"><code class="language-javascript" style="font-size: 85%;"> ...
import { CalendarModule } from "ion2-calendar";
...
imports: [
BrowserModule,
HttpClientModule,
CalendarModule,
IonicModule.forRoot(MyApp)
],
...</code>
</pre>
Ara implementem la funcionalitat desitjada en la pàgina <i>home.ts</i>:
<br />
<pre class="prettyprint linenums"><code class="language-javascript" style="font-size: 85%;">import { Component } from '@angular/core';
import { ModalController } from 'ionic-angular';
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
import { Geolocation } from '@ionic-native/geolocation';
import { CalendarModal,
CalendarModalOptions,
CalendarResult
} from "ion2-calendar";
import * as moment from 'moment';
@Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage {
API: string = "https://api.sunrise-sunset.org/json";
// Estany de Sant Maurici
lat: number = 42.582104;
lon: number = 1.0008419;
sunrise : string;
sunset : string;
date : Date = new Date();
dateMsg: string = "Avui";
posMsg : string;
posError : boolean = false;
constructor( public http: HttpClient,
private geolocation: Geolocation,
public modalCtrl: ModalController
)
{
moment.locale('ca-ES');
}
ionViewDidLoad() {
this.geolocation.getCurrentPosition().then((answer) => {
this.lat = answer.coords.latitude;
this.lon = answer.coords.longitude;
this.posMsg = "Posició actual";
this.getSunriseSunsetFromApi();
}).catch((error) => {
console.log('Error getting location', error);
this.posMsg = "Per defecte";
this.posError = true;
this.getSunriseSunsetFromApi();
});
}
selectDate() {
const options: CalendarModalOptions = {
title: 'Escull la data',
canBackwardsSelected: true,
closeLabel: "Cancel·lar",
doneLabel: "Fet",
weekdays: moment.weekdaysShort(),
weekStart: 1,
};
let myCalendar = this.modalCtrl.create(CalendarModal, {
options: options
});
myCalendar.present();
myCalendar.onDidDismiss((date: CalendarResult, type: string) => {
if(date) {
console.log(date);
this.date = date.dateObj;
this.dateMsg = this.date.toLocaleDateString("ca-ES");
this.getSunriseSunsetFromApi();
}
});
}
getSunriseSunsetFromApi() {
let data = { lat: this.lat.toString(),
lng: this.lon.toString(),
formatted: "0",
date: this.date.toISOString().slice(0,10)
};
console.log("Calling API with date= "+data.date);
const params = new HttpParams({fromObject: data});
const headers = new HttpHeaders().set("Accept", "application/json");
let options = {headers: headers, params: params, withCredentials: false};
this.http.get(this.API, options).subscribe(answer => {
if (answer['status']=="OK") {
let date_options = {hour: "2-digit", minute: "2-digit"};
this.sunrise = new Date(answer['results'].sunrise)
.toLocaleTimeString("ca-ES", date_options);
this.sunset = new Date(answer['results'].sunset)
.toLocaleTimeString("ca-ES", date_options);
}
},
err => console.log(err)
);
}
}</code>
</pre>
<br />
Observem les modificacions que hi hem fet:<br />
<ul>
<li>[6-10] Importem les classes necessàries pel calendari.</li>
<li>[26-27] Declarem noves variables per la data escollida.</li>
<li>[33] Afegim el <a href="https://ionicframework.com/docs/api/components/modal/ModalController/">controlador Modal de Ionic</a> per poder obrir el calendari en una finestra.</li>
<li>[36] Indiquem el llenguatge a utilitzar per a mostrar dates en el calendari.</li>
<li>[54-77] Declarem una nova funció <i>selectDate</i>, que ens permet obrir el calendari per escollir la data i assignar-la a les variables que hem declarat. Aquesta funció es cridarà a partir de la interfície d'usuari, com veurem en el codi de <i>home.html</i>. Els paràmetres de configuració del calendari els hem obtingut de la documentació del mòdul. A l'exemple de codi que es presenta en la documentació simplement hi hem afegit l'assignació a les variables i la crida a l'API.</li>
<li>[83] S'ha afegit un nou paràmetre a l'hora de cridar l'API per tal d'utilitzar la data seleccionada enlloc de l'actual.</li>
</ul>
<br />
Les modificacions fetes a <i>home.tml</i> són poques:<br />
<pre class="prettyprint linenums"><code class="language-html" style="font-size: 85%;"> ...
<ion-item-divider color="light">Data</ion-item-divider>
<ion-item (click)="selectDate()">
<ion-icon name="search" item-left color="primary"></ion-icon>
<ion-note item-right>{{ dateMsg }}</ion-note>
</ion-item>
...</code>
</pre>
Hi hem afegit una icona i hem canviat el missatge fixe pel contingut de la variable <i>dateMsg</i>, però la part més interessant és la captura de l'event <i>click</i> en el <i><ion-item></i>. Utilitzem les <a href="https://angular.io/guide/user-input">opcions per capturar les interaccions de l'usuari</a> que ens proporciona Angular. En aquest cas, el que es fa és cridar la funció <i>selectDate</i> del nostre controlador (la que obre el calendari i crida l'API amb la nova data seleccionada).<br />
<br />
Podem provar el codi en el navegador, com sempre amb:<br />
<pre class="prettyprint"> $ ionic serve -c -s
</pre>
En fer clic en la fila de la data, s'obre una finestra amb el calendari:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiXWB4v8kFSpY4_zh1aHBlCahZhW28zcG8LURf8QmnGxaApia7dh0-lhNPE3ApX_vG6wQTh4UurJke6qdArIPQUa_DA55R749VOGvQjvzL7n814AKYuiZ-gwzVQ9cvvEgMekLyTtUCZdyU6/s1600/IonicApp_sunrise_15.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="699" data-original-width="1364" height="326" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiXWB4v8kFSpY4_zh1aHBlCahZhW28zcG8LURf8QmnGxaApia7dh0-lhNPE3ApX_vG6wQTh4UurJke6qdArIPQUa_DA55R749VOGvQjvzL7n814AKYuiZ-gwzVQ9cvvEgMekLyTtUCZdyU6/s640/IonicApp_sunrise_15.png" width="640" /></a></div>
Podem veure en la consola l'objecte que rep el calendari. Tot seguit, en escollir una nova data i tancar la finestra, es crida l'API i la pantalla s'actualitza amb la nova informació:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgNEqIgcAP_5h_Fu3l7pB8IA337Gnosbwxw4IhUfSUeUCUoevXjBBN1_6W0QqMx80uiCk0dGyQyYLeYrypxx_gYBGrDLkHEIoSfxDfXyeDcXjmDx41gmYNvnRrfLZ16wRdoswsIY5ii1rgE/s1600/IonicApp_sunrise_16.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="699" data-original-width="1364" height="326" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgNEqIgcAP_5h_Fu3l7pB8IA337Gnosbwxw4IhUfSUeUCUoevXjBBN1_6W0QqMx80uiCk0dGyQyYLeYrypxx_gYBGrDLkHEIoSfxDfXyeDcXjmDx41gmYNvnRrfLZ16wRdoswsIY5ii1rgE/s640/IonicApp_sunrise_16.png" width="640" /></a></div>
<br />
Observem com s'ha actualitzat la data i les hores de sortida/posta del sol, i també podem comprovar en la consola com s'ha cridat l'API amb la data que s'havia escollit.<br />
<br />
Com sempre, teniu tot el codi a la vostre disposició en el Github: <a href="https://github.com/jrierab/SunriseSunset/releases/tag/v0.3">v0.3</a> (amb calendari).<br />
<br />
En la propera modificació, podríem permetre canviar el lloc, enlloc d'utilitzar la posició actual.<br />
<br />
Seguiu l'evolució de l'aplicació:<br />
<ul>
<li>En <a href="https://anomenaidesa.blogspot.com/2018/03/ionic-aplicacio-per-saber-lhora-de_7.html">la cinquena part</a> afegirem Google Maps per triar el lloc.</li>
<li>En <a href="https://anomenaidesa.blogspot.com/2018/03/ionic-aplicacio-per-saber-lhora-de_24.html">la sisena part</a> afegirem traduccions a diversos idiomes.</li>
<li>En <a href="https://anomenaidesa.blogspot.com/2018/03/ionic-aplicacio-per-saber-lhora-de_29.html">la setena part</a> personalitzarem icones i tipus de lletra.</li>
</ul>Jordihttp://www.blogger.com/profile/10269271669040475159noreply@blogger.com2tag:blogger.com,1999:blog-2348072173208135260.post-73266857747562926082018-02-27T19:04:00.000+01:002018-03-31T18:52:22.652+02:00Ionic: aplicació per saber l'hora de sortida/posta del sol (III)<b>Part III: Versió amb Geolocalització</b><br />
<br />
Aquest article ens mostrarà com afegir geolocalització a l'aplicació per saber l'hora de sortida i posta de sol justament del lloc on ens trobem.<br />
<br />
Fins ara hem vist:<br />
<ul>
<li>En <a href="https://anomenaidesa.blogspot.com/2018/02/ionic-aplicacio-per-saber-lhora-de.html" target="_blank">la primera part</a> vàrem preparar el projecte inicial.</li>
<li>En <a href="https://anomenaidesa.blogspot.com/2018/02/ionic-aplicacio-per-saber-lhora-de_24.html">la segona part</a> vàrem crear l'aplicació, fixant el lloc.</li>
</ul>
<br />
Ara afegirem geolocalització per obtenir la posició actual del mòbil. La precisió variarà depenent de si està el GPS actiu, o si el posicionament és per les antenes de ràdio, wifi, etc... Però pel que necessitem nosaltres, serà més que suficient.<br />
<br />
Afegim el mòdul de <a href="https://ionicframework.com/docs/native/geolocation/">Geolocalització del Ionic</a> al projecte:<br />
<pre class="prettyprint">$ ionic cordova plugin add cordova-plugin-geolocation \\
--variable GEOLOCATION_USAGE_DESCRIPTION="Per posicionar-te"
$ npm install --save @ionic-native/geolocation
</pre>
Ja veieu que es tracta d'un mòdul que requereix <i>Cordova</i>. Molts d'aquest mòduls només estan disponibles en un dispositiu físic (mòbil o <i>tablet</i>) i no en el navegador d'un PC. És important comprovar sempre la compatibilitat, per no tenir sorpreses.<br />
<br />
En el cas que ens ocupa, <i>Browser</i> apareix llistat com a compatible, pel que sí podem utilitzar-lo en el navegador d'un PC (això sí, ens demanarà permís abans de permetre la localització).<br />
<br />
Comprovem la <a href="https://ionicframework.com/docs/native/geolocation/">documentació</a> del propi mòdul per veure com utilitzar-lo:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiEykOmfgC0mQQxh7ZVYR9_2qjHSrZuzgjTRXE2M4kjCssAh5o979Za85YJ6nAwxtBOyGNOThZchL0H0AxGyHF24rN3UWtRn8BsKL48UHsOb9J4cdTIejjrIjp0TpFsxBdIFqjXew_ao5mS/s1600/IonicApp_sunrise_10.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="351" data-original-width="631" height="355" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiEykOmfgC0mQQxh7ZVYR9_2qjHSrZuzgjTRXE2M4kjCssAh5o979Za85YJ6nAwxtBOyGNOThZchL0H0AxGyHF24rN3UWtRn8BsKL48UHsOb9J4cdTIejjrIjp0TpFsxBdIFqjXew_ao5mS/s640/IonicApp_sunrise_10.png" width="640" /></a></div>
<br />
Aquest codi l'afegirem a la pàgina <i>home.ts</i>. Però no ens hem d'oblidar d'afegir també el proveïdor <i>Geolocation</i> en el fitxer <i>app.module.ts</i>. Ens cas contrari, generaria un error de l'estil:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj4eZYNNxFGrBV6JO45c9DMRSTNExMlKKS_IfYjbERlIhUJuWZFW94p1BmFfKXq8c3iZ_gEyb9q3bRXxJRJGe28_jarOtBBk48WR9zIDQoUiryw9_8A1vsCKRmrN_LHXqQdyjxUnAdi37VX/s1600/IonicApp_sunrise_11.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="237" data-original-width="1213" height="124" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj4eZYNNxFGrBV6JO45c9DMRSTNExMlKKS_IfYjbERlIhUJuWZFW94p1BmFfKXq8c3iZ_gEyb9q3bRXxJRJGe28_jarOtBBk48WR9zIDQoUiryw9_8A1vsCKRmrN_LHXqQdyjxUnAdi37VX/s640/IonicApp_sunrise_11.png" width="640" /></a></div>
<br />
Ja que hi estem posats, organitzarem el codi separant la crida a l'API en una funció separada. Ens anirà bé, ja que la cridarem en un parell de situacions diferents.<br />
<br />
També aprofitarem per generar un missatge indicant si la posició en la que es calcula és l'actual o la que hi ha per defecte (en cas de no poder usar la geolocalització). En aquest cas, donarem també un avís en la pantalla, en forma d'error o advertiment.<br />
<br />
Bé, el codi quedaria com segueix:<br />
<pre class="prettyprint linenums"><code class="language-javascript" style="font-size: 85%;">import { Component } from '@angular/core';
import { NavController } from 'ionic-angular';
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
import { Geolocation } from '@ionic-native/geolocation';
@Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage {
API: string = "https://api.sunrise-sunset.org/json";
// Estany de Sant Maurici
lat: number = 42.582104;
lon: number = 1.0008419;
sunrise : string;
sunset : string;
posMsg : string;
posError : boolean = false;
constructor( public navCtrl: NavController,
public http: HttpClient,
private geolocation: Geolocation
)
{ }
ionViewDidLoad() {
this.geolocation.getCurrentPosition().then((answer) => {
//console.log(answer);
this.lat = answer.coords.latitude;
this.lon = answer.coords.longitude;
this.posMsg = "Posició actual";
this.getSunriseSunsetFromApi();
}).catch((error) => {
console.log('Error getting location', error);
this.posMsg = "Per defecte";
this.posError = true;
this.getSunriseSunsetFromApi();
});
}
getSunriseSunsetFromApi() {
console.log("Calling API...");
let data = { lat: this.lat.toString(),
lng: this.lon.toString(),
formatted: "0"
};
const params = new HttpParams({fromObject: data});
const headers = new HttpHeaders().set("Accept", "application/json");
let options = {headers: headers, params: params, withCredentials: false};
this.http.get(this.API, options).subscribe(answer => {
console.log(answer);
if (answer['status']=="OK") {
let date_options = {hour: "2-digit", minute: "2-digit"};
this.sunrise = new Date(answer['results'].sunrise)
.toLocaleTimeString("ca-ES", date_options);
this.sunset = new Date(answer['results'].sunset)
.toLocaleTimeString("ca-ES", date_options);
console.log(this.sunrise);
console.log(this.sunset);
}
},
err => console.log(err)
);
}
}</code>
</pre>
<br />
Observem els canvis:<br />
<ul>
<li>[5] Importem el nou mòdul de geolocalització.</li>
<li>[21-22] Declarem les noves variables pel missatge del lloc i el possible error. </li>
<li>[26] Afegim el nou proveïdor de geolocalització.</li>
<li>[30-44] Ara, en carregar la pàgina provem d'obtenir la posició actual tal com en indica la documentació del mòdul. Si tot és correcte, obtenim la posició; en cas contrari, utilitzem la que tenim per defecte. En tot cas, actualitzem un missatge per l'usuari i una senyal d'error.</li>
<li>[46-75] La crida a l'API que calcula la sortida/posta de sol l'hem separat en una funció pròpia. A part d'això, no hi hem fet canvis.</li>
</ul>
<br />
També cal modificar el fitxer <i>home.html</i> per mostrar el nou missatge i el possible error:<br />
<pre class="prettyprint"><code class="language-html" style="font-size: 85%;"> ...
<ion-content padding>
<ion-list>
<ion-item-divider color="light">Lloc: {{ posMsg }}</ion-item-divider>
<ion-item color="light" *ngIf="posError">
<h2>No s'ha pogut obtenir la posició actual</h2>
<p>Verifica els permisos</p>
<ion-icon name="warning" color="danger" item-left></ion-icon>
</ion-item>
...</code>
</pre>
<br />
Només hi hem afegit un <i>ion-item</i> per mostrar un error en cas que no es pugui utilitzar la geolocalització. Fixem-nos en l'ús de l'operador <a href="https://angular.io/api/common/NgIf"><i>*ngIf</i></a> d'Angular. Amb ell, només es mostrarà l'element (i tot el seu contingut) si es compleix la condició indicada (en aquest cas, si la variable <i>posError</i> s'avalua com a certa [línia 41, quan salta un error en provar de geolocalitzar]).<br />
<br />
El resultat, quan salta l'error, es pot veure a continuació:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhmwcCcQJ4bdH9O66nZV-T0_VzrVntDIPu9_V8msuHG9poKDOkiGtEZXxWc9P9ILg8HeNUhUBZJEyCmcdfkvGjAkJOnSUY0BvGtd5MD3d9TgwXO1IEj1h8OFmGlcnvAGwBm9Puc0Asck4UU/s1600/IonicApp_sunrise_12.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="679" data-original-width="1089" height="398" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhmwcCcQJ4bdH9O66nZV-T0_VzrVntDIPu9_V8msuHG9poKDOkiGtEZXxWc9P9ILg8HeNUhUBZJEyCmcdfkvGjAkJOnSUY0BvGtd5MD3d9TgwXO1IEj1h8OFmGlcnvAGwBm9Puc0Asck4UU/s640/IonicApp_sunrise_12.png" width="640" /></a></div>
<br />
Si mirem els missatges en la consola, podem veure que l'error que es produeix és un <a href="https://en.wikipedia.org/wiki/HTTP_403">403</a> (intent d'accedir a un recurs sense permisos adients).<br />
<br />
En canvi, si li donem els permisos adequats, el resultat que obtenim és:<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEihIXG_k7k-YnWb7cOdcmA7dTNUkvQk7CM1KeKWj7KimZiBxBN1T5xxmdxShneSMh6sMNWqqaMGmdIwKzT-ojSa66aXCT2O8m6-hIaUoRNF4kl-4lsAgyfynyNhJze7Nuvn80X3pPDnuSWb/s1600/IonicApp_sunrise_14.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="624" data-original-width="912" height="436" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEihIXG_k7k-YnWb7cOdcmA7dTNUkvQk7CM1KeKWj7KimZiBxBN1T5xxmdxShneSMh6sMNWqqaMGmdIwKzT-ojSa66aXCT2O8m6-hIaUoRNF4kl-4lsAgyfynyNhJze7Nuvn80X3pPDnuSWb/s640/IonicApp_sunrise_14.png" width="640" /></a></div>
<br />
Noteu com el codi corresponent a l'error ja no es mostra ! En canvi, en la consola podem veure l'objecte que ens retorna el mòdul de geolocalització.<br />
<br />
I d'aquesta manera tant senzilla hem aconseguit geolocalitzar l'aplicació. Com sempre, teniu tot el codi a la vostre disposició en el Github: <a href="https://github.com/jrierab/SunriseSunset/releases/tag/v0.2">v0.2</a> (amb geolocalització).<br />
<br />
En la propera modificació, podríem afegir un calendari per escollir la data que volem, enlloc d'utilitzar l'actual.<br />
<br />
Seguiu l'evolució de l'aplicació:<br />
<ul>
<li>En <a href="https://anomenaidesa.blogspot.com/2018/03/ionic-aplicacio-per-saber-lhora-de.html">la quarta part</a> afegirem un calendari. </li>
<li>En <a href="https://anomenaidesa.blogspot.com/2018/03/ionic-aplicacio-per-saber-lhora-de_7.html">la cinquena part</a> afegirem Google Maps per triar el lloc.</li>
<li>En <a href="https://anomenaidesa.blogspot.com/2018/03/ionic-aplicacio-per-saber-lhora-de_24.html">la sisena part</a> afegirem traduccions a diversos idiomes.</li>
<li>En <a href="https://anomenaidesa.blogspot.com/2018/03/ionic-aplicacio-per-saber-lhora-de_29.html">la setena part</a> personalitzarem icones i tipus de lletra.</li>
</ul>
<br />Jordihttp://www.blogger.com/profile/10269271669040475159noreply@blogger.com0tag:blogger.com,1999:blog-2348072173208135260.post-50762109752191117872018-02-24T12:36:00.000+01:002018-03-31T18:52:12.496+02:00Ionic: aplicació per saber l'hora de sortida/posta del sol (II)<b>Part II: Versió "clavada a ferro"</b><br />
<br />
Aquest article ens mostrarà com fer una aplicació amb Ionic per saber l'hora de sortida i posta de sol en un lloc determinat.<br />
<br />
En <a href="https://anomenaidesa.blogspot.com/2018/02/ionic-aplicacio-per-saber-lhora-de.html" target="_blank">la primera part</a> vàrem preparar el projecte inicial.<br />
<br />
Ara farem una primera versió senzilla, on fixarem el lloc i el dia. Més endavant anirem modificant el codi per fer-lo més útil en general (i de passada anirem veien diferents tècniques).<br />
<br />
En lloc de fer el càlcul de l'hora de sortida/posta de sol directament, l'aplicació es connectarà a internet per a obtenir-la. Amb una cerca ràpida a Google podem trobar una API que sembla feta a mida a <a href="https://sunrise-sunset.org/api" target="_blank">https://sunrise-sunset.org/api</a>.<br />
<br />
En una primera versió, podem fer una petició simple amb:<br />
<pre class="prettyprint lang-sh"> https://api.sunrise-sunset.org/json?
lat=36.7201600&lng=-4.4203400&formatted=0</pre>
<br />
I obtindrem una resposta en JSON similar a:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjnJNRbZlUqsBJZqyspZsBmgkIQ4rzFjeirTKWJyOt1ZCpXHIt78ineChJb0lqwzXJplA9YGIONLjslhmhXP7vS7OaWQdsTmxmD6-YzmieajVih6_KuiruRxZM2TW6cV-KS3uJanz_Xlx77/s1600/IonicApp_sunrise_04.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="313" data-original-width="663" height="302" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjnJNRbZlUqsBJZqyspZsBmgkIQ4rzFjeirTKWJyOt1ZCpXHIt78ineChJb0lqwzXJplA9YGIONLjslhmhXP7vS7OaWQdsTmxmD6-YzmieajVih6_KuiruRxZM2TW6cV-KS3uJanz_Xlx77/s640/IonicApp_sunrise_04.png" width="640" /></a></div>
<br />
Fixeu-vos que només necessitem la latitud i la longitud i fer una petició a l'API anterior per obtenir les dades que necessitem en la nostre aplicació.<br />
<br />
En una aplicació Ionic, per poder fer peticions web, primer hem d'importar el mòdul <i>HttpClientModule</i> al fitxer <i>app.module.ts</i>. Quedaria com segueix:<br />
<pre class="prettyprint"><code class="language-javascript" style="font-size: 85%;"> ...
import { SplashScreen } from '@ionic-native/splash-screen';
import { HttpClientModule } from '@angular/common/http';
@NgModule({
declarations: [
...
],
imports: [
BrowserModule,
HttpClientModule,
IonicModule.forRoot(MyApp)
],
bootstrap: [IonicApp],
...
</code></pre>
<br />
Amb això ja podem implementar la funcionalitat desitjada en la pàgina <i>home.ts</i>:<br />
<pre class="prettyprint linenums"><code class="language-javascript" style="font-size: 85%;"> import { Component } from '@angular/core';
import { NavController } from 'ionic-angular';
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
@Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage {
API: string = "https://api.sunrise-sunset.org/json";
// Estany de Sant Maurici
lat: number = 42.582104;
lon: number = 1.0008419;
sunrise : string;
sunset : string;
constructor( public navCtrl: NavController,
public http: HttpClient )
{ }
ionViewDidLoad() {
console.log("Calling API...");
let data = { lat: this.lat.toString(),
lng: this.lon.toString(),
formatted: "0"
};
const params = new HttpParams({fromObject: data});
const headers = new HttpHeaders().set("Accept", "application/json");
let options = { headers: headers,
params: params,
withCredentials: false
};
this.http.get(this.API, options).subscribe(answer => {
console.log(answer);
if (answer['status']=="OK") {
let date_options = {hour: "2-digit", minute: "2-digit"};
this.sunrise = new Date(answer['results'].sunrise)
.toLocaleTimeString("ca-ES", date_options);
this.sunset = new Date(answer['results'].sunset)
.toLocaleTimeString("ca-ES", date_options);
console.log(this.sunrise);
console.log(this.sunset);
}
},
err => console.log(err)
);
}
}
</code></pre>
<br />
Observem l'estructura:<br />
<ul>
<li>[4] Hem importat les classes que necessitarem.</li>
<li>[11-18] Declarem les variables internes a la classe, amb el seu tipus (gràcies, Typescript !) i opcionalment amb un valor per defecte. Aquí fixarem el valor de la latitud i la longitud del lloc que ens interessi a partir de les dades GPS (que podem obtenir a través de google maps, per exemple).</li>
<li>[21] Afegim el proveïdor per a les connexions a internet en el constructor.</li>
<li>[24] <i>ionViewDidLoad</i> és una funció que es crida una sola vegada, i és el lloc ideal per posar-hi codi d'inicialització. En aquest cas, com que les nostres dades no varien gaire sovint, les podem inicialitzar aquí. Val la pena consultar els cicles de vida d'una pàgina a Ionic a <a href="https://ionicframework.com/docs/api/navigation/NavController/">NavController</a>.</li>
<li>[27-30] Preparem les dades en el format requerit per l'API. Necessitem convertir els números en <i>strings</i>.</li>
<li>[32-37] Generem els paràmetre i capçaleres de la nostre petició a l'API, i finalment ho posem tot plegat en un objecte <i>options</i>.</li>
<li>[39-55] Fem la petició i quan obtenim la resposta, extraiem les dades que volem mostrar. En cas d'error, traiem un missatge per la consola.</li>
</ul>
Val la pena comentar que la petició web retornarà les dades al cap d'un temps indeterminat. Per això, el valor es retorna a través d'un <a href="http://reactivex.io/documentation/observable.html">Observable</a>, al qual ens subscrivim per rebre el seu valor quan estigui disponible.<br />
<br />
Fixem-nos com el fitxer <i>home.ts</i> actua com a controlador de les dades de la classe. Per la seva banda, la visualització d'aquestes es fa en el fitxer <i>home.html</i>:<br />
<pre class="prettyprint linenums"><code class="language-html" style="font-size: 85%;"><ion-header>
<ion-navbar>
<ion-title>Sortida/posta de Sol</ion-title>
</ion-navbar>
</ion-header>
<ion-content padding>
<ion-list>
<ion-item-divider color="light">Lloc</ion-item-divider>
<ion-item>
<ion-label>Latitud</ion-label>
<ion-note item-right>{{ lat }}</ion-note>
</ion-item>
<ion-item>
<ion-label>Longitud</ion-label>
<ion-note item-right>{{ lon }}</ion-note>
</ion-item>
<ion-item-divider color="light">Data</ion-item-divider>
<ion-item>
<ion-note item-right>Avui</ion-note>
</ion-item>
<ion-item-divider color="light">Horari</ion-item-divider>
<ion-item>
<ion-icon name="sunny" color="orange" item-left></ion-icon>
<ion-note item-right>{{ sunrise }}</ion-note>
</ion-item>
<ion-item>
<ion-icon name="moon" color="blue" item-left></ion-icon>
<ion-note item-right>{{ sunset }}</ion-note>
</ion-item>
</ion-list>
</ion-content></code>
</pre>
<br />
L'estructura utilitza <i>tags</i> tipus <i>html</i> que, de fet, són <a href="https://ionicframework.com/docs/components/">components pre-definits de Ionic</a>. Amb això podem construir una interfície d'usuari agradable sense molt d'esforç: amb llistes, icones, etiquetes, etc...<br />
<br />
El més destacable és com podem utilitzar en el fitxer <i>html</i> les variables que tenim definides en el nostre controlador, gràcies als operadors d'<a href="https://angular.io/guide/template-syntax">enllaç de dades d'Angular</a>. En la nostre aplicació només utilitzem l'operador d'interpolació, <i>{{ var }},</i> que serà substituït en la pàgina de sortida pel valor que tingui la variable en la classe en cada moment.<br />
<br />
Bé, ha arribat el moment de veure el resultat:<br />
<pre class="prettyprint"> $ ionic serve -c -s</pre>
<br />
El resultat ens apareixerà en una finestra nova al navegador:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgox-RwX7RMqrtDJyIGcUtP2JAPDQmaWe55mat0fOLb0buDMyh0Q4Eoqj2CR7_-wU-NAyGjPQgGKVpI5XGEgfv2Q6RHFfjrxGaP8nDvGxN8oDoIv2xaijA02k0uDwrFunNy2im59zHY1prG/s1600/IonicApp_sunrise_05.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="644" data-original-width="1014" height="406" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgox-RwX7RMqrtDJyIGcUtP2JAPDQmaWe55mat0fOLb0buDMyh0Q4Eoqj2CR7_-wU-NAyGjPQgGKVpI5XGEgfv2Q6RHFfjrxGaP8nDvGxN8oDoIv2xaijA02k0uDwrFunNy2im59zHY1prG/s640/IonicApp_sunrise_05.png" width="640" /></a></div>
<br />
Observem com gràcies a les "Eines de desenvolupador" podem veure els missatges que surten per la consola. En el cas d'un objecte com el que obtenim de la petició a l'API [línia 40], el podem expandir i veure'n tots els detalls.<br />
<br />
Però anem a veure com es genera l'aplicació mòbil. De fet, si tenim els SDK d'Android i el XCode ja instal·lats i configurats, és tan senzill com:<br />
<pre class="prettyprint"> $ ionic cordova build android
$ ionic cordova build ios</pre>
<br />
El resultat es pot veure a continuació:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgUa5SF7GHmWCka3OyrUm6oRuLsEw4lyJfbxZUQZn3gmEhyTtXx6bVaA3fCTLR6pRgQQu7FwIscr9FNK03HWrWBPuQCgrhoUle4vextbOjG2k2fOnxdZn1wjRVDPGwTC7JOy6AYEVM5mmFA/s1600/IonicApp_sunrise_06.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="600" data-original-width="675" height="568" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgUa5SF7GHmWCka3OyrUm6oRuLsEw4lyJfbxZUQZn3gmEhyTtXx6bVaA3fCTLR6pRgQQu7FwIscr9FNK03HWrWBPuQCgrhoUle4vextbOjG2k2fOnxdZn1wjRVDPGwTC7JOy6AYEVM5mmFA/s640/IonicApp_sunrise_06.png" width="640" /></a></div>
<br />
A l'esquerra hi ha una captura de pantalla directe des d'un Samsung S6, i a la dreta el resultat en l'emulador d'un iPhone 7.<br />
<br />
Tal com es pot observar, <b>Ionic ens permet obtenir una aplicació que corre en els dos principals sistemes operatius mòbils</b> (i en la web, tot sigui dit de passada) <b>a partir d'un mateix codi</b>.<br />
<br />
I parlant de codi, si us animeu a provar l'aplicació, la teniu disponible al GitHub: <a href="https://github.com/jrierab/SunriseSunset/releases/tag/v0.1">v0.1</a> (clavada a ferro). Proveu-la, i ja em comentareu.<br />
<br />
Més endavant anirem modificant l'aplicació per permetre variar el lloc i la data, la qual cosa ens permetrà veure noves tècniques de programació, ús del GPS en els dispositius mòbils, etc...
<br />
Seguiu l'evolució de l'aplicació:<br />
<ul>
<li>En <a href="https://anomenaidesa.blogspot.com/2018/02/ionic-aplicacio-per-saber-lhora-de_27.html">la tercera part</a> afegirem geolocalització. </li>
<li>En <a href="https://anomenaidesa.blogspot.com/2018/03/ionic-aplicacio-per-saber-lhora-de.html">la quarta part</a> afegirem un calendari. </li>
<li>En <a href="https://anomenaidesa.blogspot.com/2018/03/ionic-aplicacio-per-saber-lhora-de_7.html">la cinquena part</a> afegirem Google Maps per triar el lloc.</li>
<li>En <a href="https://anomenaidesa.blogspot.com/2018/03/ionic-aplicacio-per-saber-lhora-de_24.html">la sisena part</a> afegirem traduccions a diversos idiomes.</li>
<li>En <a href="https://anomenaidesa.blogspot.com/2018/03/ionic-aplicacio-per-saber-lhora-de_29.html">la setena part</a> personalitzarem icones i tipus de lletra.</li>
</ul>
<br />Jordihttp://www.blogger.com/profile/10269271669040475159noreply@blogger.com0tag:blogger.com,1999:blog-2348072173208135260.post-63946688246559713152018-02-22T19:03:00.000+01:002018-03-31T18:51:56.811+02:00Ionic: aplicació per saber l'hora de sortida/posta del sol (I)<b>Part I: Preparació</b><br />
<br />
Per mostrar les excel·lències de Ionic, anem a fer una aplicació senzilla. En aquest cas, una que ens indiqui a quina hora surt i s'amaga el sol en un lloc determinat, en un dia determinat. Pot ser interessant per una sortida romàntica o en el seu defecte, per fotògrafs solitaris ;-)<br />
<br />
Si no coneixeu Ionic o encara no l'heu instal·lat, podeu llegir <a href="https://anomenaidesa.blogspot.com/2018/02/ionic-aplicacions-multi-plataforma-amb.html" target="_blank">el meu article</a> al respecte.<br />
<br />
En primer lloc, crearem l'aplicació partint d'una base que ja ens dona el propi Ionic. Per això fem:<br />
<pre class="prettyprint lang-sh">$ ionic start SunriseSunset</pre>
<br />
Escollim la base '<i>tabs</i>', indiquem que volem generar codi iOs/Android i comença la instal·lació. Quan ens pregunti si volem integrar l'aplicació amb <a href="https://ionicframework.com/pro" target="_blank">Ionic Pro</a>, li podem dir que no, de moment.<br />
<br />
El resultat és un directori <i>SunriseSunset</i> ja preparat. Podem comprovar que tot ha anat bé amb:<br />
<pre class="prettyprint lang-sh">$ cd SunriseSunset
$ ionic serve -c -s</pre>
<br />
Això compilarà l'aplicació i ens obrirà una finestra en el navegador per defecte. En el Chromium, obrim les "Eines per a desenvolupadors" i la barra de selecció de dispositius i se'ns mostrarà alguna cosa similar a:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgKasOqrVcne3CJeh-StvcImRZbSFqDWgvymkN_QEo2mG8qWdO2gGYLOi9xZBYcSmAQ97bzMB3c4qg2ieP0g5ezv2pCKw9bQeLXc3QF9qrcGdULgvjqu_DiwblS_TlA3kS3hBWZcucKN9Bg/s1600/IonicApp_sunrise_01.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="848" data-original-width="1191" height="452" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgKasOqrVcne3CJeh-StvcImRZbSFqDWgvymkN_QEo2mG8qWdO2gGYLOi9xZBYcSmAQ97bzMB3c4qg2ieP0g5ezv2pCKw9bQeLXc3QF9qrcGdULgvjqu_DiwblS_TlA3kS3hBWZcucKN9Bg/s640/IonicApp_sunrise_01.png" width="640" /></a></div>
<br />
Podem interactuar amb les icones tal com ho faríem en un dispositiu mòbil. També veurem els missatges que vagin apareixent en la consola, la qual cosa ens serà molt útil per depurar el codi, més endavant.<br />
<br />
Si comprovem els missatges que ens ha donat durant la creació del directori font, podrem veure com ja està tot preparat per treballar amb el <a href="https://git-scm.com/" target="_blank">git</a> (que és sistema de control de versions més utilitzat en l'actualitat). Per defecte està pensat per treballar amb Ionic Pro, però res no ens impedeix de fer-ho en el nostre compte personal a <a href="https://github.com/" target="_blank">Github</a>.<br />
<br />
Primer hem de crear un repositori remot a Github i després associar-hi el nostre directori de treball. Ho podem fer seguint les indicacions de <a href="https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/" target="_blank">https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/</a> amb:<br />
<pre class="prettyprint lang-sh">$ git config --global user.email "el_teu_correu@email.com"
$ git config --global user.name "El Teu Nom"
$ git config --global push.default simple
$ git commit -m "Ionic. Template tabs."
$ git remote add origin https://github.com/_usuari_/SunriseSunset.git
$ <span style="color: #24292e; font-family: , "consolas" , "liberation mono" , "menlo" , "courier" , monospace; font-size: 14px;">git push -u origin master</span>
</pre>
<br />
Tot seguit comprovem com el nostre repositori a Github ja conté el codi:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhSx64NPg-4i3h4AW9MMG6rYz0zBYdQ3hd9hmiOBy9Pi3p6kJAwz9POIK6vke5Yv0lw70ry1mEzUIWPoTFgr-PVCw3hS8CjgqXxvVj4smxSIBBmyZgstmCyKJpa9tK4OV8ipw4UVHB8qVgW/s1600/IonicApp_sunrise_02.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="816" data-original-width="1018" height="513" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhSx64NPg-4i3h4AW9MMG6rYz0zBYdQ3hd9hmiOBy9Pi3p6kJAwz9POIK6vke5Yv0lw70ry1mEzUIWPoTFgr-PVCw3hS8CjgqXxvVj4smxSIBBmyZgstmCyKJpa9tK4OV8ipw4UVHB8qVgW/s640/IonicApp_sunrise_02.png" width="640" /></a></div>
Per editar el codi, recomano el <a href="https://code.visualstudio.com/" target="_blank">VisualStudioCode</a>. És un editor lleuger que ens permetrà treballar sense distraccions i de forma eficient, amb compleció de codi, ajuda en línia i una multitud d'altres funcionalitats que anireu descobrint sobre la marxa.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjBAmqcQyTHx1xTU0Zp3Eg5WRwWHFqI2KsSw8ht2StYrGOzTxnIuwEU1Vq2DK3p6cJb35GG2I2vY1AZHR7SXjf01qeQP7CqSOm9mrxGaLwdrkTNguicf1u87oG4YV2S0HFfvSEo6QXPr2qc/s1600/IonicApp_sunrise_03.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="866" data-original-width="1091" height="508" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjBAmqcQyTHx1xTU0Zp3Eg5WRwWHFqI2KsSw8ht2StYrGOzTxnIuwEU1Vq2DK3p6cJb35GG2I2vY1AZHR7SXjf01qeQP7CqSOm9mrxGaLwdrkTNguicf1u87oG4YV2S0HFfvSEo6QXPr2qc/s640/IonicApp_sunrise_03.png" width="640" /></a></div>
<br />
En la imatge es pot veure l'estructura de fitxers del projecte (a l'esquerra) i el codi HTML modificat amb <i>tags</i> propis del Ionic.<br />
<br />
Amb això acabem les tasques de preparació i ja ho tenim tot a punt per a implementar el que vulguem.<br />
<br />
Ben aviat comprovarem el senzill que resulta fer l'aplicació que ens hem proposat. Serà en la segona part :-)<br />
<br />
Seguiu l'evolució de l'aplicació:<br />
<ul>
<li>En <a href="https://anomenaidesa.blogspot.com/2018/02/ionic-aplicacio-per-saber-lhora-de_24.html">la segona part</a> crearem l'aplicació, fixant el lloc.</li>
<li>En <a href="https://anomenaidesa.blogspot.com/2018/02/ionic-aplicacio-per-saber-lhora-de_27.html">la tercera part</a> afegirem geolocalització. </li>
<li>En <a href="https://anomenaidesa.blogspot.com/2018/03/ionic-aplicacio-per-saber-lhora-de.html">la quarta part</a> afegirem un calendari.</li>
<li>En <a href="https://anomenaidesa.blogspot.com/2018/03/ionic-aplicacio-per-saber-lhora-de_7.html">la cinquena part</a> afegirem Google Maps per triar el lloc.</li>
<li>En <a href="https://anomenaidesa.blogspot.com/2018/03/ionic-aplicacio-per-saber-lhora-de_24.html">la sisena part</a> afegirem traduccions a diversos idiomes.</li>
<li>En <a href="https://anomenaidesa.blogspot.com/2018/03/ionic-aplicacio-per-saber-lhora-de_29.html">la setena part</a> personalitzarem icones i tipus de lletra.</li>
</ul>
<br />Jordihttp://www.blogger.com/profile/10269271669040475159noreply@blogger.com0tag:blogger.com,1999:blog-2348072173208135260.post-70323655106801882942018-02-18T13:19:00.000+01:002018-02-18T13:19:12.999+01:00Ionic: aplicacions multi-plataforma amb el mateix codiCom qui no vol la cosa, porto treballant amb <a href="https://ionicframework.com/" target="_blank">Ionic</a> des de fa gairebé un any i mig. Vaig arribar a <a href="https://ionicframework.com/docs/v1/" target="_blank">Ionic v1</a> buscant una plataforma que permetés desenvolupar aplicacions multi-plataforma (Android, iOS i web) amb els mínims canvis de codi. Justament això era el que oferia Ionic v1.<br />
<br />
Però no tot era perfecte. Al cap d'un mes, vaig voler provar una de les betes de Ionic 2. I ja no hi va haver tornada enrere. La nova versió de Ionic afegia el que li mancava a Ionic v1: una millor estructuració del codi, orientació a objectes i SOBRETOT verificació de tipus gràcies a la substitució del javascript pel <a href="https://www.typescriptlang.org/" target="_blank">Typescript</a> com a llenguatge base de programació.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgGEh8TlCovo7mJM2QDfnJpF98EN1LCA2s96lK3etSzRZNaol9EYCOz3YxUeyN8WDHMc0jVOaqbloxeCuU-c07lQw2B2zwngolAxb8Kt4zHt47Hyxt-ZXeXMtb-W78ci3LtfUMiF3-sXvIa/s1600/Ionic.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="710" data-original-width="1170" height="387" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgGEh8TlCovo7mJM2QDfnJpF98EN1LCA2s96lK3etSzRZNaol9EYCOz3YxUeyN8WDHMc0jVOaqbloxeCuU-c07lQw2B2zwngolAxb8Kt4zHt47Hyxt-ZXeXMtb-W78ci3LtfUMiF3-sXvIa/s640/Ionic.png" width="640" /></a></div>
<br />
La ràpida evolució d'Ionic m'ha provocat alguns inconvenients quan s'han introduït canvis que trencaven la compatibilitat amb el codi anterior, però en general ha evolucionat en la bona direcció.<br />
<br />
És interessant destacar que la base del Ionic és <a href="https://angular.io/" target="_blank">Angular</a> i les tecnologies web: <a href="https://developer.mozilla.org/ca/docs/Web/JavaScript" target="_blank">javascript</a>, <a href="https://developer.mozilla.org/ca/docs/Web/Guide/HTML/HTML5" target="_blank">HTML5</a> i <a href="https://developer.mozilla.org/ca/docs/Web/CSS" target="_blank">CSS</a>. Això vol dir que es beneficia directament de l'evolució dels navegadors i de tot el suport a internet en general. I la propera versió, Ionic 4, generarà directament components web, pel que es podrà utilitzar amb qualsevol <i>framework</i> enlloc d'Angular.<br />
<br />
Bé, crec que ja he fet prou venda del producte.<br />
<br />
Properament veurem com de fàcil és fer una aplicació mòbil amb Ionic. Però mentrestant, si voleu provar Ionic, us aconsello que seguiu les instruccions d'instal·lació per a la vostra plataforma a <a href="https://ionicframework.com/getting-started" target="_blank">https://ionicframework.com/getting-started</a>.<br />
<br />
Com a consell addicional, instal·leu primer el <a href="https://github.com/creationix/nvm" target="_blank">NVM</a> seguint les instruccions de <a href="https://ionicframework.com/docs/developer-resources/using-nvm/" target="_blank">https://ionicframework.com/docs/developer-resources/using-nvm/</a>. Això us permetrà canviar fàcilment les versions del <a href="https://nodejs.org/en/" target="_blank">Node</a>, necessari per executar Ionic, i evitarà problemes de permisos (o haver d'utilitzar el <i>sudo</i> per instal·lar paquets).<br />
<br />
Si teniu dubtes, podeu utilitzar els comentaris (o anar directament al <a href="https://forum.ionicframework.com/" target="_blank">fòrum de Ionic</a>). Ànims !<br />
<br />Jordihttp://www.blogger.com/profile/10269271669040475159noreply@blogger.com0tag:blogger.com,1999:blog-2348072173208135260.post-67780089445987452192017-07-18T19:23:00.000+02:002017-07-18T19:23:05.501+02:00LEDE amb adblock: adéu als anuncis no desitjatsFa poc vaig actualitzar el meu encaminador (<i>router</i>) amb una versió de <a href="http://anomenaidesa.blogspot.com/2017/07/lede-amb-fast-path-activat-300-mb.html" target="_blank">LEDE amb Fast Path</a>, gràcies a la qual estic aprofitant al màxim la meva connexió de fibra.<br />
<br />
Però disposar de LEDE té moltes altres avantatges. Una d'elles és que s'hi pot instal·lar un bloquejador d'anuncis global, sense haver d'anar navegador per navegador, ni dispositiu per dispositiu.<br />
<br />
Suposo que no cal que us digui que els anuncis són necessaris per al manteniment de moltes pàgines i serveix gratuïts a internet. Però una cosa és l'ús i l'altra l'abús:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh3zwnnvL9Yy5sV-JILL-RnBrnODL0zdW8H4DzoZxNdWlj8lngGOPbcG8RID_j6aPVItq3X-MXTwxv9SgM5DrpgYvydWlwSJqDxYiYEQNngKoKuHi2IigYgH-jPS7hms0WRDxfEofoOLi43/s1600/adblock_OFF.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="828" data-original-width="1024" height="322" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh3zwnnvL9Yy5sV-JILL-RnBrnODL0zdW8H4DzoZxNdWlj8lngGOPbcG8RID_j6aPVItq3X-MXTwxv9SgM5DrpgYvydWlwSJqDxYiYEQNngKoKuHi2IigYgH-jPS7hms0WRDxfEofoOLi43/s400/adblock_OFF.jpg" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<span id="goog_665005003"></span><span id="goog_665005004"></span><br /></div>
Dos terços de la pàgina dedicats a la publicitat ?? Anem a posar-hi remei. Connectem a l'encaminador (LEDE/Openwrt) per SSH:<br />
<pre class="prettyprint lang-sh">$ ssh root@192.168.1.1</pre>
<br />
I instal.lem els paquets necessaris:<br />
<pre class="prettyprint lang-sh"># opkg update
# opkg install adblock luci-app-adblock libustream-mbedtls
# logread -e "adblock"
</pre>
<br />
La darrera línia serveix per comprovar que tot ha anat bé. De totes maneres, la millor opció és anar a la pàgina web de l'encaminador:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh278VGQ_JyVyyWZz7O_w7WLGBG1Bm1KpWoefinI9nw3ZgrlN4j8Grsyen6i02ve0RtHrhnYAFFS-6aLaDMNSfdY36PV2fdAaFb4CuJ3k4NHD-aNO_rcu0ItzXieAErTXJsgIer79sGiwxJ/s1600/adblock_Config.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1176" data-original-width="1087" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh278VGQ_JyVyyWZz7O_w7WLGBG1Bm1KpWoefinI9nw3ZgrlN4j8Grsyen6i02ve0RtHrhnYAFFS-6aLaDMNSfdY36PV2fdAaFb4CuJ3k4NHD-aNO_rcu0ItzXieAErTXJsgIer79sGiwxJ/s640/adblock_Config.png" width="588" /></a></div>
<br />
Aquí cal comprovar que el bloquejador estigui actiu i que tingui un cert número de dominis bloquejats. El número exacte depen de les llistes que tinguem activades (jo, de moment, tinc les que venien per defecte).<br />
<br />
El resultat? Jutgeu vosaltres mateixos amb la mateixa pàgina inicial:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjQOUQHghy45nZyAFtoE2b8BPhMwjH0eGmejFq8dAgm9UFtlv8S_fbjraNG3ibkmbAyKVb5ZTEitbc40D71Sq9Ey1KRY54z2IT6VM63kQScgI5Zmj1sG2u7scqox-KJDmJhxNis3K8nXSbV/s1600/adblock_ON.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="844" data-original-width="1024" height="328" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjQOUQHghy45nZyAFtoE2b8BPhMwjH0eGmejFq8dAgm9UFtlv8S_fbjraNG3ibkmbAyKVb5ZTEitbc40D71Sq9Ey1KRY54z2IT6VM63kQScgI5Zmj1sG2u7scqox-KJDmJhxNis3K8nXSbV/s400/adblock_ON.jpg" width="400" /></a></div>
<br />
El resultat és una <b>navegació més neta (i ràpida)</b>. <br />
<br />
I si voleu col·laborar al manteniment d'alguna pàgina en concret, sempre podeu afegir-la a la llista blanca (<i>Advanced</i> -> <i>Edit Whitelist</i>) i tornareu a veure-hi els anuncis. Però a la resta, bon vent i barca nova !<br />
<br />Jordihttp://www.blogger.com/profile/10269271669040475159noreply@blogger.com0tag:blogger.com,1999:blog-2348072173208135260.post-89136773856516289062017-07-13T20:28:00.001+02:002017-07-13T20:28:19.239+02:00LEDE amb Fast Path activat: 300 MB simètricsJa fa temps vaig parlar de com posar un <i>firmware</i> Gargoyle (Openwrt) als encaminadors (<i>routers</i>) <a href="http://anomenaidesa.blogspot.com/2013/01/gargoyle-openwrt-en-un-router-tp-link.html" target="_blank">TP-Link TL-WR1043ND</a> i <a href="http://anomenaidesa.blogspot.com/2013/07/gargoyle-en-un-tp-link-wdr3600.html" target="_blank">TP-Link WDR3600</a> per gaudir de diferents avantatges, però principalment per la facilitat de "<a href="http://anomenaidesa.blogspot.com/search/label/Openwrt" target="_blank">trastejar</a>" amb ells.<br />
<br />
<a href="https://lede-project.org/" target="_blank">LEDE</a> es defineix com "un re-inici de la comunitat de l'<a href="https://openwrt.org/" target="_blank">Openwrt</a>". Va néixer per donar un nou impuls a l'Openwrt, que porta més de dos anys sense treure noves versions. I certament ho ha fet. S'han actualitzat els controladors de les xarxes sense fils (que era un dels temes que més problemes donava) i, en general, s'han afegit molts canvis que havien quedat bloquejats sense arribar al nucli de l'Openwrt.<br />
<br />
Actualment, la versió estable de LEDE és la <a href="http://downloads.lede-project.org/releases/" target="_blank">17.01.2</a>. Us recomano provar-la.<br />
<br />
Però si voleu més emoció, podeu instal·lar una versió "en proves" que afegeix el <a href="https://forum.lede-project.org/t/qualcomm-fast-path-for-lede/4582" target="_blank"><i>Fast Path</i></a>, gràcies a l'usuari <a href="https://github.com/gwlim" target="_blank">gwlim (Github)</a>. Què és això? Sense entrar en detalls tècnics, es podria dir que optimitza els camins que segueixen els paquets en l'encaminador. L'efecte? <b>Millora la velocitat de les connexions, tant per cable com per xarxa sense fils</b>.<br />
<br />
Evidentment, he volgut veure-ho per mi mateix. He instal·lat una <a href="https://github.com/gwlim/Fast-Path-LEDE-OpenWRT/tree/master/Fast-Path/July-2017" target="_blank">versió preparada pel mateix gwlim</a> en el meu TP-Link WDR3600.<br />
<br />
Els resultats parlen per ells mateixos:<br />
<ol>
<li>Prova amb el <i>firmware</i> Gargoyle 1.5. i amb els serveis de "Qualitat de Servei" activats (quotes i priorització de tràfic). Resultat 70MB / 10MB.</li>
<li>Prova amb el <i>firmware</i> Gargoyle 1.5. sense serveis addicionals. Resultat 125MB / 111MB.</li>
<li> Prova amb <i>firmware</i> LEDE 17.01 amb <i>Fast Path</i> actiu preparada per <i>gwlim</i>. Resultat <b>304MB / 300MB</b>.</li>
</ol>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjncEX6aeQ5W88v8jCkwXxRqosh226ekqtHyclTXS91nScXrUkHdAqi34oABWbfnOOVmxzxnGWVN9HaovYIRLgcCsV5JXh5V1MYmrtMo0dPmPSlzGIuoevpnG8CAHpy6PkeGQXPxphLMgKJ/s1600/FastPathTest.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="423" data-original-width="797" height="338" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjncEX6aeQ5W88v8jCkwXxRqosh226ekqtHyclTXS91nScXrUkHdAqi34oABWbfnOOVmxzxnGWVN9HaovYIRLgcCsV5JXh5V1MYmrtMo0dPmPSlzGIuoevpnG8CAHpy6PkeGQXPxphLMgKJ/s640/FastPathTest.png" width="640" /></a></div>
<br />
Totes les proves s'han fet amb connexió per cable, en el mateix encaminador i amb pocs minuts de diferència.<br />
<br />
Les proves són consistents en diferents pàgines:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhcE-0z-DS83IgoLUX1IzdixD49rLTQcNNNgDlXROOiQqq6mIs1t0eB0-j4sEkzurB_T8PA837U40U7pdi7BvlvaFMxHLUy3n15vMsq_8dtivzw5E2Syxk-miWXFV8CEha2VbNAA8NtZqJR/s1600/FastPathTest2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="485" data-original-width="1251" height="248" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhcE-0z-DS83IgoLUX1IzdixD49rLTQcNNNgDlXROOiQqq6mIs1t0eB0-j4sEkzurB_T8PA837U40U7pdi7BvlvaFMxHLUy3n15vMsq_8dtivzw5E2Syxk-miWXFV8CEha2VbNAA8NtZqJR/s640/FastPathTest2.png" width="640" /></a></div>
<br />
En resum, una gran millora que permet treure el màxim profit del servei de fibra contractat, pràcticament triplicant la velocitat obtinguda i sense haver de mantenir l'encaminador de Movistar ni comprar un encaminador d'altes prestacions.Jordihttp://www.blogger.com/profile/10269271669040475159noreply@blogger.com0