La prima app

Memos, un blocco note minimale
In questo capitolo vedremo come realizzare un semplice Notepad, cioè un’applicazione per prendere appunti. Prima di concentrarci sul codice cerchiamo di studiare il funzionamento di questa applicazione.
La nostra idea è di avere di tre schermate. La prima è la schermata principale, la prima che l’utente vedrà, in cui presentiamo l’elenco delle note già salvate. Se si fa clic su una nota (o se ne viene aggiunta una) viene aperta la schermata con i dettagli, nella quale è possibile modificare il titolo ed il testo della nota scelta. Ecco come vorremmo che si presentassero.

Memos, schermata di modifica
Dalla parte inferiore dello schermo è possibile cancellare una nota prescelta se si fa clic sul Cestino. Questa azione aprirà una finestra di conferma.

Memos, finestra di conferma della cancellazione
Il codice sorgente di Memos è disponibile nel Repository su Github (è possibile anche scaricarlo come [file .zip][2] ). Consiglio di scaricare i file, così sarà più semplice seguire la guida. Un’altra copia del codice sorgente è disponibile nella cartella code della [repository Github di questo libro][13].
Memos utilizza [IndexedDB][3] per salvare le note e il toolkit [Gaia Building Blocks][4] per creare le interfacce. In un futuro aggiornamento il testo conterrà molte informazioni su Gaia Building Blocks, ma in questa prima versione mi limiterò ad utilizzarlo. Per ulteriori informazioni sull’argomento e per sapere quali interfacce vi sono integrate visitare i link appena segnalati.
Il primo passaggio è creare una cartella per l’applicazione di nome memos.
Creare il manifesto dell’app
Il file manifesto di Memos è molto semplice. Crea un file chiamato manifest.webapp nella cartella memos. I manifesti sono dei file in formato [JSON][5] che descrivono un’applicazione per Firefox OS di cui abbiamo già parlato nel precedente capitolo.
Qui di seguito è riportato il contenuto del file manifesto di Memos. Fate attenzione con il copia e incolla, perché è molto facile mettere una virgola nel posto sbagliato (o non metterla) e creare un file JSON non valido. Esistono molti strumenti per validare un file JSON, incluso uno specifico per validare i file manifesto delle app, lo puoi trovare online su [il marketplace][6]. Per ulteriori informazioni su questi file consultare la [pagina su MDN][7].
Memos manifest (manifest.webapp)
1 {
2 "name": "Memos",
3 "version": "1.1",
4 "description": "A simple memo taking app",
5 "launch_path": "/index.html",
6 "permissions": {
7 "storage": {
8 "description": "Required for storing and retrieving notes."
9 }
10 },
11 "developer": {
12 "name": "Andre Garzia",
13 "url": "http://andregarzia.com"
14 },
15 "icons": {
16 "60": "/style/icons/icon_60.png",
17 "128": "/style/icons/icon_128.png"
18 }
19 }
Analizziamo i campi di questo file manifesto:
| Campi | Descrizione |
|---|---|
| name | Il nome dell’applicazione |
| version | La versione attuale dell’applicazione |
| launch_path | Il file utilizzato per avviare un’applicazione |
| permissions | I permessi delle API utilizzate, con molte informazioni |
| developer | I contatti dello sviluppatore |
| icons | L’icona utilizzata in diversi formati |
La parte più interessante di questo file manifesto è la richiesta per i permessi di storage per poter utilizzare IndexedDB senza alcun limite di spazio disco1(con questi permessi possiamo salvare le note che vogliamo - anche se dobbiamo fare attenzione a non usare troppo spazio sul disco dell’utente!).
Ora che il file manifesto è pronto, passiamo al codice HTML.
Scriviamo il codice HTML
Prima di iniziare a lavorare sul codice HTML facciamo una breve panoramica su Gaia Building Blocks, una raccolta di codici CSS e JavaScript che rispettano il layout degli elementi d’interfaccia nativi di Firefox OS e che possiamo riutilizzare per creare l’interfaccia della nostra applicazione.
Come nelle pagine web, non è richiesto l’uso del look and feel di Firefox OS nella propria applicazione. Utilizzare o meno Gaia Building Blocks è una scelta personale - e le buone applicazioni dovrebbero sapersi distinguere per uno stile e un’esperienza utente proprie. La cosa importante da capire è che un’applicazione non subirà alcun tipo di pregiudizio o penalità su Firefox Marketplace se non utilizza lo stile di Gaia. Personalmente, non essendo un bravo designer, preferisco ricorrere a degli UI toolkit già pronti piuttosto che creare uno stile personale per le app.
La struttura HTML che utilizzeremo per questa applicazione seguirà gli schemi adottati da Gaia Building Blocks in cui ogni schermata è racchiusa in un tag <section> e gli elementi seguono un formato predefinito. Se non si è ancora scaricato il codice sorgente dal repository memos, è importante farlo quanto prima poiché contiene i file necessari, inclusi quelli del toolkit Gaia Building Blocks. Per coloro che dovessero avere poca confidenza con git e Github, i file sono disponibili anche come file .zip.
|
Attenzione: la versione di Gaia Building Blocks che ho utilizzato non è la più recente. Purtroppo, ho dovuto fare questa scelta perché l’applicazione Memos non era compatibile con l’ultima versione di Gaia Building Blocks. Nei propri progetti è sempre meglio utilizzare l’ultima versione disponibile del toolkit. |
Includere Gaia Building Blocks
Prima di qualsiasi altra cosa copiare le cartelle shared e styles incluse nel repository che è stato scaricato, nella cartella memos creata in precedenza. Questo consentirà l’utilizzo del toolkit Gaia Building Blocks nelle proprie applicazioni.
Iniziamo con l’inserimento del codice necessario nel file index.html.
1 <!DOCTYPE html>
2 <html>
3 <head>
4 <meta charset="utf-8">
5 <link rel="stylesheet" type="text/css" href="/style/base.css" />
6 <link rel="stylesheet" type="text/css" href="/style/ui.css" />
7 <link rel="stylesheet" type="text/css" href="/style/building_blocks.css" />
8 <link rel="stylesheet" type="text/css" href="shared/style/headers.css" />
9 <link rel="stylesheet" type="text/css" href="shared/style_unstable/lists.css\
10 " />
11 <link rel="stylesheet" type="text/css" href="shared/style_unstable/toolbars.\
12 css" />
13 <link rel="stylesheet" type="text/css" href="shared/style/input_areas.css" />
14 <link rel="stylesheet" type="text/css" href="shared/style/confirm.css" />
15 <title>Memos</title>
16 </head>
La prima riga dichiara che il documento utilizza il formato HTML5. Dalla linea 5 alla 15 vengono inclusi i file CSS dei vari componenti da usare nell’app: testata, liste, campi testuali, ecc…
Costruiamo la schermata principale
Adesso che abbiamo incluso i riferimenti a Gaia Building Blocks possiamo sfruttare il lavoro di Mozilla per creare un’applicazione a 5 stelle.
Iniziamo a costruire le varie schermate. Come abbiamo detto prima, ogni schermata è racchiusa in una <section> all’interno del corpo (il tag <body>) del documento HTML. All’elemento <body> assegnamo un attributo role il cui valore deve essere application, in questo modo quando l’app verrà lanciata verranno utilizzati i fogli di stile di Gaia Building Blocks per definire l’aspetto dell’interfaccia. Scriviamo quindi <body role="application">. Adesso creiamo la prima schermata, quindi la prima <section>, dichiariamo il tag body come abbiamo detto prima.
1 <body role="application">
2
3 <section role="region" id="memo-list">
4 <header>
5 <menu type="toolbar">
6 <a id="new-memo" href="#"><span class="icon icon-add">add</span></a>
7 </menu>
8 <h1>Memos</h1>
9 </header>
10 <article id="memoList" data-type="list"></article>
11 </section>
Nella schermata abbiamo incluso un <header> in cui mettiamo un pulsante che permette di aggiungere nuove note ed il nome dell’applicazione stessa. La schermata include un tag <article> che è utilizzato per mostrare il contenuto della nota. Useremo il pulsante e l’ID dell’articolo per catturare gli eventi nella parte JavaScript.
Sottolineo il fatto che ogni schermata è un semplice blocco di codice HTML. Costruire queste schermate utilizzando diversi linguaggi su altri sistemi richiede molto lavoro. Tutto quello che faremo è dare ad ogni contenitore un ID specifico che richiameremo successivamente.
La prima schermata è completa adesso vediamo la schermata di modifica.
Costruire la schermata di modifica
La schermata di modifica è un po’ complessa perché contiene la finestra di dialogo di eliminazione delle note.
1 <section role="region" id="memo-detail" class="skin-dark hidden">
2 <header>
3 <button id="back-to-list"><span class="icon icon-back">back</span>
4 </button>
5 <menu type="toolbar">
6 <a id="share-memo" href="#"><span class="icon icon-share">share</spa\
7 n>
8 </a>
9 </menu>
10 <form action="#">
11 <input id="memo-title" placeholder="Memo Title" required="required" \
12 type="text">
13 <button type="reset">Remove text</button>
14 </form>
15 </header>
16 <p id="memo-area">
17 <textarea placeholder="Memo content" id="memo-content"></textarea>
18 </p>
19 <div role="toolbar">
20 <ul>
21 <li>
22 <button id="delete-memo" class="icon-delete">Delete</button>
23 </li>
24 </ul>
25 </div>
26 <form id="delete-memo-dialog" role="dialog" data-type="confirm" class="hidde\
27 n">
28 <section>
29 <h1>Confirmation</h1>
30 <p>Are you sure you want to delete this memo?</p>
31 </section>
32 <menu>
33 <button id="cancel-delete-action">Cancel</button>
34 <button id="confirm-delete-action" class="danger">Delete</button>
35 </menu>
36 </form>
37 </section>
Nella parte superiore dello schermo, rappresentata dall’elemento <header> si trovano:
- un pulsante per ritornare alla schermata principale
- un campo di testo per modificare il titolo della nota
- un pulsante per condividere la nota via email
Sotto questa parte è presente il testo della nota racchiuso dal tag <textarea> , ancora più sotto c’è una seconda barra con il pulsante per cancellare la nota attuale.
Questi tre elementi e il loro contenuto rappresentano la schermata di modifica. Successivamente abbiamo un elemento <form> che verrà utilizzato per mostrare la finestra di conferma di cancellazione della nota. Questa finestra è molto semplice, contiene del testo e due pulsanti, uno per cancellare la nota ed uno per annullare l’azione.
A questo punto chiudiamo il tag <section> avendo tutte le schermate necessarie, il codice HTML rimanente serve per includere i file JavaScript e completare il documento HTML con tutti i tag di chiusura.
1 <script src="/js/model.js"></script>
2 <script src="/js/app.js"></script>
3 </body>
4 </html>
Manipoliamo il codice JavaScript
Adesso ci divertiremo a dare vita alla nostra applicazione utilizzando JavaScript. Per organizzare meglio questo codice ho scelto di suddividerlo in due file:
- model.js: contiene le routine per salvare e recuperare le note dalla memoria del dispositivo, ma non la struttura logica, la gestione dei click sugli elementi dell’interfaccia e la gestione dell’input utente, questi sono nel secondo file, in questo modo abbiamo scritto del codice modulare. In teoria potremmo riutilizzare questo file in altre applicazioni che creano e gesticono note per l’utente.
- app.js: collega gli eventi dell’interfaccia agli elementi HTML e contiene la struttura logica dell’applicazione.
Entrambi i file devono essere posizionati nella cartella js che troviamo accanto alle cartelle style e shared.
model.js
Utilizzeremo IndexedDB per salvare le note nel dispositivo. Avendo chiesto i permessi storage nel file manifesto possiamo salvare quante note vogliamo - però non dobbiamo abusarne!
Infatti i dispositivi Firefox OS solitamente non hanno molta memoria da dedicare alle app e il loro contenuto (le note nel nostro caso) ed è sempre meglio essere consapevoli di quali dati vengono memorizzati, inoltre gli utenti daranno un voto negativo a un’applicazione se consumerà troppa memoria senza motivo. Memorizzare troppo materiale porta a problemi di prestazioni e l’app risulterà lenta e poco reattiva. Al momento del caricamento su Firefox Marketplace, sarà necessario indicare nel modulo diretto ai revisori il motivo per cui l’app necessita di accesso illimitato alla memoria per il suo funzionamento, se non lo segnalate vi verrà esplicitamente richiesto dai revisori. Nel caso non siate in grado di giustificare tale richiesta, i revisori respingeranno l’app, che quindi non verrà pubblicata sul portale.
La parte di codice in model.js che mostriamo qui sotto si occupa del collegamento e della creazione dello storage.
1 var dbName = "memos";
2 var dbVersion = 1;
3
4 var db;
5 var request = indexedDB.open(dbName, dbVersion);
6
7 request.onerror = function (event) {
8 console.error("Can't open indexedDB!!!", event);
9 };
10 request.onsuccess = function (event) {
11 console.log("Database opened ok");
12 db = event.target.result;
13 };
14
15 request.onupgradeneeded = function (event) {
16
17 console.log("Running onUpgradeNeeded");
18
19 db = event.target.result;
20
21 if (!db.objectStoreNames.contains("memos")) {
22
23 console.log("Creating objectStore for memos");
24
25 var objectStore = db.createObjectStore("memos", {
26 keyPath: "id",
27 autoIncrement: true
28 });
29 objectStore.createIndex("title", "title", {
30 unique: false
31 });
32
33 console.log("Adding sample memo");
34 var sampleMemo1 = new Memo();
35 sampleMemo1.title = "Welcome Memo";
36 sampleMemo1.content = "This is a note taking app. Use the plus sign in t\
37 he topleft corner of the main screen to add a new memo. Click a memo to edit it.\
38 All your changes are automatically saved.";
39
40 objectStore.add(sampleMemo1);
41 }
42 }
Il codice appena visto crea un oggetto db ed un oggetto request. L’oggetto db è utilizzato in altre funzioni per manipolare le note memorizzate.
Nell’implementazione della funzione request.onupgradeneeded creiamo una nota di benvenuto. Questa funzione è eseguita quando l’applicazione viene lanciata per la prima volta (o quando la versione del database cambia). In questo modo, al primo avvio dell’applicazione, il database conterrà una nota di esempio.
Una volta aperta la connessione al database e inizializzato il meccanismo di archiviazione, è ora di creare le funzioni basilari per manipolare le note.
1 function Memo() {
2 this.title = "Untitled Memo";
3 this.content = "";
4 this.created = Date.now();
5 this.modified = Date.now();
6 }
7
8 function listAllMemoTitles(inCallback) {
9 var objectStore = db.transaction("memos").objectStore("memos");
10 console.log("Listing memos...");
11
12 objectStore.openCursor().onsuccess = function (event) {
13 var cursor = event.target.result;
14 if (cursor) {
15 console.log("Found memo #" + cursor.value.id + " - " + cursor.value.\
16 title);
17 inCallback(null, cursor.value);
18 cursor.continue();
19 }
20 };
21 }
22
23 function saveMemo(inMemo, inCallback) {
24 var transaction = db.transaction(["memos"], "readwrite");
25 console.log("Saving memo");
26
27 transaction.oncomplete = function (event) {
28 console.log("All done");
29 };
30
31 transaction.onerror = function (event) {
32 console.error("Error saving memo:", event);
33 inCallback({
34 error: event
35 }, null);
36
37 };
38
39 var objectStore = transaction.objectStore("memos");
40
41 inMemo.modified = Date.now();
42
43 var request = objectStore.put(inMemo);
44 request.onsuccess = function (event) {
45 console.log("Memo saved with id: " + request.result);
46 inCallback(null, request.result);
47
48 };
49 }
50
51 function deleteMemo(inId, inCallback) {
52 console.log("Deleting memo...");
53 var request = db.transaction(["memos"], "readwrite").objectStore("memos").de\
54 lete(inId);
55
56 request.onsuccess = function (event) {
57 console.log("Memo deleted!");
58 inCallback();
59 };
60 }
In questo blocco di codice abbiamo creato un costruttore che produce nuove note con alcuni campi già inizializzati. Dopodiché abbiamo implementato le altre funzioni per la presentazione, il salvataggio e la cancellazione delle note. Molte di queste funzioni richiedono che sia passato un parametro chiamato inCallback. Questo parametro è esso stesso una funzione che verrà invocata al termine della funzione chiamante. Questo è necessario per la natura asincrona di IndexedDB. Tutte le callback hanno la medesima struttura di chiamata callback(error, value), con due parametri in ingresso, in cui uno dei due assumerà il valore null a seconda del risultato della funzione chiamante.
Ora che l’archiviazione delle note e le funzioni di modifica sono state implementate, lavoriamo alla struttura logica dell’applicazione nel file app.js.
app.js
Questo file contiene la logica dell’applicazione. Il codice sorgente è troppo lungo da mostrare in una volta sola, quindi verrà diviso in parti per studiarlo meglio.
1 var listView, detailView, currentMemo, deleteMemoDialog;
2
3 function showMemoDetail(inMemo) {
4 currentMemo = inMemo;
5 displayMemo();
6 listView.classList.add("hidden");
7 detailView.classList.remove("hidden");
8 }
9
10
11 function displayMemo() {
12 document.getElementById("memo-title").value = currentMemo.title;
13 document.getElementById("memo-content").value = currentMemo.content;
14 }
15
16 function shareMemo() {
17 var shareActivity = new MozActivity({
18 name: "new",
19 data: {
20 type: "mail",
21 body: currentMemo.content,
22 url: "mailto:?body=" + encodeURIComponent(currentMemo.content) + "&s\
23 ubject=" + encodeURIComponent(currentMemo.title)
24
25 }
26 });
27 shareActivity.onerror = function (e) {
28 console.log("can't share memo", e);
29 };
30 }
31
32 function textChanged(e) {
33 currentMemo.title = document.getElementById("memo-title").value;
34 currentMemo.content = document.getElementById("memo-content").value;
35 saveMemo(currentMemo, function (err, succ) {
36 console.log("save memo callback ", err, succ);
37 if (!err) {
38 currentMemo.id = succ;
39 }
40 });
41 }
42
43 function newMemo() {
44 var theMemo = new Memo();
45 showMemoDetail(theMemo);
46 }
All’inizio vengono dichiarate alcune variabili globali (bleah :-P!) per mantenere dei riferimenti a elementi nel DOM che saranno utilizzati in alcune funzioni. La variabile globale di maggiore interesse è currentMemo, l’oggetto di riferimento per la nota che l’utente sta visualizzando.
Le funzioni showMemoDetail() e displayMemo() lavorano in coppia. La prima carica la nota selezionata in currentMemo e modifica il CSS degli elementi mostrati nella schermata di modifica. La seconda prende il contenuto della variabile currentMemo e mostra la nota a schermo. Potremmo mettere il codice nella stessa funzione ma averlo separato permette di divertirci di più con nuove implementazioni.
La funzione shareMemo() utilizza una WebActivity per aprire il programma predefinito per la posta elettronica con il contenuto della nota selezionata.
La funzione textChanged() prende il contenuto dei campi e lo inserisce nell’oggetto currentMemo che salva la nota. Questo perché avremo un’applicazione con auto-salvataggio. Tutte le modifiche al contenuto o al titolo invocheranno la funzione che salverà in IndexedDB.
La funzione newMemo() crea una nuova nota e apre la schermata di modifica con la nuova nota creata.
1 function requestDeleteConfirmation() {
2 deleteMemoDialog.classList.remove("hidden");
3 }
4
5 function closeDeleteMemoDialog() {
6 deleteMemoDialog.classList.add("hidden");
7 }
8
9 function deleteCurrentMemo() {
10 closeDeleteMemoDialog();
11 deleteMemo(currentMemo.id, function (err, succ) {
12 console.log("callback from delete", err, succ);
13 if (!err) {
14 showMemoList();
15 }
16 });
17 }
18
19 function showMemoList() {
20 currentMemo = null;
21 refreshMemoList();
22 listView.classList.remove("hidden");
23 detailView.classList.add("hidden");
24 }
La funzione requestDeleteConfirmation() mostra la richiesta di conferma di cancellazione della nota.
Le funzioni closeDeleteMemoDialog() e deleteCurrentMemo() sono invocate dai pulsanti nella finestra di conferma.
La funzione showMemoList() effettua una pulizia e mostra l’elenco delle note presenti. Per esempio, svuota il contenuto di currentMemo se non stiamo leggendo una nota.
1 function refreshMemoList() {
2 if (!db) {
3 // HACK:
4 // this condition may happen upon first time use when the
5 // indexDB storage is under creation and refreshMemoList()
6 // is called. Simply waiting for a bit longer before trying again
7 // will make it work.
8 console.warn("Database is not ready yet");
9 setTimeout(refreshMemoList, 1000);
10 return;
11 }
12 console.log("Refreshing memo list");
13
14 var memoListContainer = document.getElementById("memoList");
15
16
17 while (memoListContainer.hasChildNodes()) {
18 memoListContainer.removeChild(memoListContainer.lastChild);
19 }
20
21 var memoList = document.createElement("ul");
22 memoListContainer.appendChild(memoList);
23
24 listAllMemoTitles(function (err, value) {
25 var memoItem = document.createElement("li");
26 var memoP = document.createElement("p");
27 var memoTitle = document.createTextNode(value.title);
28
29 memoItem.addEventListener("click", function (e) {
30 console.log("clicked memo #" + value.id);
31 showMemoDetail(value);
32
33 });
34
35 memoP.appendChild(memoTitle);
36 memoItem.appendChild(memoP);
37 memoList.appendChild(memoItem);
38
39
40 });
41 }
La funzione refreshMemoList() modifica il DOM della nostra applicazione aggiornando l’elenco delle note. Sarebbe più facile usare alcuni sistemi di templating come handlebars o underscore ma quest’applicazione contiene solo vanilla javascript quindi faremo tutto a mano.
Queste sono le funzioni utilizzate dall’applicazione. Le uniche che mancano sono il gestore eventi e la chiamata iniziale di refreshMemoList().
1 window.onload = function () {
2 // elements that we're going to reuse in the code
3 listView = document.getElementById("memo-list");
4 detailView = document.getElementById("memo-detail");
5 deleteMemoDialog = document.getElementById("delete-memo-dialog");
6
7 // All the listeners for the interface buttons and for the input changes
8 document.getElementById("back-to-list").addEventListener("click", showMemoLi\
9 st);
10 document.getElementById("new-memo").addEventListener("click", newMemo);
11 document.getElementById("share-memo").addEventListener("click", shareMemo);
12 document.getElementById("delete-memo").addEventListener("click", requestDele\
13 teConfirmation);
14 document.getElementById("confirm-delete-action").addEventListener("click", d\
15 eleteCurrentMemo);
16 document.getElementById("cancel-delete-action").addEventListener("click", cl\
17 oseDeleteMemoDialog);
18 document.getElementById("memo-content").addEventListener("input", textChange\
19 d);
20 document.getElementById("memo-title").addEventListener("input", textChanged);
21
22 // the entry point for the app is the following command
23 refreshMemoList();
24
25 };
Ora che tutti i file sono pronti proviamo l’applicazione nel simulatore.
Provare l’applicazione col simulatore
Prima di avviare l’applicazione nel simulatore è preferibile verificare che tutti i file siano al posto giusto altrimenti l’applicazione non funzionerà. Ecco quale dovrebbe essere il contenuto della cartella memos:

Lista dei file utilizzati da Memos
Se si ha il vago sospetto di aver commesso qualche errore è possibile verificarlo confrontando il repository memos su github (un’ulteriore copia del codice sorgente è disponibile nella cartella code nel repo del testo).
Tutto a posto? Bene, cominciamo.
Per aprire la Dashboard del Simulatore fare clic nel menu Sviluppo -> WebIde.

Apertura della Dashboard del simulatore
Fai clic sul pulsante Apri app, seleziona Apri app pacchettizzata e seleziona la cartella contenente il file manifesto dell’applicazione Memos for Firefox Os.

Aggiunta di una nuova applicazione
Se tutto funziona come previsto Memos verrà aggiunta ai progetti del WebIde.

Memos mostrata nella Dashboard
Una volta aggiunta l’applicazione scegliamo la versione del simulatore che vogliamo usare e avviamola. Una volta avviato possiamo installare Memos premendo il pulsante triangolare e sul simulatore verrà avviata la nostra app.

Il pulsante triangolare da premere
A questo punto è possibile provare tutte le funzionalità di Memos.

Memos installato nel Simulatore
Congratulazioni! Abbiamo creato e provato la nostra prima applicazione. Non è un’applicazione complessa o rivoluzionaria - ma spero sia utile per capire il workflow di sviluppo di Firefox OS. Chi ha già visto e lavorato con il web ha di sicuro potuto notare che non è molto diverso dallo sviluppo web classico.
Sottolineiamo che ogni volta che viene modificato il codice sorgente è necessario premere il pulsante Aggiorna per aggiornare il contenuto dell’applicazione presente nel simulatore.
Riassunto
In questo capitolo abbiamo creato la nostra prima applicazione per Firefox OS e l’abbiamo lanciata nel simulatore. Nel prossimo capitolo vedremo uno strumento molto utile chiamato Boilerplate, un insieme di esempi che rispondono ad esigenze basilari di un’applicazione, come selezionare un contatto dalla rubrica, far vibrare il telefono o controllare la carica della batteria, tutto questo è documentato con codice già pronto e adatto ad ogni evenienza.
Firefox OS Boilerplate App
Il boilerplate di cui abbiamo letto in precedenza è un’applicazione dimostrativa con molti esempi basilari. Contiene un esempio per la maggior parte delle Web Activity, la questione multilingua, installazione dell’applicazione, API HTML5 e l’interfaccia grafica di Gaia realizzata con Gaia Building Blocks.
La parte interessante è che si trova su Github, quindi c’è anche una demo sempre aggiornata che puoi trovare online.
Puoi provare il boilerplate nel tuo browser preferito ma se usi Chrome vedrai che nella console apparirà Open Web Apps not supported che vuol dire che non supporta questo standard proposto da Mozilla.
Lo standard “Open Web App” consiste in un accesso hardware, installazione locale, storage offline, marketplace, API per i pagamenti e le ricevute, Persona per i login e il famoso manifest. Questo insieme di caratteristiche ed il manifesto di Mozilla formano le Open Web Apps!
Noi sviluppatori web speriamo che le Open Web Apps vengano supportate da altri browser e sistemi operativi in modo da semplificare il lavoro per noi e per una migliore esperienza utente.
Dopo questo messaggio pubblicitario riprendiamo il discorso!
I prodotti di Mozilla che utilizzano Gecko supportano le Open Web Apps e in Firefox OS vediamo l’apice delle loro potenzialità. Firefox sia per Android che desktop le supporta quindi collegandosi al marketplace si possono installare le applicazioni pensate per queste interfacce (se specificate nel marketplace).
Il boilerplate è un esempio per le Open Web Apps (template) più il supporto proprietario (al momento!) per Firefox OS.
Il boilerplate è diviso in tre sezioni ma io ne aggiungo una quarta per gli altri dettagli.
I dettagli che fanno la differenza
Incominciamo da questi dettagli:
- AppCache
- Offline
- Installare l’applicazione
- Multilingua
- Grafica di Gaia
Alcune di queste cose le abbiamo viste o le vedremo in questa fantastica guida.
Offline
Con Offline mi riferisco a del codice che permette di sapere se il dispositivo è connesso sfruttando l’oggetto window.navigator.connection che è descritto sulla pagina MDN dedicata con i dettagli tecnici e il supporto crossbrowser.
Il codice è presente in due versioni:
- Uno dei tanti pulsanti che usa l’API diretta fornisce due informazioni: la banda disponibile in MB (0 se è offline, infinity se sconosciuta o solitamente la linea fissa) e se la connessione è a consumo.
- Con AppCache per sapere se si utilizza l’applicazione in modalità offline, nel boilerplate è utilizzato per mostrare il pallino verde se si è online.
Vediamo quindi questi due codici:
1 var connection = window.navigator.mozConnection,
2 online = "<strong>Connected:</strong> " + (connection.bandwidth),
3 metered = "<strong>Metered:</strong> " + connection.metered;
1 var appCache = window.applicationCache;
2 appCache.onerror = function() {
3 displayStatus.className = "offline";
4 displayStatus.title = "Offline";
5 };
Installare l’applicazione
Installazione dell’applicazione vuol dire utilizzare le Open Web Apps. Vediamo come funziona il codice di installazione e di verifica installazione. Prima di tutto verifichiamo se il sistema le supporta (con navigator.mozApps) dopo di che inseriamo delle callback di successo o fallimento dell’installazione.
1 if (navigator.mozApps) {
2 var checkIfInstalled = navigator.mozApps.getSelf();
3 checkIfInstalled.onsuccess = function () {
4 if (checkIfInstalled.result) {
5 // Already installed
6 var installationInstructions = document.querySelector("#installation\
7 -instructions");
8 if (installationInstructions) {
9 installationInstructions.style.display = "none";
10 }
11 }
12 else {
13 var install = document.querySelector("#install"),
14 manifestURL = location.href.substring(0, location.href.lastIndex\
15 Of("/")) + "/manifest.webapp";
16 install.className = "show-install";
17 install.onclick = function () {
18 var installApp = navigator.mozApps.install(manifestURL);
19 installApp.onsuccess = function() {
20 install.style.display = "none";
21 };
22 installApp.onerror = function() {
23 alert("Install failed\\n\\n:" + installApp.error.name);
24 };
25 };
26 }
27 };
28 } else {
29 console.log("Open Web Apps not supported");
30 }
Come si può vedere il codice è molto semplice, si dà l’indirizzo del file manifest e si verifica se è stato installato. Per installare il boilerplate fate click sul simbolo ‘+’ in alto a destra.
Questo codice eseguito su Firefox OS, Firefox for Android e Firefox desktop aprirà una finestra modale nativa per chiedere se installare l’applicazione.
WebActivity
Finalmente parliamo di una delle grandi novità per gli sviluppatori web! Finalmente potremo accedere al sistema in modo più profondo! Ho già detto finalmente?
Scherzi a parte, in questa sezione possiamo accedere ad alcune delle azioni del sistema come fare una telefonata o scattare una foto. Queste WebActivity purtroppo sono solo per Firefox OS e quindi il testing è solo con simulatore ma c’è chi sta lavorando ad una polyfill per supportare le web activity anche da browser (dove possibile).
Non vedremo il codice nel dettaglio delle varie WebActivity, quindi rimando al file webapp.js, ma per completezza ecco alcune rese disponibili da Firefox OS:
- Cercare file
- Scattare una foto
- Registrare un video
- Avviare una telefonata
- Mandare un SMS
- Aggiungere un contatto
- Modificare un contatto
- Condividere un sito
- Condividere una foto sui social network
- Condividere una foto via mail
- Aprire un sito
- Scrivere un’email (con l’applicazione di sistema)
- Salvare un segnalibro
- Aprire un video
- Modificare le impostazioni del telefono
Praticamente vi stiamo dicendo di non pensare a come implementare tutte queste funzionalità dalla vostra app. Se volete ve le diamo noi già pronte.
Naturalmente l’importanza di questa idea non è limitata alle precedenti opzioni e basta, potete registrare la vostra WebActivity personalizzata nella vostra app per fare più o meno quello che vi pare, dal selezionare foto di gattini a prendere i contatti da una rubrica segreta o rendere disponibile un lettore di codici a barre.
La lista completa delle WebActivity disponibili su Firefox OS la trovi online.
WebAPI
Questa sezione del boilerplate contiene sia esempi esclusivi per Firefox OS ma anche alcuni esempi di tecnologie HTML5 di recente standardizzazione. Tra questi abbiamo le notifiche di sistema, il blocco della rotazione o dello spegnimento dello schermo che sono solo per Firefox OS mentre vibrazione, verifica della connessione (ne abbiamo parlato prima), geolocalizzazione, quantità di luce ambientale, prossimità dell’utente allo schermo, accesso alla batteria sono invece API standard e quindi disponibili a chiunque abbia un browser moderno.
API Privilegiate
Queste API sono particolari e sono disponibili solo per le app privilegiate e sono quasi tutte standard HTML5.
Tra gli esempi disponibili abbiamo: verificare se l’applicazione è in primo piano, accedere alle immagini sul dispositivo, effettuare una richiesta ad un server e prendere i contatti dal telefono. Questi esempi sono uno standard HTML5 mentre selezionare i contatti e fare richieste XHR CrossDomain sono una peculiarità di Firefox OS.
Multilingua
Nel Boilerplate appena visto è utilizzata una libreria JavaScript chiamata webL10n. Questa libreria è presente in Gaia ma si tratta di una versione modificata (questa è la versione presente nel boilerplate).
Grazie a webL10n il sito riconosce in automatico la lingua utilizzata dal dispositivo e carica le traduzioni appropriate.
Al caricamento del file JavaScript della libreria viene caricato il file locales.ini che contiene i riferimenti alle varie lingue disponibili dell’applicazione. Dopodiché se c’è la lingua utilizzata dal sistema provvede a sostituire i testi che gli abbiamo indicato alla creazione dell’app, se la lingua non è presente viene lasciata la versione base (dobbiamo indicarla noi).
Diamo un’occhiata al codice prima di vedere come avviene la magia della localizzazione.
1 <link rel="resource" type="application/l10n" href="locales/locales.ini" />
2 <script type="application/javascript" src="js/l10n.js"></script>
Con questo codice il boilerplate carica i file della lingua… ma come sa dove agire per cambiare il testo?
La libreria si basa sugli attributi data-l10n-id che mettiamo agli elementi di cui vogliamo la traduzione che deve contenere il nome di riferimento della stringa da mostrare.
Ecco un esempio per chiarire le idee.
1 <html>
2 <head>
3 <script type="text/javascript" src="l10n.js"></script>
4 <link rel="prefetch" type="application/l10n" href="locales.ini" />
5 </head>
6 <body>
7 <button data-l10n-id="test" title="click me!">This is a test</button>
8 </body>
9 </html>
Il bottone ha una proprietà data-l10n-id con attributo test, questo vuol dire che se nel file locales.ini esiste una riga di testo simile a questa
1 [it]
2 test = Questo è un test
3 test.title = Cliccami!
Il testo all’interno del bottone verrà sostituito dal testo Questo è un test nel caso io chieda la lingua italiana. Esistono anche librerie più avanzate realizzate sempre da Mozilla come l20n che ha più funzionalità rispetto a quella contenuta nel boilerplate.
File della lingua
- locales.ini: un esempio che contiene i percorsi dei vari file di lingua con il loro codice di riconoscimento.
- manifest.properties: un esempio che contiene la traduzione del manifest.
- app.properties: un esempio che contiene la traduzione completa del boilerplate.
Come si può vedere è il classico file .ini proprietà = testo che viene elaborato da JavaScript e caricato all’interno del tag contenente nell’attributo data-l10n-id.
Riassunto
Abbiamo visto molte funzionalità di Firefox OS e delle Open Web Apps tramite un semplice ma completo boilerplate. Ricordati che per poter utilizzare le WebActivity o alcune API hai bisogno dei permessi degli utenti e quindi devi chiedere i permessi relativi compilando il file manifest accuratamente. Abbiamo visto anche la questione multilingua che è molto importante e di come sia semplice integrare questa soluzione nei propri progetti.
- Per saperne di più su questa autorizzazione leggi la pagina MDN sui permessi.↩