Università degli Studi di Napoli Federico II Facoltà di Scienze MM.FF.NN. Corso di Laurea in Informatica Tesi sperimentale di Laurea Triennale Realizzazione di un sistema di rubrica telefonica in ambiente Android Relatori Candidato Prof. Guido Russo Dr. Filippo Cestari Anastasia Kokonozi matr. 566/201 Anno Accademico 2011-2012 Indicegenerale Prefazione....................................................................................................................4 CAPITOLO I...............................................................................................................6 Dispositivi mobile e software.....................................................................................6 1.1 Smartphone....................................................................................................6 1.2 Tablet.............................................................................................................7 1.3 Diffusione dei dispositivi e sistemi operativi................................................7 CAPITOLO II...........................................................................................................10 Stato dell'arte.............................................................................................................10 2.1 Premessa......................................................................................................10 2.2 Applicazioni delle università estere.............................................................11 2.3 Applicazioni delle università italiane...........................................................17 2.4 Analisi comparativa.....................................................................................22 CAPITOLO III..........................................................................................................25 Modello di programmazione.....................................................................................25 3.1 App native vs Web app................................................................................25 3.2 Sviluppo nativo............................................................................................28 3.3 PhoneGap: il framework..............................................................................31 3.4 Compatibilità...............................................................................................33 CAPITOLO IV..........................................................................................................34 L'implementazione....................................................................................................34 4.1 Ambiente di sviluppo: Server......................................................................34 4.1.1 Installazione e configurazione di LDAP..............................................34 4.1.2 Importazione dei dati su LDAP............................................................37 Anastasia Kokonozi 566/201 Pagina 2 di 117 4.1.3 WebService/API...................................................................................38 4.2 Ambiente di sviluppo: Android....................................................................44 4.2.1 Installazione e configurazione di Android SDK...................................45 4.2.2 Installazione di Eclipse ed ADT Plugin................................................47 4.2.3 Creazione di un nuovo progetto con PhoneGap...................................48 4.3 Struttura del progetto...................................................................................49 4.3.1 AndroidManifest...................................................................................50 4.3.2 HTML5.................................................................................................53 4.3.3 CSS – Responsive design.....................................................................55 4.3.4 JavaScript – jQuery Mobile..................................................................59 4.3.5 JavaScript – PhoneGap.........................................................................65 CAPITOLO V...........................................................................................................69 La sperimentazione...................................................................................................69 Conclusioni...............................................................................................................76 APPENDICE.............................................................................................................77 A. Installazione dell'applicazione......................................................................77 B. Porting dell'applicazione...............................................................................79 C. Testare l'applicazione con l'emulatore...........................................................81 D. Pubblicazione dell'applicazione....................................................................94 E. Codice dell'applicazione................................................................................97 Bibliografia..............................................................................................................117 Anastasia Kokonozi 566/201 Pagina 3 di 117 Prefazione Nel corso degli ultimi anni la grande diffusione di smartphone e tablet ha portato una parte dell’informatica a focalizzarsi sullo sviluppo di applicazioni per questi dispositivi. I programmatori quindi si sono dovuti scontrare su diverse problematiche come: l’enorme differenza di prestazioni fra questi dispositivi ed i personal computer. Diversi sistemi operativi mobili che permettono di sviluppare nuove app solamente mediante delle proprie SDK (Software Development Kit), frammentazione dei dispositivi anche nel caso in cui sfruttano un sistema operativo identico (risoluzione del display differente, presenza o meno di un monitor touchscreen, etc). Tutte queste problematiche hanno portato le software house, che vogliono intraprendere lo sviluppo di applicativi per smartphone e tablet, a fare diverse scelte ovvero: sviluppare per una sola tipologia di piattaforma, oppure creare diversi team di sviluppo ognuno dei quali specializzato sulla programmazione su un tipo di sistema operativo mobile, oppure usare delle tecnologie innovative che permettono di sviluppare un app per più piattaforme. All’interno di questo elaborato verrà mostrato lo sviluppo per dispositivi Android di un’applicazione chiamata Rubrica Unina usando il framework PhoneGap. La scelta è ricaduta su questo framework perché è uno dei primi che permette di creare applicazioni native per i vari dispositivi che supporta, partendo da un codice comune scritto in JavaScript. Il presente lavoro di tesi consiste nel progettare quest'applicazione con funzionalità di rubrica telefonica per la ricerca dei recapiti dei docenti dell'Università degli Studi di Napoli Federico II. Più precisamente lo scopo della tesi è quello di realizzare un prototipo che possa essere utilizzato dagli studenti o da chiunque altro necessiti di mettersi in Anastasia Kokonozi 566/201 Pagina 4 di 117 contatto con un docente, fornendogli un ulteriore mezzo per effettuare delle ricerche in modo immediato dei recapiti del docente, quali numero di telefono, indirizzo email, struttura di afferenza, etc. L'applicazione si baserà sul funzionamento del sistema web FastGuttel, che permette la ricerca di un contatto per nome, cognome, struttura e numero telefonico. Dispone inoltre di un'area riservata, che non sarà presa in considerazione per quest'applicazione, dove i docenti possono visualizzare i dati relativi al loro interno telefonico. Viene ora esposta la struttura secondo la quale è organizzato il seguente elaborato di laurea. Nel Capitolo 1 si presenta una breve introduzione al mondo delle applicazioni mobile, dei dispositivi mobile e della loro attuale diffusione. Nel Capitolo 2 vengono passate in rassegna alcune applicazioni utilizzate dalle università italiane ed estere, che integrano funzionalità di rubrica telefonica, al fine di effettuare un'analisi comparativa per evidenziare le funzionalità più utilizzate in quest'ambito. Nel Capitolo 3 viene analizzato il modello di programmazione e le differenze tra lo sviluppo nativo di un'applicazione e lo sviluppo di una web app, con particolare attenzione al framework PhoneGap. Nel Capitolo 4 è descritto il processo di preparazione dell'ambiente di sviluppo, sia della parte server che della parte relativa allo sviluppo per Android. E' inoltre descritta la base dati con cui l'integrazione interagisce e sono illustrati dettagliatamente tutti gli strumenti utilizzati, con alcuni frammenti di codice. Il lavoro si conclude illustrando il funzionamento dell'applicazione realizzata , nel Capitolo 5. In Appendice vengono illustrate le procedure per l'installazione dell'applicazione su un dispositivo Android, il porting dell'applicazione su altre piattaforme ed il testing dell'applicazione tramite l'emulatore messo a disposizione dall'SDK di Android. Anastasia Kokonozi 566/201 Pagina 5 di 117 CAPITOLO I Dispositivi mobile e software Un dispositivo mobile (anche conosciuto come dispositivo portatile, palmare) è semplicemente un dispositivo di piccole dimensioni che implementa funzionalità uguali o simili a quelle dei personal computer. A causa delle loro dimensioni ridotte la comunicazione tra utente e dispositivo avviene tramite monitor touchscreen e/o mini tastiere; inoltre possiedono caratteristiche hardware e prestazionali inferiori rispetto i personal computer, presupposto significativo da considerare durante la fase di sviluppo di un software. 1.1 Smartphone Uno smartphone è un dispositivo portatile che abbina le funzionalità di un telefono cellulare a quelle di gestione dei dati personali, può derivare dall'evoluzione di un palmare a cui si aggiungono funzioni di telefono o viceversa, di un telefono mobile a cui si aggiungono funzioni di palmare. La caratteristica più interessante degli smartphone è la possibilità di installarvi ulteriori applicazioni (app), che incrementano le funzionalità del dispositivo. Queste app possono essere sviluppate dal produttore del telefono, dallo stesso utilizzatore, o da terze parti. Attualmente gli smartphone vengono venduti con diversi accessori hardware come fotocamera, GPS, componenti che permettono di connettere il dispositivo con il web piuttosto che con tecnologie come il bluetooth e il WiFi che permettono la comunicazione con altri apparecchi. Inoltre vengono anche preinstallati programmi Anastasia Kokonozi 566/201 Pagina 6 di 117 per l’accesso a internet, la gestione delle email, la pianificazione delle attività e per la compatibilità con i più comuni formati di file come PDF e quelli della suite Microsoft Office. 1.2 Tablet Un tablet è un computer di dimensioni compatte che come principale sistema di input utilizza uno schermo touchscreen controllato da una penna o tramite le dita. Il nome deriva dalla forma del dispositivo che ricorda una tavoletta utilizzata per la scrittura. Questi dispositivi non sono dotati di tastiera sebbene questa spesso possa essere simulata sullo schermo. Nel 2010 Apple ha presentato l'iPad un tablet computer che ha rilanciato il mercato di questi dispositivi difatti le vendite nel 2010 oscillano intorno ai 19,5 milioni. I vantaggi di questi tipi di dispositivi sono altamente soggettivi, tuttavia un indubbio vantaggio risiede nel fatto che gli utenti trovano più intuitivo premere sull'immagine che rappresenta un oggetto piuttosto che guidare un dispositivo di puntamento come un mouse per selezionare un oggetto. 1.3 Diffusione dei dispositivi e sistemi operativi Audiweb è un organismo che rileva i dati audience di internet in Italia, offrendo al mercato dati obiettivi, di carattere quantitativo e qualitativo, sulla fruizione del mezzo. Di recente Audiweb ha pubblicato i risultati della ricerca sulla diffusione di internet e della connessione alla rete relative al mese di dicembre 2012. I dati impressionano e ci fanno realizzare che l'informatizzazione di massa, anche nel nostro paese, è pressoché compiuta, infatti sono ben 38,4 milioni gli Italiani che accedono a internet (da casa, ufficio, da luogo di studio o altri luoghi), pari al 79,6% Anastasia Kokonozi 566/201 Pagina 7 di 117 della popolazione tra gli 11 e i 74 anni di età. Più in dettaglio, emerge una maggiore disponibilità di accesso da casa attraverso computer (35,8 milioni di individui tra gli 11 e i 74 anni), mentre l’accesso da luogo di lavoro è indicato dal 48,2% degli occupati (10,9 milioni di individui) e l’accesso da luogo di studio è indicato dal 7,7% dei casi (3,7 milioni). Il 34,8% degli individui ha dichiarato di avere un cellulare con accesso a internet (16,8 milioni) e il 5,6% (2,7 milioni) da tablet. E’ interessante soprattutto quest’ultimo dato decisamente in crescita, la penetrazione degli smartphone oggi raggiunge il 50% di chi possiede un telefono cellulare, la più alta in Europa, e cresce rapidamente anche la diffusione dei tablet: oggi i dati ci confermano come entrambe queste tipologie di dispositivo mobile abbiano un ruolo fondamentale nelle scelte di acquisto di tutti i giorni. Dati confermati anche dall'analisi di Nielsen sull'utilizzo del mobile in Italia nell'ultimo trimestre del 2012. Nielsen è leader mondiale nelle informazioni di marketing e nella rilevazione di dati sui consumi e sull’utilizzo dei media. Anastasia Kokonozi 566/201 Pagina 8 di 117 Confermata ancora una volta la crescita degli smartphone, un trend che continua ormai da più di due anni e che coinvolge oltre la metà dei possessori di cellulari, ovvero il 56% degli utenti italiani di telefonia mobile. La vera novità però non è la crescita dei possessori di smartphone, bensì l’andamento dei principali attori all’interno del segmento. Symbian, leader indiscusso con oltre il 70% di quota nel 2010, ha registrato trimestre dopo trimeste un graduale calo, imputabile anche al passaggio di Nokia a Windows, arrivando nel terzo trimestre del 2012 a dover cedere la posizione di leadership ad Android, che, con una crescita senza precedenti, arriva al 36% di quota di mercato, superando ogni altro sistema operativo. La diffusione dei dispositivi mobile e delle connessioni alla rete da questi dispositivi, piuttosto che dai classici pc desktop, giustifica l'interesse nella realizzazione dell'applicazione oggetto di questo lavoro di tesi, come supporto innovativo e avanzato al servizio web FastGuttel. Anastasia Kokonozi 566/201 Pagina 9 di 117 CAPITOLO II Stato dell'arte 2.1 Premessa Nonostante il crescente interesse per lo sviluppo di applicazioni per dispositivi mobile quali smartphone e tablet, la produzione di lavori inerenti a ciò nell'ambito dell'istruzione è piuttosto scarna, almeno in Italia. Analizzando lo stato dell’arte nella letteratura scientifica è evidente che la maggior parte delle università americane, invece, possiedono una propria applicazione. Queste possono essere distinte in tre principali categorie: 1. Applicazioni per iOS: disponibili per dispositivi Apple con sistema operativo iOS, quali iPhone, iPad e iPod, scaricabili dall'Apple Store 2. Applicazioni per Android: disponibili per dispositivi con sistema operativo Android, quali smartphone e tablet di varie marche 3. Applicazioni web-based: fruibili attraverso l'uso di un browser mobile, indipendentemente dal sistema operativo del dispositivo utilizzato Tra le molteplici università che dispongono di una propria applicazione mobile, sono state selezionate quelle disponibili per Android con funzionalità di rubrica telefonica, per la ricerca dei numeri di telefono (e non solo) di docenti e personale universitario. L’analisi di queste applicazioni è servita come riferimento di partenza. Anastasia Kokonozi 566/201 Pagina 10 di 117 2.2 Applicazioni delle università estere Sono state prese in considerazione le applicazioni delle seguenti università: 1. * University of Alabama => Nome App: Alabama 2. Auburn University => Nome App: Auburn University 3. Auburn University => Nome App: Auburn University 4. * Bucknell University => Nome App: Bucknell 5. Cardiff University => Nome App: Cardiff University 6. * Duke University => Nome App: DukeMobile 7. * Emory University => Nome App: Emory Mobile 8. * Griffith University => Nome App: Griffith 9. * University of San Diego => Nome App: MySDMobile 10. * Northewstern University => Nome App: Northwestern 11. Ohio State University => Nome App: OSU Mobile 12. * Texas A&M University => Nome App: TAMUmobile 13. University of Manitoba => Nome App: Umanitoba 14. * University of Liverpool => Nome App: UoLMobile 15. University of Virginia => Nome App: Virginia Tra tutte queste ce ne sono alcune (quelle evidenziate dall'asterisco iniziale) che sono molto simili per funzionalità ed interfaccia grafica, indicativamente cambia solo il logo dell'università ed i colori. Quindi di queste, per brevità, ne sarà illustrata solo una, quella dell'università dell'Arizona. Anche tra le rimanenti ce ne sono alcune simili tra loro, quindi ne verranno illustrate solo alcune. Proprio da questo si evince il fatto che le università Americane seguono un modello abbastanza standard per quanto riguarda lo sviluppo delle loro applicazioni mobile. Anastasia Kokonozi 566/201 Pagina 11 di 117 University of Arizona La schermata principale presenta le icone associate alle varie funzionalità dell'applicazione. Quella presa in esame è “Phonebook” che corrisponde alla rubrica. La seconda schermata, mostrata dopo aver cliccato sull'icona Phonebook della schermata principale mostra una toolbar nella parte superiore dello schermo nella quale è possibile inserire il nome/cognome da cercare e cercarlo cliccando sull'apposita icona a forma di lente di ingrandimento. Da questa schermata è possibile accedere anche ai preferiti, ovvero la lista dei contatti segnati come tali, oppure alla cronologia della ricerca dove sono mostrate le ultime ricerche effettuate. Il risultato della ricerca, se esiste almeno una corrispondenza, sarà una lista di uno o più nominativi. Anastasia Kokonozi 566/201 Pagina 12 di 117 Per accedere alle informazioni associate ad un nominativo basta cliccare sul nominativo desiderato. Selezionando il nominativo vengono mostrate le informazioni ad esso associate (solo quelle disponibili, non tutti i nominativi hanno associate le stesse informazioni). Cliccando sull'indirizzo email viene aperta l'applicazione di sistema per la gestione della posta elettronica. Cliccando, invece, sul numero di telefono viene mostrata la tastiera telefonica, con il numero precompilato, permettendo di effettuare una normale telefonata. Infine premendo il pulsante sinistro del dispositivo (tutti i dispositivi Android ne hanno uno) vengono mostrate delle impostazioni avanzate, come l'aggiunta delle informazioni visualizzate ad un contatto già esistente nella rubrica del telefono, la creazione di un nuovo contatto, oppure l'aggiunta del contatto visualizzato ai Anastasia Kokonozi 566/201 Pagina 13 di 117 preferiti (visualizzabili in seguito dalla schermata già vista in precedenza). Cardiff University La schermata principale presenta le icone associate alle varie funzionalità dell'applicazione. Quella presa in esame è “Contacts” che corrisponde alla rubrica. L'interfaccia grafica fa pensare ad un applicazione non nativa, sviluppata tramite l'utilizzo di un qualche framework (es. PhoneGap) che permette di realizzare delle web app utilizzando Html e Javascript. Questa deduzione parte dal fatto che le icone e lo stile degli elementi della pagina (input, bottoni, etc.) sembra essere proprio quello di jQuery Mobile, un framework Javascript per la realizzazione di pagine web mobile. La seconda schermata, mostrata dopo aver cliccato sull'icona Contacts della Anastasia Kokonozi 566/201 Pagina 14 di 117 schermata principale mostra una pagina dove è obbligatorio inserire il cognome e, opzionalmente, è possibile inserire il nome o parte di esso. La ricerca viene effettuata alla pressione dell'apposito tasto “Search”. Il risultato della ricerca, se esiste almeno una corrispondenza, sarà una lista di uno o più nominativi. Per accedere alle informazioni associate ad un nominativo basta cliccare sul nominativo desiderato. Selezionando il nominativo vengono mostrate le informazioni ad esso associate. Se al nominativo è associato un indirizzo email, viene mostrata un pulsante al centro della pagina tramite il quale viene aperta l'applicazione di sistema per la gestione della posta elettronica. Se al nominativo è associato un numero di telefono, viene mostrato un Anastasia Kokonozi 566/201 Pagina 15 di 117 pulsante al centro della pagina tramite il quale viene aperta la tastiera telefonica, con il numero precompilato, permettendo di effettuare una normale telefonata. University of Manitoba Anche quest'applicazione è molto simile alle altre per grafica e funzionalità ma, a differenza delle altre, permette di visualizzare anche una mappa con l'ubicazione dell'ufficio del docente cercato. Purtroppo andando a caricare la mappa viene mostrato solo un marker ma la mappa dietro non viene caricata. Cliccando sul marker viene mostrata una foto dell'edificio dove si trova l'ufficio selezionato. Anastasia Kokonozi 566/201 Pagina 16 di 117 2.3 Applicazioni delle università italiane Passando ad analizzare il panorama italiano riscontriamo la presenza di circa una ventina di applicazioni mobile a fronte di un centinaio di università italiane (statali, non statali e telematiche). Tra le applicazioni provate, quelle con funzionalità di rubrica sono solo 4: 1. Università degli Studi di Bergamo => Nome App: MyUniBG 2. Università degli Studi di Napoli “Parthenope” => Nome App: Parthenope 3. Politecnico di Torino => Nome App: PoliTo 4. Università di Calabria => Nome App: UnicalFind Università degli Studi di Bergamo La schermata principale presenta le icone associate alle varie funzionalità dell'applicazione. Quella presa in esame è “Persone” che corrisponde alla rubrica. Anastasia Kokonozi 566/201 Pagina 17 di 117 La seconda schermata, mostrata dopo aver cliccato sull'icona Persone della schermata principale, mostra un lista di nominativi ordinati alfabeticamente sul cognome. Nella pagina, inoltre, è presente una toolbar per la ricerca (più che una ricerca è un filtro, in quanto la lista è inizialmente visualizzata per intero e, successivamente, viene filtrata mostrando solo i nominativi che corrispondono al testo inserito). La ricerca (o filtro) è effettuata solo sul campo cognome e non sul nome. Per accedere alle informazioni associate ad un nominativo basta cliccare sul nominativo desiderato. Selezionando il nominativo vengono mostrare le informazioni ad esso associate. Oltre al nominativo vengono mostrati nella pagina 2 pulsanti per inviare un'email al contatto o per telefonare al contatto. Questi pulsanti sono sempre Anastasia Kokonozi 566/201 Pagina 18 di 117 visibili, anche in assenza di queste informazioni. Università degli Studi di Napoli “Parthenope” La schermata principale presenta le icone associate alle varie funzionalità dell'applicazione. Quella presa in esame è “Elenco” che corrisponde alla rubrica. La seconda schermata, mostrata dopo aver cliccato sull'icona Elenco della schermata principale, mostra un lista di nominativi ordinati alfabeticamente sul cognome con i dettagli disponibili (es. dipartimento, mail, numero di telefono, etc.). Anche qui è presente un filtro per la ricerca, effettuata sia sul campo cognome che sul campo nome. A differenza delle altre applicazioni, non è necessario cliccare sul nominativo per visualizzarne il dettaglio in quanto le informazioni disponibili per ogni Anastasia Kokonozi 566/201 Pagina 19 di 117 nominativo sono già mostrate. Cliccando invece sul nominativo viene aperta una popup con alcuni pulsanti per inviare un'email al contatto, telefonare al contatto e chiudere la popup tornando sulla schermata di ricerca. Su questa funzionalità è possibile che ci sia un bug in quanto, come si evince dallo screenshot di cui sotto, la popup aperta ha come intestazione il cognome ed il nome di una persona diversa dal contatto cercato. Politecnico di Torino La schermata principale presenta le icone associate alle varie funzionalità dell'applicazione. Quella presa in esame è “Cerca” che corrisponde alla rubrica. Anche in questo caso (vedi Cardiff University) l'interfaccia grafica fa pensare ad un'applicazione non nativa, sviluppata tramite l'utilizzo di un qualche framework (es. PhoneGap) grazie al quale è possibile sviluppare una web app in html e javascript. Anastasia Kokonozi 566/201 Pagina 20 di 117 La seconda schermata, mostrata dopo aver cliccato sull'icona Cerca della schermata principale, mostra una pagina dove inserire il nome/cognome da cercare. La ricerca viene effettuata alla pressione dell'apposito tasto “Cerca”. Il risultato della ricerca, se esiste almeno una corrispondenza, sarà una lista di uno o più nominativi. Per accedere alle informazioni associate ad un nominativo basta cliccare sul nominativo desiderato. Anche in questo caso, se al nominativo è associato un'indirizzo email, cliccando su questo viene aperta l'applicazione di sistema per la gestione della posta elettronica, mentre se al nominativo è associato un numero di telefono, cliccando su questo viene aperta la tastiera telefonica, con il numero precompilato, permettendo di effettuare una normale chiamata. Anastasia Kokonozi 566/201 Pagina 21 di 117 2.4 Analisi comparativa Le applicazioni analizzate sono, in realtà, tutte molto simili tra loro e, tra queste, alcune utilizzano lo stesso identico schema, grafico e funzionale, personalizzato solo nelle combinazioni di colori e loghi dell'università di riferimento. Di seguito una tabella riassuntiva che indica le funzionalità associate ad ogni applicazione analizzata. Questa prima tabella prende in considerazione le seguenti funzionalità: ricerca per nome/cognome, ricerca per dipartimento, gestione dei preferiti. RICERCA PER RICERCA PER PREFERITI NOME COGNOME DIPARTIMENTO NOME APP UNIVERSITA' Alabama University of Alabama SI NO SI Arizona University of Arizona SI NO SI Auburn University Auburn University SI NO NO Bucknell Bucknell University SI NO SI Cardiff University Cardiff University SI NO NO DukeMobile Duke University SI NO SI Emory Mobile Emory University SI NO SI Griffith Griffith University SI NO SI MySDMobile University of San Diego SI NO SI Northwestern Northwestern University SI NO SI OSU Mobile Ohio State University SI NO NO TAMUMobile Texas A&M Mobile SI NO SI Umanitoba University of Manitoba SI NO NO Anastasia Kokonozi 566/201 Pagina 22 di 117 UoL Mobile University of Liverpool SI NO SI Virginia University of Virginia SI NO NO MyUniBG Università degli studi di Bergamo SI NO NO Parthenope Università degl Studi di Napoli “Parthenope” SI NO NO PoliTo Politecnico di Torino SI NO NO UnicalFind Università di Calabria SI SI NO Questa seconda tabella, invece, prende in considerazione le seguenti funzionalità: gestione della cronologia, invio email dall'app, inoltro di una chiamata dall'app. NOME APP UNIVERSITA' CRONOLOGIA EMAIL CHIAMATA Alabama University of Alabama SI SI SI Arizona University of Arizona SI SI SI Auburn University Auburn University SI SI SI Bucknell Bucknell University SI SI SI SI SI SI Cardiff University Cardiff University DukeMobile Duke University SI SI SI Emory Mobile Emory University SI SI SI Griffith Griffith University SI SI SI MySDMobile University of San Diego SI SI SI Northwestern Northwestern University SI SI SI OSU Mobile Ohio State University SI SI SI Anastasia Kokonozi 566/201 Pagina 23 di 117 TAMUMobile Texas A&M Mobile SI SI SI Umanitoba University of Manitoba SI SI SI UoL Mobile University of Liverpool SI SI SI Virginia University of Virginia SI SI SI MyUniBG Università degli studi di Bergamo NO SI SI Parthenope Università degl Studi di Napoli “Parthenope” NO SI SI PoliTo Politecnico di Torino NO SI SI UnicalFind Università di Calabria NO SI SI Anastasia Kokonozi 566/201 Pagina 24 di 117 CAPITOLO III Modello di programmazione 3.1 App native vs Web app Google, Facebook, Amazon e Twitter hanno già rilasciato versioni delle proprie app in HTML 5 incapsulate all’interno di applicazioni native per iOS e Android. Un fatto che non può essere ignorato. Che differenza c’è fra questi due generi di applicazioni per dispositivi mobili? Le app native sono più potenti rispetto alle web app in termini di user experience e possono fare molto perché comunicano in maniera diretta con l'hardware, ma il web sta evolvendo molto velocemente e già rispetto all'anno scorso sono cambiate molte cose. Fra le novità ci sono framework come PhoneGap, Titanium o jQuery Mobile in grado di creare web app da esportare in applicazioni native. Le app native oggi sono ancora più performanti perché non hanno la barriera del web runtime da affrontare e possono sfruttare direttamente il processore, ma la distanza con le web app è sempre minore. Il web runtime è un software, integrato nei framework per lo sviluppo di web app, che permette di interagire con il sistema operativo. Mentre un app nativa accede direttamente all'hardware del telefono, una web app deve passare attraverso questo software per poter accedere al dispositivo. A questo si deve aggiungere che oramai anche per creare un’app per iOS occorre fare cinque o sei ulteriori sviluppi per adattarla ai vari iDevice. La situazione può essere addirittura peggiore nel caso di Android perché, data la frammentazione del sistema operativo, questo viene utilizzato su decine di diversi dispositivi che hanno caratteristiche hardware diverse e, quindi, potrebbe essere Anastasia Kokonozi 566/201 Pagina 25 di 117 necessario effettuare innumerevoli numerosi test e bug fixing per coprire il maggior numero possibile di dispositivi. Mentre le app in HTML 5 si adattano senza problemi e consentono di creare componenti personalizzati per i vari dispositivi. Analizziamo più attentamente la differenza tra le due tipologie. Web App: E’ una pagina web, accessibile da qualsiasi smartphone che supporta un web browser ed è connesso a Internet. Scritta in HTML5, JavaScript e CSS ha le sue facce negative e positive. Può inoltre essere inserita in un contesto nativo grazie all'uso di framework come PhoneGap. I pro: • Costa poco svilupparla perchè non richiede grosse competenze tecniche. Uno sviluppatore è in grado di realizzarla facendo attenzione a scrivere i fogli di stile (CSS) senza dimensioni fisse (no width e height in px), quindi utilizzando solo dimensioni in percentuali (em). Inoltre vi sono dei framework come jQuery Mobile che semplificano di molto il lavoro, soprattutto per quant riguarda la gestione degli eventi. Un evento è per esempio lo scroll, il touch su un elemento o lo swipe. • Non richiedendo grosse competenze tecniche sia il tempo di sviluppo che di testing è dimezzato rispetto alla creazione di una applicazione nativa. • Rispetto ad un’app nativa lo streaming dei contenuti multimediali è gestito meglio rendendo il tutto molto più fluido e veloce. • GPS, fotocamera e audio recorder grazie ad HTML 5 sono sensori che possono essere utilizzati senza dover per forza far uso di applicazioni native. I contro: • Bisogna porre molta attenzione alla realizzazione dell'interfaccia grafica se si vuole offrirre all'utente una user experience dello stesso livello di un app Anastasia Kokonozi 566/201 Pagina 26 di 117 nativa. • L'utilizzo intensivo di JavaScript può influire sulla velocità dell'applicazione • Per ovviare ad alcuni problemi di velocità, accedere più facilmente ai componenti hardware ed utilizzare alcuni componenti grafici delle app native, la web app deve essere integrata in un framework come PhoneGap grazie al quale viene inserita in un contesto nativo che ne permette, ad esempio, la pubblicazione sugli stores. Quando utilizzarla: • Necessità di essere compatibili con tanti dispositivi: mobile e non. • Divulgazione di informazioni di consumo. • Convertire un sito web in un sito ottimizza App Nativa: Le applicazioni native sono sviluppate in linguaggio macchina e devono essere scritte e compilate appositamente per ogni tipo di sistema operativo supportarto dal device. Per esempio le applicazioni per iOS (iPhone/iPad/iPod) sono scritte in ObjectC, quelle per Android sono sviluppate in Java mentre quelle per Windows Mobile 7 sono sviluppate ambiente .NET. Anche le applicazioni native hanno i loro pro e contro. I pro: • Possibilità di inviare delle notifiche push geolocalizzate. Le notifiche push sono dei messaggi che si possono inviare a tutti coloro che hanno installato la vostra applicazione. Simili a degli SMS sono molti utili per fare marketing sul mobile. • Accesso a tutti i sensori hardware installati sul dispositivo: NFC, accellerometro, GPS, fotocamera, magnetometro, sensore di prossimità, di Anastasia Kokonozi 566/201 Pagina 27 di 117 luminosità di posizionamento e altri. • GUI più reattiva perchè vengono effettuate chiamate dirette di sistema e il codice non è interpretato come nelle web app da un browser ma è scritto direttamente in codice macchina. I contro: • Costosa: sviluppare un'applicazione nativa base, per quanto semplice possa essere, tra sviluppo e testing richiede molto più tempo di una web app, quindi il costo di uno sviluppo nativo in termini di tempo e costi è decisamente superiore. • Le app native, essendo tali per definizione, funzionano solo per specifici dispositivi per cui sono state scritte e compilate. Di conseguenza un’app compilata per iPhone non funzionerà su un telefono che supporta come sistema operativo Android e viceversa. Quando utilizzarla? • Realizzazioni di giochi • Esigenza di accedere a particolari sensori hardware messi a disposizione dal dispositivo e non utilizzabili via HTML 5 o tramite l'uso di framework e relativi plugin nativi • 3.2 In generale, di tutte le cose che non si possono fare con pagine web Sviluppo nativo Affrontiamo brevemente il discorso dell'implementazione nativa con Java solo per poterne evidenziare le differenze rispetto all'approccio scelto, ovvero quello di realizzare l'applicazione in modo non nativo. Si afferma che il linguaggio Java per le applicazioni Android sia il Java standard (esistono delle contestazioni a questa affermazione). Un dato di fatto: la Anastasia Kokonozi 566/201 Pagina 28 di 117 virtual machine è diversa (Dalvik). Dal punto di vista del programmatore l'approccio cambia: si sta programmando un terminale mobile che risponde necessariamente ad eventi (touch schermo, azioni da tastiera). Nella tipica applicazione Android non ci sono metodi "main": tutto viene pilotato dall'utente; potremmo dire che l'utente stesso è il programma principale. I concetti fondamentali a cui è legato lo sviluppo nativo per Android sono: le Activity (attività) e l'interfaccia utente, seguiti poi da altri quali Intent e Service. Un’Activity è un componente di un’applicazione che fornisce uno schermo con il quale l’utente può interagire per fare qualcosa, come fare una telefonata, scattare una foto o mandare una email. Ogni attività è fornita di uno schermo sul quale viene disegnata l’interfaccia utente. La finestra tipicamente riempie lo schermo, ma può essere più piccola e “galleggiare” sopra altre finestre. Un’applicazione di solito consiste in attività multiple che sono vincolate l’una all’altra. Tipicamente, un’attività svolge la funzione di attività principale, viene presentata all’utente al lancio dell’applicazione per la prima volta. Ogni attività può iniziare altre attività per svolgere diversi compiti. Ogni volta che un’attività inizia, la precedente viene fermata, ma il sistema mantiene le attività in uno stack (il back stack). Quando una nuova attività inizia, viene fatto un push sul back stack e guadagna lo schermo (anche detto focus) dell’utente. Il back stack funziona ovviamente con il meccanismo del last in, first out. Quindi, quando l’utente ha finito con un’attività e preme il tasto Back (presente in tutti i telefoni Android), l’attività viene tolta dallo stack con un a pop e distrutta. L’attività immediatamente precedente viene ripresa. Quando un’attività è fermata a favore di un’altra attività che parte, viene notificato il cambiamento tramite i metodi di callback della vita di un’attività. Anastasia Kokonozi 566/201 Pagina 29 di 117 Ci sono vari metodi di callback che un attività può ricevere, dato che il sistema può crearla, fermarla, farla ripartire, distruggerla. Ogni callback fornisce la possibilità di svolgere specifiche azioni che sono appropriate per quel cambiamento di stato. Per esempio quando un’attività viene fermata, dovrebbero essere rilasciati gli oggetti che occupano più risorse, come la rete o connessioni a un database. Quando un’attività riparte, si possono riacquistare le risorse e riprendere le azioni che sono state interrotte. Queste transizioni di stato sono parte del ciclo di vita di un’attività. In un applicazione Android, l’interfaccia utente è costruita utilizzando oggetti View e ViewGroups. Ci sono vari tipi di questi oggetti, ognuno dei quali è discendente della classe View. Gli oggetti View sono l’unità di base dell’interfaccia utente. La classe View serve come base per le sottoclassi che offrono oggetti come campi testo o bottoni. La classe ViewGroup serve come base per le sottoclassi Anastasia Kokonozi 566/201 Pagina 30 di 117 chiamate che offrono diversi tipi di stile: lineare, tabulare o relativo. Un oggetto View è una struttura dati le cui proprietà contengono i parametri per lo stile ed il contenuto di uno specifico rettangolo dell’area dello schermo. Un oggetto View gestisce, relativamente al proprio rettangolo di schermo, la propria misura, l'interazione con i tasti ed il cambio di focus. Come oggetto dell’interfaccia utente, un oggetto View è anche un punto di interazione con l’utente ed un ricevitore di eventi interattivi. 3.3 PhoneGap: il framework Phonegap è un progetto Open Source della Nitobi Software, un’azienda che crea applicazioni mobile e web applications da più di dieci anni, recentemente acquisita da Adobe. Consiste in un insieme di librerie statiche che permettono di sviluppare velocemente ed efficacemente applicazioni per dispositivi mobili di diverse famiglie (piattaforme). L’idea fondamentale di questo progetto è nel cercare di realizzare lo slogan “Write once, port everywhere”. PhoneGap si propone di focalizzare gli sforzi degli sviluppatori sull’applicazione piuttosto che “perdere tempo” ad adattarla ad ogni piattaforma. Per fare ciò un’applicazione realizzata con PhoneGap richiede solo la conoscenza di HTML, CSS e JavaScript. Il porting verso le varie piattaforme viene fatto installando gli ambienti di sviluppo relativi e compilando la web app realizzata. I requisiti sono quindi di installare le SDK, dove richiesto dalla piattaforma, e gli strumenti per consentire la compilazione delle applicazioni. PhoneGap fa da ponte tra il sistema operativo e la web application realizzata dallo sviluppatore. Si intuisce che occorre che dalla web application esista una modalità standard per invocare le API native in maniera indipendente dal tipo di piattaforma sottostante. PhoneGap è infatti un framework che permette ad una web application di invocare le API native mediante funzioni JavaScript di cui è possibile vedere gli esempi forniti sul sito di riferimento. Anastasia Kokonozi 566/201 Pagina 31 di 117 Il vantaggio nell’utilizzo di questo framework è che esso fornisce l’aggancio tra la piattaforma nativa e la web application, di modo che uno sviluppatore che non ha conoscenze specifiche sui linguaggi nativi, possa dedicarsi allo sviluppo della web application con tecnologie standard. La caratteristica fondamentale di PhoneGap sta nel fatto che, una volta realizzata l’applicazione per il mobile con questo framework, l’applicazione si adatta a qualunque piattaforma supportata. PhoneGap userà il linguaggio nativo della piattaforma per accedere alle risorse hardware e software in modo da aggiungere le funzionalità di base al motore JavaScript e renderle così facilmente utilizzabili dall’applicazione come fossero tradizionali metodi di libreria. In base alla tipologia di piattaforma con la quale dovrà interfacciarsi, l’implementazione di aggancio sarà di conseguenza sviluppata in Objective C per iPhone, in Java per Android, e così via. Tale implementazione è fornita dallo stesso framework. Il risultato sarà un pacchetto composto di due elementi principali (runtime e parti statiche) con differenti responsabilità che però cooperano tra loro per fornire delle funzioni a valore aggiunto. Nel caso specifico, il runtime si occupa di dialogare direttamente con il dispositivo e le parti statiche offrono l’interfaccia verso l’utente. L’uso di JavaScript e di Ajax consente poi di dare vita alle applicazioni che possono avere comportamenti complessi e comunicazioni verso server remoti realizzando di fatto un’applicazione completa. Il progetto PhoneGap è sempre in via di sviluppo. La documentazione sulle api è aggiornata costantemente. Le funzionalità disponibili al momento sono: • accelerometro: utilizza il sensore di moto • bussola: ottiene la direzione in cui il dispositivo punta • camera: cattura una immagine con la fotocamera • contatti: lavora con il database dei contatti • device info: raccogli informazioni sul telefono • events: aggancia gli eventi nativi tramite JavaScript Anastasia Kokonozi 566/201 Pagina 32 di 117 • file: connetti al file system nativo tramite JavaScript • geolocazione: rende l’applicazione coscente della locazione • media: registra e ascolta file audio • network: controlla lo stato della rete • notifica: notifiche visuali, acustiche, tattili • storage: agganciati alle opzioni dello storage nativo del sistema La creazione di un’applicazione richiede la sola conoscenza dei linguaggi: HTML, CSS, JavaScript. Questi tre linguaggi contribuiscono a creare la struttura della pagina web (HTML), di personalizzarne il suo aspetto (CSS), e di renderla dinamica ed interattiva mediante il codice JavaScript. 3.4 Compatibilità Le piattaforme (sistemi operativi mobile) attualmente supportati da questo framework sono: • Android • Blackberry • iOS • Symbian • WebOS • Windows Phone • Windows 8 • Bada • Tizen Anastasia Kokonozi 566/201 Pagina 33 di 117 CAPITOLO IV L'implementazione 4.1 Ambiente di sviluppo: Server Per la preparazione dell'ambiente di sviluppo è stata analizzata l'architettura del sistema FastGuttel e si è cercato di replicare la parte di nostro interesse. I dati di FastGuttel sono memorizzati su un server LDAP. Quindi è stato installato e configurato il server LDAP, è stata importata una copia dei dati utilizzati dal sistema FastGuttel ed è stato sviluppato un WebService che si occupa di interrogare il database ed estrarre i dati necessari al corretto funzionamento dell'applicazione. 4.1.1 Installazione e configurazione di LDAP LDAP (Lightweight Directory Access Protocol, tradotto Protocollo d'accesso agli annuari leggeri) è un database gerarchico (diverso, quindi, da MySql che è un database relazionale). Viene usato principalmente per memorizzare dati relativi a degli utenti. LDAP presenta le informazioni sotto forma di albero, nel quale le informazioni, dette dati (entry), sono rappresentate sotto forma di rami. Ogni entry è costituita da un insieme di coppie chiave/valore dette attributi. Anastasia Kokonozi 566/201 Pagina 34 di 117 Il sistema operativo del server utilizzato per l'ambiente di sviluppo è Ubuntu. Per installare LDAP sul server, una volta effettuato l'accesso allo stesso, va digitato il comando: sudo apt-get install slapd Analizziamo il comando dato: • sudo: viene utilizzato per eseguire un comando come amministratore. Viene successivamente richiesta la password di root per eseguire il comando. • apt-get: si tratta di uno strumento a riga di comando usato per operare con l'APT (Advanced Packaging Tool) al fine di eseguire operazioni come l'installazione, l'aggiornamento o la rimozione di pacchetti software • install: opzione di apt-get che indica che vogliamo installare un nuovo pacchetto • slapd: è il nome del pacchetto software da installare (OpenLDAP Server) Al termine dell'installazione bisogna effettuare la configurazione del server LDAP digitando questo comando: sudo dpkg-reconfigure slapd Viene eseguita, quindi, una procedura guidata che, tramite una serie di richieste, Anastasia Kokonozi 566/201 Pagina 35 di 117 permette di configurare il tutto. Analizziamo queste richieste con i relativi dati inseriti: 1. Omettere la configurazione del server OpenLDAP? • La risposta è, ovviamente, No 2. Nome di dominio DNS: • In genere si usa far corrispondere un dominio LDAP al dominio DNS della struttura che si deve rappresentare. Quindi inseriamo: unina.it 3. Nome dell'organizzazione: • Il dominio viene generalmente suddivisio in Organization Unit (OU) in base alla naturale suddivisione della realtà. Inseriamo: University 4. Password dell’amministratore: • La password che sarà associata all'account dell'amministratore del server LDAP. Inseriamo: 4pprubr1c4! 5. Conferma della password: • Per evitare possibili errori di digitazione viene richiesto nuovamente l'inserimento della password: 4pprubr1c4! 6. Database di backend da usare: • Si fa riferimento a due possibili soluzioni di Database Caching, BDB ed HDB. Ognuna con i suoi pro e contro. Manteniamo il valore di default: HDB 7. Eliminare il database in caso di rimozione completa di slapd? • Trattandosi di un'ambiente di sviluppo, in caso di problemi con il database, sarebbe sicuramente preferibile la rimozione completa con la disinstallazione, in modo da riavere una base dati “pulita” a seguito di una reinstallazione. Rispondiamo: Si 8. Abilitare il protocollo LDAPv2? • Il protocollo corrente utilizzato per lo scambio dei dati tra il client ed il server è LDAPv3, ma in fase di configurazione viene offerta la possibilità Anastasia Kokonozi 566/201 Pagina 36 di 117 di abilitare il supporto anche al precedente protocollo LDAPv2. Non avendo questa necessità, rispondiamo: No Lo username dell'amministratore viene generato automaticamente con una parte fissa cn=admin ed una parte variabile che dipende dal nome di dominio dc=unina,dc=it Il risultato è: cn=admin,dc=unina,dc=it e la password ad esso associata è quella scelta durante la configurazione. 4.1.2 Importazione dei dati su LDAP A questo punto il server LDAP è installato e configurato, pronto per l'inserimento dei dati. Andremo quindi a importare una copia dei dati presenti sul database di produzione nella nostra installazione. Il file contenente l'export dei dati è in formato LDIF (LDAP Data Interchange Format): questo formato è usato per rappresentare i campi di LDAP in un formato testo semplice. Per importare questo file, però, è necessario installare le LDAP Utils. Si tratta di un pacchetto di utility composto dai seguenti componenti: • ldapadd: utilizzato per il caricamento di nuovi record sul database • ldapdelete: utilizzato per la cancellazione di record dal database • ldapmodify: utilizzato per modificare i record del database • ldapsearch: utilizzato per effettuare ricerche sul database Installiamo, quindi, questo pacchetto con il seguente comando: sudo apt-get install ldap-utils Dei componenti visti, quello che c'interessa per importare i dati è ldapadd. Procediamo all'import dei dati su LDAP utilizzando il file LDIF, con il seguente comando: sudo ldapadd -Wx -D “USERNAME” -f /PERCORSO-DEL-FILE/export.ldif Anastasia Kokonozi 566/201 Pagina 37 di 117 Analizziamo il comando dato: • sudo: viene utilizzato per eseguire il comando come amministratore • ldapadd: è il tool utilizzato per aggiungere dei record al database • -Wx: opzione di ldapadd per l'autenticazione semplice, cioè la password non viene passata da riga di comando, ma viene richiesta dopo l'esecuzione del comando • -D “USERNAME”: opzione per specificare lo username da usare per accedere al database, sarà successivamente richiesta la password di questo account • -f /PERCORSO-DEL-FILE-LDIF: opzione per specificare il percorso di un file contenente le informazioni da aggiungere sul database 4.1.3 WebService/API Un WebService è un componente applicativo. Possiamo definirlo come un sistema software in grado di mettersi al servizio di un'applicazione comunicando su di una medesima rete tramite il protocollo HTTP. Un WebService consente, quindi, alle applicazioni che vi si collegano di usufruire delle funzioni che mette a disposizione. Un sistema può interagire con un WebService tramite appositi “messaggi”, trasportati tramite il protocollo HTTP e generalmente formattati secondo lo standard XML. L'XML (eXtensible Markup Language) è un meta linguaggio di markup, non ha tag predefiniti e non serve per definire pagine Web né per programmare. Esso serve esclusivamente per definire altri linguaggi. Non è altro, quindi, che un insieme standard di regole sintattiche per modellare la struttura di documenti e dati. Nell'ambito dei WebService, lo standard più diffuso, basato su XML (per la modellazione della struttura dei dati da scambiare), è SOAP (Simple Object Access Protocol). Un WebService è in grado di offrire un'interfaccia software assieme alla Anastasia Kokonozi 566/201 Pagina 38 di 117 descrizione delle sue caratteristiche, cioè è in grado di farci sapere che funzioni mette a disposizione (senza bisogno di conoscerle a priori) e ci permette inoltre di capire come vanno utilizzate. Ciò significa che con una semplice connessione al WebService, anche senza conoscerlo, possiamo stabilire le operazioni che fornisce e possiamo subito iniziare ad usarle perché ogni operazione ha una sua descrizione comprendente i parametri che si aspetta di ricevere, quelli che restituirà ed il tipo di entrambi. JavaScript, però, non dispone delle funzioni per l'interrogazione di un WebService. Questo non significa che JavaScript non può interrogare un WebService, può farlo ma la request XML da spedire per una richiesta deve essere formattata “manualmente” e non si può sfruttare quella che è una delle caratteristiche principali di un WebService, ovvero l'autodiscover (la possibilità di conoscere le operazioni disponibili corredate dei parametri richiesti in input e quelli restituiti in output) proprio perché questa funzionalità non è stata implementata nelle librerie JavaScript. Per interagire con l'applicazione si è scelto, quindi, un approccio leggermente diverso dettato principalmente dal fatto che l'applicazione è stata realizzata in JavaScript, con il quale è molto più semplice lavorare con dati in formato JSON: acronimo di JavaScript Object Notation, è una tecnologia, ma sarebbe meglio definirlo un "formato", concepito per l'interscambio dei dati all'interno di applicazioni di tipo client-server. Non parleremo più, quindi, di WebService ma, per una maggior precisione, parleremo di API (Application Programming Interface): un insieme di metodi, ognuno caratterizzato dalla propria URL (indirizzo web), ai quali vanno passati i parametri richiesti. Queste API sono state realizzate in PHP. Si tratta di un linguaggio di scripting interpretato, principalmente concepito per la realizzazione di pagine web dinamiche. Un linguaggio di scripting interpretato significa che il codice, scritto in tale linguaggio, deve essere eseguito da un altro programma (il programma madre o un suo modulo). A differenza dell'HTML che non è un Anastasia Kokonozi 566/201 Pagina 39 di 117 linguaggio di programmazione, bensì un linguaggio di markup (ossia descrive le modalità di impaginazione, formattazione e visualizzazione grafica che vengono lette ed interpretate dal browser), il PHP non è processato dal browser ma deve essere processato da un web server: nativamente l'ambiente “naturale” di PHP è un server UNIX per il quale il più diffuso web server è Apache, ma ce ne sono anche altri come Lighttpd, ad esempio; nulla vieta però di usare questo linguaggio anche con IIS, il web server per Windows. Il PHP permette il passaggio di parametri da una pagina ad un'altra sfruttando tre array di variabili: $_GET, $_POST, $_SESSION. Il primo tipo di parametro ($_GET) viene passato tramite la stringa che compare come indirizzo nella relativa barra del browser; il secondo tipo di parametro ($_POST) viene passato in background; il terzo di parametro ($_SESSION) non solo viene passato in background, ma resta persistente durante la sessione, quindi fino a che non viene chiuso il browser oppure fino a quando non viene distrutta la variabile di sessione. Nell'implementazione delle API si è scelto di accettare i parametri di input sia in GET (principalmente per poter fare dei rapidi test direttamente da browser cambiando facilmente i parametri, presenti sulla url chiamata) che in POST. I metodi messi a disposizione dalle API implementate sono: 1. listaUtenti • Parametri d'input: nome, cognome, numero, struttura • Descrizione: questo metodo cerca gli utenti che corrispondono ai parametri passati in input. Se, ad esempio, viene chiamato questo metodo passandogli solo il parametro nome, viene restituita la lista di tutti gli utenti che hanno quel nome. Se viene chiamato passandogli i parametri cognome e struttura, viene restituita la lista di tutti gli utenti che hanno quel cognome e che appartengono a quella struttura, etc. etc. • Output: Array(“error”, “result”) Anastasia Kokonozi 566/201 Pagina 40 di 117 ◦ error: true se si è verificato un errore, false altrimenti ◦ result: false se si è verificato un errore, altrimenti: Array(“cognome”, “nome”, “dipartimento”, “telefono”, “email”, “uid”) 2. cercaUtente • Parametri d'input: uid (significa user identifier, quindi lo username) • Descrizione: questo metodo cerca e restituisce l'utente che corrisponde all'uid passato in input restituendone tutti i dettagli. • Output: Array(“error”, “result”) ◦ error: true se si è verificato un errore, false altrimenti ◦ result: false se si è verificato un errore, altrimenti: Array(“cognome”, “nome”, “dipartimento”, “telefono”, “email”, “uid”) 3. login • Parametri d'input: uid, password • Descrizione: questo metodo verifica la correttezza delle informazioni passate in input. Se esiste l'uid e la password ad esso associata è corretta, restituisce true, altrimenti restituisce false. • Output: Array(“error”) ◦ error: true se i dati inseriti non sono corretti, false altrimenti 4. cercaNome • Parametri d'input: nome • Descrizione: questo metodo viene utilizzato per l'autocompletamento del campo nome. In pratica estrae tutti gli utenti che hanno un nome che contiene la stringa passata in input. Così se scriviamo, ad esempio, gui la ricerca restituisce tutti gli utenti il cui nome contiene la stringa gui (Guido, ad esempio) e questi vengono utilizzati come suggerimenti per permetterci di accedere più rapidamente all'informazione cercata. • Output: Array(“error”, “result”) ◦ error: true se si è verificato un errore, false altrimenti Anastasia Kokonozi 566/201 Pagina 41 di 117 ◦ result: false se si è verificato un errore, altrimenti: Array(“nome”) 5. cercaCognome • Parametri d'input: cognome • Descrizione: questo metodo viene utilizzato per l'autocompletamento del campo cognome. Estrae, quindi, tutti gli utenti che hanno un cognome che contiene la stringa passata in input. Così se scriviamo, ad esempio, rus la ricerca restituisce tutti gli utenti il cui cognome contiene la stringa rus (Russo, ad esempio) e questi vengono utilizzati come suggerimenti per permetterci di accedere più rapidamente all'informazione cercata. • Output: Array(“error”, “result”) ◦ error: true se si è verificato un errore, false altrimenti ◦ result: false se si è verificato un errore, altrimenti: Array(“cognome”) 6. cercaNumero • Parametri d'input: numero • Descrizione: questo metodo viene utilizzato per l'autocompletamento del campo numero (di telefono). In pratica estrae tutti gli utenti che hanno un numero di telefono che contiene la stringa passata in input. Così se scriviamo, ad esempio, 081 la ricerca restituisce tutti gli utenti il cui numero di telefono contiene la stringa 081 e questi vengono utilizzati come suggerimenti per permetterci di accedere più rapidamente all'informazione cercata. • Output: Array(“error”, “result”) ◦ error: true se si è verificato un errore, false altrimenti ◦ result: false se si è verificato un errore, altrimenti: Array(“numero”) 7. cercaStruttura • Parametri d'input: struttura • Descrizione: questo metodo viene utilizzato per l'autocompletamento del campo struttura. In pratica estrae tutti gli utenti associati ad una struttura Anastasia Kokonozi 566/201 Pagina 42 di 117 il cui nome contiene la stringa passata in input. Così se scriviamo, ad esempio, dip la ricerca restituisce tutti gli utenti associati ad una struttura il cui nome contiene la stringa dip (Dipartimento di scienze fisiche, ad esempio) e questi vengono utilizzati come suggerimenti per permetterci di accedere più rapidamente all'informazione cercata. • Output: Array(“error”, “result”) ◦ error: true se si è verificato un errore, false altrimenti ◦ result: false se si è verificato un errore, altrimenti: Array(“struttura”) Il funzionamento delle API può essere testato da browser passando i parametri in GET, mentre all'interno dell'applicazione il passaggio dei parametri può essere fatto in POST. Per invocare un metodo va chiamata la URL delle API seguita dal nome del metodo e, quindi, vanno passati i parametri. Un esempio generico di come deve essere costruita la URL per un test da browser è il seguente: http://SERVER/nomeMetodo/?parametro1=valore&parametro2=valore Questo invece è un'esempio specifico di chiamata al metodo login: http://SERVER/login/?uid=guidrus&password=demo E' possibile cambiare la base dati utilizzata senza bisogno di modificare l'applicazione. Per far questo basta utilizzare le API mantenendo la struttura ed il formato di output. Per modificare le API per leggere i dati da un database MySql o da un altro WebService SOAP, ad esempio, anziché da un database LDAP bisogna sfruttare sempre i metodi già presenti ed i parametri di input che vengono inviati dall'applicazione. Il codice delle API va, però, modificato come di seguito: 1. database MySql: • preparare la query per estrarre dal database i dati necessari, sfruttando i parametri ricevuti in input Anastasia Kokonozi 566/201 Pagina 43 di 117 • effettuare la connessione al server MySql specificando i parametri per la connessione • eseguire la query tramite la connessione attiva • ciclare i dati restituiti dalla query per costruire un'array con la struttura prevista • chiudere la connessiona al database • restituire l'array costruito encodizzato nel formato JSON 2. WebService SOAP: • preparare la request per effettuare la chiamata SOAP, sfruttando i parametri ricevuti in input • istanziare il SOAP Client, necessario per comunicare con il WebService SOAP • chiamare il metodo necessario del WebService passando la request costruita in precedenza • decodificare i dati restituiti dalla chiamata in formato XML • ciclare i dati decodificati per costruire un'array con la struttura prevista • restituire l'array costruito encodizzato nel formato JSON 4.2 Ambiente di sviluppo: Android Android è un sistema operativo open source inizialmente sviluppato da Android Inc. e successivamente acquistato da Google nel 2005. La piattaforma è basata sul kernel Linux, usa il database SQLite, la libreria dedicata SGL per la grafica bidimensionale e supporta lo standard OpenGL per la grafica tridimensionale. Le applicazioni vengono eseguite tramite la Dalvik Virtual Machine che è a tutti gli effetti una Java Virtual Machine adattata ai dispositivi mobili. Questa macchina virtuale è ottimizzata per sfruttare la poca memoria presente Anastasia Kokonozi 566/201 Pagina 44 di 117 nei dispositivi mobili, consente inoltre di utilizzare diverse istanze della macchina virtuale contemporaneamente e nasconde al sistema operativo sottostante la gestione della memoria e dei thread. Per sviluppare applicazioni Android è necessario preparare l'ambiente di sviluppo. La preparazione di questo si concentra sull'installazione e configurazione delle librerie di Android necessarie e sull'individuazione degli strumenti necessari per lo sviluppo ed il testing. 4.2.1 Installazione e configurazione di Android SDK Il primo passo per creare l'ambiente di sviluppo Android è l'installazione dell'Android SDK. SDK è una sigla che sta per Software Development Kit, vale a dire un kit di sviluppo di software. Attraverso questo kit di sviluppo, qualsiasi utente potrà procurarsi il codice sorgente di ogni versione Android e modificarlo o migliorarlo autonomamente. Con l'SDK, scaricabile dal sito ufficiale, sarà quindi possibile disporre su pc di un'interfaccia per lanciare applicazioni Android e modificarle. L'SDK è disponibile per i vari sistemi operativi (Windows, Mac OS X, Linux), di seguito vediamo la procedura per l'installazione su Linux. Scaricare ed estrarre il pacchetto .tar.gz, relativo alla versione di Linux, reperibile dal sito ufficiale di Android Developers: http://developer.android.com/sdk/ Prima di iniziare a configurare SDK è necessario preparare il nostro sistema in modo da procedere correttamente con l'installazione. Uno dei tools inclusi nell'SDK è l'AVD Manager (Android Virtual Device Manager), un tool utilizzato per la gestione dell'emulatore, tramite la creazione di device virtuali, dei quali è possibile specificare le caratteristiche (versione di Android, risoluzione del device, memoria, etc.) in fase di creazione. Anastasia Kokonozi 566/201 Pagina 45 di 117 Per funzionare questo tool necessita che sia installato Java sul sistema, quindi se non è installato provvediamo ad installare OpenJDK, un'implementazione di Java che permetterà il corretto funzionamento dell'SDK, tramite il seguente comando: sudo apt-get install openjdk-6-jre openjdk-6-jdk Android SDK contiene, inoltre, un importante tool chiamato Android Debug Bridge (ADB). Esso è uno strumento utilizzabile da linea di comando, utilizzato per controllare e comunicare con dispositivi collegati con cavo USB. Una volta installato, è possibile utilizzare ADB per copiare e trasferire file dal computer al dispositivo mobile, installare applicazioni, lanciare comandi, visualizzare file di log e molto altro. Per ottenere questi strumenti è necessario eseguire Android SDK Manager. Spostarsi quindi nella cartella ~/android-sdk-linux_x86/tools e digitare in un terminale il seguente comando: ./android Nella nuova finestra che comparirà, saranno disponibili le librerie per le varie versioni di Android, dalla 1.5 all'attuale 4.2, e gli SDK Tools. Spuntare, quindi, tutti gli elementi da installare e procedere con l'installazione cliccando su “Install packages”. Per un corretto funzionamento dell'SDK è necessario impostare il percorso dove si trovano i files dell'SDK in modo che questo sia accessibile da qualsiasi directory. Modificare con un editor di testo il file .bashrc presente nella propria home e inserire all'inizio del file le seguenti linee di testo: #AndroidDev PATH export PATH=${PATH}:~/android-sdk-linux_x86/tools export PATH=${PATH}:~/android-sdk-linux_x86/platform-tools L'installazione dell'Android SDK è ora completata. Se si dispone di un dispositivo con sistema operativo Android da utilizzare per lo sviluppo, basterà lanciare in un terminale il seguente comando, per controllare se il proprio dispositivo viene Anastasia Kokonozi 566/201 Pagina 46 di 117 riconosciuto dall'ADB: adb devices Comparirà in elenco il dispositivo collegato tramite cavo USB. 4.2.2 Installazione di Eclipse ed ADT Plugin Ora che è pronto l'ambiente Android abbiamo bisogno di un'IDE (Integrated Development Environment, in italiano Ambiente di Sviluppo Integrato). E' un software che, in fase di programmazione, “aiuta” i programmatori nello sviluppo del codice sorgente di un programma. Il più diffuso per lo sviluppo di Android è Eclipse, davvero molto semplice da installare. Si tratta dell'IDE più diffuso principalmente perchè per questo è disponibile un plugin ufficiale di Android che permette di integrare una serie di funzionalità aggiuntive altrimenti non disponibili su altri IDE. Per l'installazione di Eclipse basta scaricare il pacchetto .tar.gz per Linux dal sito ufficiale http://www.eclipse.org e decomprimerlo. Una volta decompresso verrà creata una cartella contenente un file eseguibile eclipse che, eseguito, inizializzerà l'IDE. ADT sta per Android Development Tools. Questo plugin è stato realizzato per permettere agli sviluppatori di eseguire più velocemente alcune operazioni in fase di sviluppo, quali la creazione di un nuovo progetto per Android, la creazione di interfacce grafiche, il debug delle applicazioni sviluppate, etc. Dopo aver installato Eclipse aprirlo e cliccare su “Help > Install new software”. Nella finestra che compare cliccare su “Add” per aggiungere un nuovo repository e nella finestra di dialogo che si aprirà inserire “ADT plugin” nella casella “Name” e “https://dl-ssl.google.com/android/eclipse/” nella casella “Location” (è l'indirizzo del repository dal quale scaricare l'ultima versione del plugin). Cliccare OK e chiudere la finestra di dialogo. A questo punto nella finestra “Available Software” spuntare la voce Anastasia Kokonozi 566/201 Pagina 47 di 117 “Developer Tools” e cliccare “Next”. Poi cliccare ancora “Next” ed, infine “Finish” (dopo aver accettato la licenza). Quando l’installazione sarà completata sarà necessario riavviare Eclipse. Ora bisogna dire ad Eclipse dove si trova l’Andoid SDK. Cliccare su “Window > Preferences” e selezionare dalla colonna sinistra la voce “Android”. Nel pannello a destra, nella voce “Location” inserire il percorso completo della cartella nella quale abbiamo installato l’Android SDK. 4.2.3 Creazione di un nuovo progetto con PhoneGap Per la creazione di un nuovo progetto con PhoneGap è necessario, innanzitutto, configurare l'ambiente di sviluppo per Android. Una volta configurato il tutto, si può procedere alla creazione del progetto con PhoneGap. A questo scopo PhoneGap mette a disposizione delle utility a linea di comando (per Windows, Mac OS X e Linux) grazie alle quali è possibile creare la struttura base per un nuovo progetto, da importare successivamente su Eclipse. Cominciamo scaricando l'ultima versione stabile dal sito ufficiale http://www.phonegap.com Decomprimendo il file scaricato abbiamo 2 directory: • doc: contiene tutta la documentazione per l'utilizzo del framework e delle sue funzionalità sulle varie piattaforme supportate • lib: contiene le librerie per la creazione di un nuovo progetto sulle varie piattaforme disponibili Apriamo un terminale ed accediamo alla cartella di Android: ~/phonegap/lib/android/bin e lanciamo il seguente comando: ./create <project_folder_path> <package_name> <project_name> Analizziamo il comando: • ./create: esecuzione del tool per la creazione di un nuovo progetto Anastasia Kokonozi 566/201 Pagina 48 di 117 • project_folder_path: è il percorso dove sarà salvato il nuovo progetto • package_name: è il nome del package, es: it.unina.rubrica • project_name: è il nome del progetto, es: Rubrica Unina Fatto questo viene creato il nuovo progetto nel percorso specificato e non resta che importarlo su Eclipse. Lanciamo, quindi, Eclipse e creiamo un nuovo progetto. Tra le opzioni proposte dal wizard per la creazione di un nuovo progetto, selezioniamo “Android Project from Existing Code” (Progetto Android da Codice Esistente) ed indichiamo il percorso nel quale abbiamo creato il nuovo progetto. Nel progetto così creato ci sarà una directory assets/www nella quale è presente una index.html, che è il file che viene caricato all'avvio dell'applicazione, ed in questa cartella va sviluppata l'applicazione inserendo i propri files, immagini, css, javascript, html, etc. 4.3 Struttura del progetto La struttura del progetto è stata suddivisa in directory, per una migliore comprensione e manutenibilità del codice: • config: contiene i files JavaScript per la configurazione dell'applicazione, come ad esempio i testi utilizzati, le url per le chiamate Ajax, etc. • css: contiene i fogli di stile che definiscono l'impostazione grafica dell'applicazione • html: contiene le pagine HTML in cui è definita la struttura del template dell'applicazione • img: contiene le immagini usate nell'applicazione, quali loghi e icone • js: contiene tutti i files JavaScript nei quali è contenuta la logica applicativa Anastasia Kokonozi 566/201 Pagina 49 di 117 4.3.1 AndroidManifest Il codice seguente, invece, fa riferimento all'AndroidManifest.xml Si tratta di un file richiesto per qualsiasi applicazione. Si trova nella cartella principale e descrive le variabili globali del package, le versioni minima e massima di Android supportate, gli schermi supportati (smallScreens, normalScreens, largeScreens, etc), i permessi richiesti per l'installazione dell'app, etc. <manifest xmlns:android="http://schemas.android.com/apk/res/android" android:windowSoftInputMode="adjustPan" package="it.unina.rubrica" android:versionName="1.0" android:versionCode="5"> <supports-screens android:largeScreens="true" android:normalScreens="true" android:smallScreens="true" android:resizeable="true" android:anyDensity="true" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <application android:icon="@drawable/icon" android:label="@string/app_name" android:debuggable="true"> <activity android:name="mainRubrica" android:label="@string/app_name" android:theme="@android:style/Theme.Black.NoTitleBar" android:screenOrientation="nosensor" android:configChanges="keyboardHidden|orientation"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> <uses-sdk android:minSdkVersion="7" android:targetSdkVersion="16"/> </manifest> Anastasia Kokonozi 566/201 Pagina 50 di 117 Una delle prime informazioni da indicare nel file è il package, quindi il nome del pacchetto, associato all'app. Questo nome verrà poi utilizzato anche per la pubblicazione su Google Play, il market di Android. E' stato utilizzato: it.unina.rubrica Al package sono associate altre informazioni quali il numero della versione ed un eventuale nome ad esso associato. Vengono poi indicati gli “screens” (schermi) supportati. La configurazione utilizzata non è per niente restrittiva, permette quindi l'installazione e l'esecuzione dell'app su tutti i dispositivi, indipendentemente dallo schermo che hanno. Un altro parametro molto importante è quello relativo ai permessi. Ogni volta che si installa un'applicazione su Android, come si può notare, vengono evidenziati i vari permessi richiesti dall'app per funzionare. Tali permessi sono relativi alla lettura di dati del telefono come la rubrica ed i messaggi ed all'uso di alcune funzioni di Android come la connessione internet. I permessi su Android sono del tipo prendere o lasciare quindi, se non si è d'accordo, non rimane che rinunciare all'applicazione. Nella maggior parte dei casi, le app chiedono le autorizzazioni per usare le funzionalità e per leggere dati interni che, se gli fossero negati, ne comprometterebbero il funzionamento. In altri casi, però, i permessi sono sicuramente esagerati e può non servire, per esempio, che un gioco debba richiedere la localizzazione oppure la connessione internet. I permessi utilizzati dall'app sono i seguenti: • android.permission.INTERNET: permesso per l'accesso ad internet • android.permission.ACCESS_NETWORK_STATE: permesso per accedere alle informazioni sullo stato della rete (utilizzato per verificare la disponibilità di connessione al lancio dell'app) Per ogni schermata dell'applicazione deve essere inserito un tag activity che specifichi le informazioni di quella schermata, quali il nome dell'activity, i componenti, l'orientamento, etc. Utilizzando PhoneGap, viene creata una sola activity all'interno della quale viene eseguito il flusso di navigazione dell'app. Su Anastasia Kokonozi 566/201 Pagina 51 di 117 questa activity è stata disattivata la rotazione del dispositivo, per forzare la visione dell'app con l'orientamento verticale (detto portrait) per una miglior visualizzazione delle informazioni. Vanno poi specificate le versioni minime e massime di Android supportate dall'app. Le versioni di Android supportate sono indicante mediante API level. Esiste cioè un livello di API corrispondente ad ogni versione del sistema operativo. Ad esempio la versione 2.1 (Eclair) che è la versione minima supportata corrisponde al livello 7 delle API. Mentre l'ultima versione di Android 4.1 (Jelly Bean) che è la versione massima supportata corrisponde al livello 16. Di seguito la tabella che mostra l'associazione tra i livelli delle API e le versioni di Android a cui corrisponde anche un nome: Platform Version API Level VERSION_CODE Android 4.1, 4.1.1 16 JELLY_BEAN Android 4.0.3, 4.0.4 15 ICE_CREAM_SANDWICH_MR1 14 ICE_CREAM_SANDWICH Android 3.2 13 HONEYCOMB_MR2 Android 3.1.x 12 HONEYCOMB_MR1 Android 3.0.x 11 HONEYCOMB 10 GINGERBREAD_MR1 9 GINGERBREAD Android 2.2.x 8 FROYO Android 2.1.x 7 ECLAIR_MR1 Android 2.0.1 6 ECLAIR_0_1 Android 2.0 5 ECLAIR Android 1.6 4 DONUT Android 4.0, 4.0.1, 4.0.2 Android 2.3.4 Android 2.3.3 Android 2.3.2 Android 2.3.1 Android 2.3 Anastasia Kokonozi 566/201 Pagina 52 di 117 Android 1.5 3 CUPCAKE Android 1.1 2 BASE_1_1 Android 1.0 1 BASE 4.3.2 HTML5 Il panorama di Internet è cambiato molto dalla precedente versione delle specifiche (HTML 4), avvenuta verso la fine del 1999. In quel tempo il Web era strettamente legato al concetto di ipertesto e l’azione più comune per l’utente era la fruizione di contenuti, tipicamente in forma testuale. La mediamente bassa velocità di connessione e il limitato investimento sul media contribuivano ad una scarsa presenza di applicazioni web, più care da sviluppare ed esigenti in termini di banda. Tutto questo era ben rappresentato da un linguaggio, l'HTML, principalmente orientato ad agevolare la stesura di semplici documenti testuali collegati fra loro. Negli anni successivi l’interesse intorno alla rete ha subito una brusca accelerazione e questo ha condizionato positivamente sia la diffusione che la velocità di connessione della stessa, attirando di conseguenza maggiori investimenti e ricerca. Al modello di fruizione dei contenuti si è aggiunta la possibilità per l’utente finale di divenire esso stesso creatore attraverso applicazioni web sempre più elaborate ed interessanti. Questo nuovo livello di complessità, in termini di sviluppo, ha però dovuto scontrarsi con un set di specifiche poco inclini ad essere utilizzate per tali fini. Parallelamente il percorso di crescita del web ha fatto emergere alcune strutture di contenuto ricorrenti, ben caratterizzate dal fenomeno dei blog: informazioni di testata, menu di navigazione, elenchi di articoli, testo a piè di pagina, ed altri. La parte dedicata al singolo articolo presenta anch’essa solitamente lo stesso set di informazioni quali autore, data di pubblicazione, titolo e corpo del messaggio. Anche in questo caso l’HTML4 non ha saputo fornire gli strumenti adatti a Anastasia Kokonozi 566/201 Pagina 53 di 117 consentire una corretta gestione e classificazione del contenuto obbligando gli sviluppatori web a ripiegare su strutture anonime, quali <div> e <p>, arricchite di valore semantico con l’utilizzo di attributi quali class e id. L’HTML5 nasce per risolvere questi problemi offrendo agli sviluppatori web un linguaggio pronto ad essere plasmato secondo le più recenti necessità, sia dal lato della strutturazione del contenuto che da quello dello sviluppo di vere e proprie applicazioni. A questo segue anche un rinvigorimento delle API JavaScript che vengono estese per supportare tutte le funzionalità di cui una applicazione moderna potrebbe aver bisogno: • salvare informazioni sul device dell’utente • accedere all’applicazione anche in assenza di una connessione Web • comunicare in modo bidirezionale sia con il server sia con altre applicazioni • eseguire operazioni in background • pilotare flussi multimediali (video, audio) • editare contenuti anche complessi, come documenti di testo • pilotare lo storico della navigazione • generare grafica 2D e 3D in tempo reale • accedere e manipolare informazioni generate in tempo reale dall’utente attraverso sensori multimediali quali microfono e webcam Tornando sull'HTML tra i cambiamenti più importanti c'è il fatto di non utilizzare più le tabelle, impropriamente utilizzate in precedenza per creare la struttura di una pagina, e tutti i tag relativi al concetto di frame, ritenuti pericoloso per l'usabilità delle pagine stesse. E non dimentichiamo che l’evoluzione dell’HTML viaggia di pari passo con quella dei CSS e che ognuno di questi componenti è progettato nella sua versione più recente per recare e ricevere beneficio dagli altri. Anastasia Kokonozi 566/201 Pagina 54 di 117 4.3.3 CSS – Responsive design I CSS (Cascading Style Sheet) comunemente detti fogli di stile, servono per separare i contenuti dalla formattazione e permettere una programmazione più chiara e facile da utilizzare. Con i fogli di stile possiamo definire la rappresentazione di documenti HTML ed XHTML seguendo le regole contenute in un insieme di direttive emanate a partire dal 1996 dal W3C (World Wide Web Consortium). In pratica partendo dal concetto che l’HMTL, così come la sua recente evoluzione, l’XHTML, dovrebbe essere visto come un linguaggio strutturale, alieno da qualunque scopo attinente la presentazione di un documento, sono stati designati i CSS grazie ai quali è possibile separare il contenuto dalla presentazione dando al testo della pagina l’aspetto desiderato: è possibile cambiare il colore o il font del testo, così come anche le dimensioni, il posizionamento, i margini ed i bordi per il testo e per tutti gli altri elementi presenti nella pagina. Ci sono diversi standard CSS, il primo CSS 1 risale al 1996; nel 1998 è stata la volta della seconda versione, CSS 2, che non presenta stravolgimenti, ma molte aggiunte rispetto alla prima, e nel 2004 sono state emanate le specifiche per il CSS 2.1, la naturale evoluzione del CSS 2 ed, attualmente, lo standard più diffuso. E’ in cantiere anche il CSS 3, di cui le specifiche non sono state ancora rilasciate, sebbene il W3C pubblichi costantemente informazioni sulle novità in fase di sviluppo. Il CSS 3 presenta soluzioni per la risoluzione di alcuni bug di interpretazione di Internet Explorer ed una soluzione per realizzare i bordi arrotondati la cui realizzazione affligge da sempre i webmaster. Nonostante non siano state ancora rilasciate le specifiche “finali” per il CSS 3, la maggior parte dei browser (nelle versioni più recenti) già supportano diverse delle nuove funzionalità, come ad esempio quella per realizzare degli angoli arrotondati: tra i vari browser quelli che attualmente offrono una buona Anastasia Kokonozi 566/201 Pagina 55 di 117 compatibilità con questo standard sono Firefox (a partire dalla versione 3.5), Google Chrome, Safari (a partire dalla versione 4), Internet Explorer (a partire dalla versione 9) ed Opera (a partire dalla versione 11). Per quanto riguarda, invece, i browser mobile supportano tutti la maggior parte delle funzionalità offerte dai CSS3. Per lo stile dell’interfaccia grafica dell’applicazione si è scelto di realizzare uno stile Web 2.0. Il Web 2.0 non è propriamente uno stile di design, ma un concetto molto più ampio che ha a che vedere, riassumendo, con l’evoluzione di internet. In pratica le interfacce grafiche sono fortemente ispirate dal design e dall’estetica di tutte quelle applicazione online che permettono uno spiccato livello d’interazione sito-utente (Wikipedia, Youtube, Facebook, Myspace, Twitter, etc.). Gli elementi grafici ed i trend più ricorrenti nello stile Web 2.0 sono: • Rotondità e curve morbide: lo stile Web 2.0 richiede un taglio netto con la grafica squadrata tipica dei layout tabellari, preferendo curve morbide, rotondità e angolature soft. • Colori tenui e tonalità pastello: le palette di questo stile tendono a colori pastello, soprattutto nelle tonalità dell’azzurro e del verde. Il trend è abbinare background soft con elementi dai colori accesi, forti e vivaci in contrasto. • Riflessi e ombre: i vari elementi della grafica devono possedere una certa profondità, dimostrare di avere in qualche modo “spessore” nel layout. Per questo si lavora molto sulle ombre, soprattutto dei pulsanti e delle modal box (le finestre in sovrimpressione). • Icone: le icone, inserite nei posti giusti, calamitano l’attenzione dell’utente e rendono i concetti universalmente chiari in modo immediato ed efficace, favorendo la leggibilità dei contenuti testuali. A livello di CSS seguire questo stile significa anche produrre un layout Tableless: Tableless deriva dall’inglese e sta per “senza tabelle”, sta ad indicare il modo corretto di creare il codice di un sito e cioè senza tabelle. O meglio, le tabelle Anastasia Kokonozi 566/201 Pagina 56 di 117 dovrebbero essere usate solo per impaginare dati prettamente tabellari, e non per inquadrare il layout di una pagina. La vecchia scuola di webmaster per fare un sito con una grafica decente usava le tabelle. Se notiamo infatti, anche Photoshop o altri programmi di grafica, per impaginare la grafica di un sito, ti consentono tuttora di dividerla in sezioni e salvare una pagina HTML che in realtà è composta da una tabella con le singole sezioni che compongono le celle. Il problema è relativo all’accessibilità delle pagine per utenti disabili in quanto se impaginiamo tutto in una tabella un utente non vedente, ad esempio, avrebbe molta difficoltà ad accedervi in quanto le informazioni contenute in una tabella vengono lette riga per riga e se, per farci stare la grafica, abbiamo diviso il layout in tante celle, allora il nostro utente non vedente non potrà godersi i contenuti della nostra applicazione con un certo ordine. Se invece dividiamo il contenuto in “box” logici (‘div’), tipo testata, menu, pagina, etc, il lettore leggerà direttamente i contenuti con un filo logico ed una certa sequenzialità. Potrà inoltre, tramite alcuni comandi, saltare il menu e passare al contenuto della pagina, e tante altre cose altrimenti non fattibili con le tabelle. Oltre a questo c’è il fatto che suddividendo in box le aree delle pagine si riescono a gestire più cose tramite i CSS ed è possibile, modificando solo il foglio di stile senza toccare il codice delle pagine, creare diversi layout. Parlando di pagine web rivolte a dispositivi mobile c'è da tenere in considerazione il “problema” della risoluzione. I dispositivi mobile, smartphone o tablet, hanno dei display di diverse dimensioni e, di conseguenza, supportano diverse risoluzioni. Applicando ad una pagina HTML un CSS che ne permette la corretta visualizzazione su uno smartphone, ad esempio, con una risoluzione di 400x800 pixel, non si avrà la stessa resa con un tablet, ad esempio, con una risoluzione di 1024x600 pixel. Per risolvere questa problematica, bisogna utilizzare i CSS per realizzare un template seguendo le regole del Responsive Design. La lingua inglese indica genericamente con l’aggettivo “responsive” tutto ciò che “reagisce o risponde rapidamente e in modo appropriato ad uno stimolo”. In italiano l'aggettivo che Anastasia Kokonozi 566/201 Pagina 57 di 117 rende al meglio il termine inglese è “adattivo”. Con Responsive Design indichiamo, quindi, quell’approccio per il quale la progettazione e lo sviluppo di una pagina web dovrebbero adattarsi al comportamento ed all’ambiente dell’utente in base a fattori come le dimensioni dello schermo, la piattaforma e l’orientamento del dispositivo. La pratica consiste in un mix di griglie, layout e immagini flessibili, più un uso attento delle media queries CSS. Già nelle prime specifiche dei CSS era previsto un meccanismo adatto a servire fogli di stile ad hoc a seconda del dispositivo di visualizzazione. È quello basato sull’utilizzo dell’attributo media e dei diversi tipi di media. Nei CSS3 il meccanismo di base è stato notevolmente migliorato e reso più efficace, fornendo agli sviluppatori uno strumento molto potente per servire stili specifici non solo in base ai dispositivi di fruizione ma anche a diverse caratteristiche degli stessi. Si entra così nell’epoca delle media queries. Una media query consiste nella dichiarazione di un tipo di media e di zero o più espressioni che verifichino le condizioni di validità o non validità delle caratteristiche di un certo media. Vediamo alcuni esempi: @media only screen and (min-device-width : 320px) { } In questo modo gli stili CSS definiti vengono applicati solo a quei dispositivi che hanno una larghezza minima (width) di 320 px. @media only screen and (min‐device‐width : 320px) and (max‐device‐width : 480px) { } In questo modo, invece, gli stili CSS definiti vengono applicati solo a quei dispositivi che hanno una larghezza (width) di risoluzione, compresa tra i 320px ed i Anastasia Kokonozi 566/201 Pagina 58 di 117 480px (in questa fascia rientrano la maggior parte degli smartphone attualmente in circolazione). E' importante, infine, oltre ad utilizzare le media queries, evitare di dare ai vari elementi che compongono la pagina delle misure assolute. Ad esempio per assegnare ad un testo la dimensione del font, bisognerebbe evitare la misura assoluta (12px) ma utilizzare la misura in percentuale (1em) che calcola la misura effettiva basandosi sulla dimensione effettiva della pagina. 4.3.4 JavaScript – jQuery Mobile JavaScript è un linguaggio di scripting comunemente usato nelle applicazioni web, con una sintassi molto vicina a quella del linguaggio Java. Per lo sviluppo dell'applicazione è stato utilizzato un framework, jQuery Mobile, basato sul framework JQuery. jQuery è un framework JavaScript, ovvero una libreria di funzioni codificate in JavaScript, che si pone come obiettivo quello di astrarre ad un livello più alto la programmazione lato client del comportamento di ogni singola pagina. Tramite l’uso di questo framework è possibile, con poche righe di codice, effettuare svariate operazioni come, ad esempio, ottenere le dimensioni di un elemento oppure farlo apparire/scomparire con effetto dissolvenza. Anche la gestione degli eventi è completamente standardizzata e gestita automaticamente assieme alla loro propagazione. Fornisce metodi e funzioni per gestire al meglio aspetti grafici e funzionali della pagina, manipolare il DOM (Document Object Model), estendere il framework mediante plugin e quant’altro ancora, mantenendo la compatibilità tra browser diversi. Basato su jQuery ed ispirato al progetto UI, jQuery Mobile è un progetto molto giovane, ma offre una buona stabilità ed un numero di funzionalità adeguate per sviluppare applicazioni web mobile complete. Uno dei vantaggi dello sviluppo web mobile è quello di poter realizzare un’unica applicazione fruibile con un largo Anastasia Kokonozi 566/201 Pagina 59 di 117 numero di dispositivi. Pur mirando a questo scenario, gli sviluppatori di jQuery Mobile hanno ben presente che offrire la stessa esperienza utente e le stesse funzionalità al mondo dei dispositivi mobile è un’impresa pressoché impossibile, principalmente per le peculiarità hardware di ogni dispositivo (dimensioni dello schermo, potenza del processore). Proprio per questo hanno scelto un approccio nel quale i dispositivi vengono raggruppati in gruppi con diversi livelli di supporto. In questo modo i device più avanzati friuranno di un’esperienza ed un’interfaccia più ricca di quelli più obsoleti, tuttavia ambedue potranno comunque fruire dei contenuti e delle funzionalità offerte dall’applicazione. Sul sito ufficiale di jQuery Mobile è disponibile una griglia, aggiornata all’ultima versione della libreria, nella quale verificare il tipo di supporto offerto. LIVELLO A – Supporto completo • Apple iOS 3.2*-6.1 - Tested on the original iPad (4.3 / 5.0), iPad 2 (4.3 / 5.1 / 6.1), iPad 3 (5.1 / 6.0), iPad Mini (6.1), 3GS (4.3), 4 (4.3 / 5.1), and 4S (5.1 / 6.0), and 5 (6.0) • Android 2.1-2.3 – Tested on the HTC Incredible (2.2), original Droid (2.2), HTC Aria (2.1), Google Nexus S (2.3). Functional on 1.5 & 1.6 but performance may be sluggish, tested on Google G1 (1.5) • Android 3.2 (Honeycomb) – Tested on the Samsung Galaxy Tab 10.1 and Motorola XOOM • Android 4.0 (ICS) – Tested on a Galaxy Nexus. Note: transition performance can be poor on upgraded devices • Android 4.1 (Jelly Bean) – Tested on a Galaxy Nexus and Galaxy 7 • Windows Phone 7.5-7.8 – Tested on the HTC Surround (7.5), HTC Trophy (7.5), LG-E900 (7.5), Nokia Lumia 800 (7.8) • Blackberry 6-10 – Tested on the Torch 9800 (6) and Style 9670 (6), BlackBerry® Torch 9810 (7), BlackBerry Z10 (10) • Blackberry Playbook (1.0-2.0) – Tested on PlayBook Anastasia Kokonozi 566/201 Pagina 60 di 117 • Palm WebOS (1.4-3.0) – Tested on the Palm Pixi (1.4), Pre (1.4), Pre 2 (2.0), HP TouchPad(3.0) • Firefox Mobile 18 – Tested on Android 2.3 and 4.1 devices • Chrome for Android 18 – Tested on Android 4.0 and 4.1 devices • Skyfire 4.1 - Tested on Android 2.3 device • Opera Mobile 11.5-12: Tested on Android 2.3 • Meego 1.2 – Tested on Nokia 950 and N9 • Tizen (pre-release) – Tested on early hardware • Samsung Bada 2.0 – Tested on a Samsung Wave 3, Dolphin browser • UC Browser – Tested on Android 2.3 device • Kindle 3, Fire, and Fire HD - Tested on the built-in WebKit browser for each • Nook Color 1.4.1 – Tested on original Nook Color, not Nook Tablet • Chrome Desktop 16-24 - Tested on OS X 10.7 and Windows 7 • Safari Desktop 5-6 - Tested on OS X 10.8 • Firefox Desktop 10-18 – Tested on OS X 10.7 and Windows 7 • Internet Explorer 8-10 – Tested on Windows XP, Vista and 7, Windows Surface RT • Opera Desktop 10-12 - Tested on OS X 10.7 and Windows 7 LIVELLO B – Supporto completo ad eccezione delle chiamate AJAX • Blackberry 5.0*: Tested on the Storm 2 9550, Bold 9770 • Opera Mini 7 - Tested on iOS 6.1 and Android 4.1 • Nokia Symbian^3 - Tested on Nokia N8 (Symbian^3), C7 (Symbian^3), also works on N97 (Symbian^1) • Internet Explorer 7 – Tested on Windows XP LIVELLO C – Supporto di base • Internet Explorer 6 and older – Tested on Windows XP • iOS 3.x and older – Tested on original iPhone (3.1), iPhone 3 (3.2) Anastasia Kokonozi 566/201 Pagina 61 di 117 • Blackberry 4.x - Tested on the Curve 8330 • Windows Mobile - Tested on the HTC Leo (WinMo 5.2) • All older smartphone platforms and featurephones – Any device that doesn’t support media queries will receive the basic, C grade experience La caratteristica peculiare di jQuery Mobile è che per realizzare un’applicazione base, basta scrivere del codice HTML5. La libreria, infatti, fa leva sulla struttura semantica delle pagine HTML5 e sugli attributi “data-” per definire le varie parti dell’interfaccia. Una volta caricata la pagina, jQuery Mobile utilizzerà questa struttura per arricchirla con altri tag e agganciare gli eventi e le interazioni ai componenti dell’applicazione. La prima porzione di codice interessante è quella relativa al meta tag viewport, che imposta alcune caratteristiche di default per la visualizzazione della pagina. In questo caso la larghezza del documento deve coincidere con quella dello schermo e lo zoom iniziale sarà uguale al 100%. <meta name="viewport" content="width=device-width, initial-scale=1" /> In jQuery Mobile la navigazione fra le pagine viene realizzata con semplici link, come faremmo in un tradizionale sito web: <a href="rubrica.html" title=”rubrica”>Rubrica telefonica</a> La differenza sostanziale sta nel fatto che il contenuto collegato verrà caricato con una chiamata asincrona ed accodato al documento. In questo modo la transizione fra una pagina e l’altra sarà più fluida. Quando avviene una transazione verso una nuova pagina, effettuiamo una chiamata Ajax verso il server per recuperare le informazioni necessarie a comporre quella pagina. Mentre viene Anastasia Kokonozi 566/201 Pagina 62 di 117 effettuata la chiamata Ajax l'utente viene notificato con un loader che indica il caricamento in corso. Al termine della chiamata Ajax il loader scompare lasciando il posto alla pagina composta dai dati ottenuti dal server. Tutto questo grazie ad un metodo messo a disposizione dal framework. Questo è il metodo da invocare prima della chiamata Ajax per mostrare il loader: $.mobile.showPageLoadingMsg(); e questo, invece, è il metodo che viene invocato al termine della chiamata Ajax per nascondere il Loader: $.mobile.hidePageLoadingMsg(); Tutti i dati, quindi, sono gestiti tramite chiamate Ajax alle API, passando ad ogni chiamata la URL del metodo invocato ed i parametri richiesti. La risposta delle API è in formato JSON, molto semplice da elaborare con JavaScript. Vediamo un'esempio di chiamata Ajax: function caricaContatto(uid) { $.mobile.showPageLoadingMsg(); $.ajax({ type: "POST", url: urls['cercaUtente'], data: 'uid=' + uid, dataType: "json", success: function(data) { }, error: function(richiesta, stato, errore) { }, complete: function() { $.mobile.hidePageLoadingMsg(); } }) } Questo metodo, ad esempio, viene utilizzato per cercare le informazioni di un Anastasia Kokonozi 566/201 Pagina 63 di 117 contatto, conoscendo il suo uid (user id, username). Prima di effettuare la chiamata Ajax viene invocato il metodo che mostra il loader (caricamento in corso). Questi i parametri passati: • type: definisce il passaggio dei parametri, può essere GET oppure POST • url: contiene l'indirizzo del metodo delle API da invocare • data: contiene i parametri passati in input al metodo delle API • dataType: definisce il tipo di risposta che ci si aspetta, HTML o JSON In corrispondenza degli eventi associati ad una chiamata Ajax, invece, possiamo definire delle funzioni per effettuare delle operazioni in corrispondenza di tali eventi, che sono: • success: funzione da lanciare se la richiesta ha successo. Accetta come argomento i dati restituiti dal server • error: funzione lanciata in caso di errore. Accetta un riferimento alla chiamata XMLHttpRequest, il suo stato e l'errore notificato • complete: funzione lanciata al completamento della richiesta, indipendentemente dal suo esito In caso di successo di una chiamata, generalmente, vengono utilizzati i dati restituiti dal server per costruire la pagina (con le informazioni del contatto cercato, ad esempio). In caso di errore di una chiamata, generalmente, viene notificato l'errore all'utente con una popup dove gli viene offerta la possibilità di riprovare a ripetere l'operazione oppure annullarla tornando sulla pagina precedente. Quando viene completata la richiesta, invece, indipendentemente dall'esito della stessa viene invocato il metodo del framework che si occupa della chiusura del loader, per far posto alla visualizzazione della pagina (in caso di successo della Anastasia Kokonozi 566/201 Pagina 64 di 117 richiesta) oppure della popup di errore (in caso di errore nella richiesta, ad es: problemi di connessione, oppure problemi di raggiungibilità del server, etc). Infine, per completezza, va detto che jQuery Mobile, pur essendo un framework JavaScript, non si occupa solo di questo, ma anche dell'HTML e dei CSS. Dell'HTML perchè nelle pagine possiamo usare dei tag sugli elementi della pagina che vengono “intercettati” dal framework che riesce ad associarli un determinato significato. Vediamo qualche esempio. Se ad un div associamo il tag data-role=“page” il framework interpreta tutto il contenuto di quel div come una pagina dell'applicazione. Se invece associamo il tag data-role=“header” oppure data-role=“footer” il framework riconosce che si tratta, rispettivamente, dell'header e del footer della pagina e li posiziona automaticamente nella parte alta (l'header) e nella parte bassa (il footer) della pagina. Per quanto riguarda, invece, i CSS jQuery Mobile offre dei CSS con una struttura responsive. Questo significa che tutti gli elementi di una pagina, come ad esempio gli input, le select o gli altri elementi di un form, vengono automaticamente stilizzati dal framework con le opportune dimensioni in base alla risoluzione supportata dal dispositivo utilizzato. In questo modo, assieme con le media queries, riusciamo ad ottenere un template completamente responsive, in grado di adattarsi da solo alle dimensioni del dispositivo di visualizzazione. 4.3.5 JavaScript – PhoneGap Come abbiamo visto PhoneGap oltre a fungere da “contenitore” convertendo la web app in HTML + JavaScript in codice nativo, mette anche a disposizione delle API JavaScript grazie alle quali è possibile accedere alle funzionalità native del telefono, come l'accesso alla fotocamera, al GPS, alla rubrica, etc. Anastasia Kokonozi 566/201 Pagina 65 di 117 Per lo sviluppo di quest'applicazione non è stata necessaria una particolare interazione con le funzionalità native del telefono. Nello specifico ne sono state usate soltanto alcune, tra quelle disponibili: • verifica della connessione • creazione di alert con l'interfaccia nativa • accesso al localStorage • gestione del pulsante per tornare indietro All'avvio dell'applicazione, durante il caricamento della prima pagina, come prima cosa viene verificata la connessione. Per fare questo controllo basta controllare il valore di questa variabile: navigator.connection.type che può assumere i seguenti valori: • WiFi: se il dispositivo è connesso alla rete tramite la rete Wireless • 2G/3G: se il dispositivo è connesso alla rete tramite la rete mobile della sim inserita nel dispositivo • null: se il dispositivo non è connesso alla rete Dal momento che l'applicazione per funzionare interroga un server dal quale recupera i dati, in assenza di connessione di rete, viene mostrato un alert che invita l'utente a riprovare o ad uscire dall'applicazione. Per la creazione di alert con l'interfaccia nativa, invece, è stato utilizzato il metodo Notification. Un'alert con l'interfaccia nativa è un alert che presenta un titolo, un testo ed uno o più pulsanti con l'interfaccia grafica nativa di Androd. Anastasia Kokonozi 566/201 Pagina 66 di 117 Questa è la sintassi del metodo utilizzato: navigator.notification.alert(message, callback, title, buttonName) e questi i parametri richiesti: • message: il messaggio da mostrare nell'alert • callback: la funzione che viene chiamata alla pressione del pulsante • title: titolo della finestra di alert • buttonName: etichetta del pulsante mostrato nell'alert Il localStorage è una delle novità introdotte dall'HTML5 e si tratta di una versione avanzata dei cookies. Si tratta, quindi, di una sorta di cache nella quale possiamo memorizzare delle informazioni che restano permanenti anche dopo la chiusura del browser. Dal momento che nel nostro caso la pagina HTML viene caricata all'interno di un'applicazione e non di un browser, per accedere alle informazioni del localStorage bisogna utilizzare i metodi messi a disposizione dalle API di PhoneGap per questo scopo, che sono: • getItem(key): ritorna l'oggetto identificato tramite la sua chiave • setItem(key, value): salva il dato passato associandolo alla chiave passata • removeItem(key): rimuove l'oggetto identificato tramite la sua chiave • clear: cancella tutti i dati memorizzati nel localStorage Queste funzionalità sono state utilizzate nell'applicazione per memorizzare i dati della cronologia e dei preferiti. In questo modo queste informazioni restano disponibili anche dopo la chiusura ed il riavvio dell'applicazione. Anastasia Kokonozi 566/201 Pagina 67 di 117 Vediamone un esempio di utilizzo: var storage = window.localStorage; // memorizziamo lo user id del docente storage.setItem(“cronologia”, “guidrus”); // recuperiamo gli utenti presenti nella cronologia var utenti = storage.getItem(“cronologia”); // svuotiamo la cronologia storage.removeItem(“cronologia”); Ovviamente per inserire nella cronologia più contatti, va memorizzato un'array con gli user id di tutti i contatti che deve essere di volta in volta aggiornato. Infine è stato impostato un listener che resta in ascolto attendendo la pressione del pulsante per tornare indietro. Dal momento che in tutte le pagine dell'applicazioni sono state inserite nell'header le icone per tornare alla pagina precedente, si è scelto di disattivare questo pulsante per la navigazione all'interno dell'applicazione. Se, invece, si è sulla pagina principale, la pressione del tasto fa mostrare un alert in cui viene chiesto all'utente se vuole veramente chiudere l'applicazione e, alla pressione del tasto Ok, viene chiusa l'applicazione. document.addEventListener("backbutton", manageBackButton); “backbutton” è l'evento inoltrato dal framework alla pressione del tasto, mentre “manageBackButton” è la funzione che controlla su quale pagina siamo e se siamo su una pagina diversa dalla principale, non fa nulla. Anastasia Kokonozi 566/201 Pagina 68 di 117 CAPITOLO V La sperimentazione Vediamo ora il funzionamento dell'app realizzata. Lanciando l'app ci si trova nella schermata principale dalla quale è possibile: 1. Effettuare la ricerca di un contatto 2. Effettuare la login al sistema Premendo sul pulsante “Rubrica telefonica” accediamo alla funzionalità di ricerca di un contatto. La ricerca può essere effettuata inserendo uno o più parametri Anastasia Kokonozi 566/201 Pagina 69 di 117 come la struttura, il cognome, il nome ed il numero. Per ogni campo è presente la funzione di autocompletamento che, a partire dalla scrittura del terzo carattere, suggerisce dei possibili valori contenti la stringa cercata. Selezioniamo i parametri su cui effettuare la ricerca e premiamo il pulsante “Cerca”. Mentre viene effettuata la chiamata Ajax al server per il recupero dei dati, viene mostrata una schermata che indica il “caricamento in corso”. Questa schermata si chiude alla ricezione dei dati per mostrare la lista dei contatti trovati. In caso di problemi di connessione di rete viene mostrato un alert all'utente offrendo la possibilità di riprovare l'operazione oppure annullarla tornando alla pagina precedente. Nella lista mostrata, contente i dati dei contatti che corrispondono al criterio di ricerca inserito, sono disponibili le principali informazioni di ogni contatto, quali il Anastasia Kokonozi 566/201 Pagina 70 di 117 nominativo, l'indirizzo email, la struttura di afferenza ed il numero di telefono. Premendo sul nome di un contatto viene, invece, caricata la pagina di dettaglio del contatto. Anastasia Kokonozi 566/201 Pagina 71 di 117 Dalla pagina di dettaglio del contatto è possibile effettuare alcune operazioni. Premendo il pulsante “Invia Email” verrà automaticamente lanciata l'app di sistema per l'invio delle mail. La nuova email creata avrà come destinatario l'indirizzo email del contatto visualizzato. Premendo, invece, sul pulsante “Chiama il Contatto” viene mostrata in sovrimpressione una finestra dalla quale è possibile scegliere se chiamare il contatto utilizzando la “normale” linea telefonica (in questo caso viene lanciata l'app di sistema per la composizione delle chiamate, con il campo del numero di telefono precompilato) oppure se effettuare la chiamata utilizzando il servizio di Skype (in questo caso viene lanciata l'app di Skype e, dopo un messaggio di conferma, viene inoltrata la telefonata). E' possibile aggiungere il contatto ai preferiti premendo il pulsante “Aggiungi ai preferiti”. Infine premendo il pulsante “Visualizza Mappa” viene mostrata una nuova schermata con la mappa centrata sull'indirizzo associato al contatto. Anastasia Kokonozi 566/201 Pagina 72 di 117 Se il contatto visualizzato è presente nei preferiti, dal dettaglio del contatto è possibile rimuovere il singolo preferito premendo il pulsante “Rimuovi dai Preferiti”. La pagina dei preferiti, accessibile tramite il pulsante “Preferiti” nel footer della pagina, mostra la lista dei contatti aggiunti ai preferiti. Per ogni contatto è sempre possibile accedere al dettaglio, come se fosse stato ricercato. E' possibile inoltre cancellare tutti i preferiti premendo l'icona in alto a destra con il simbolo del cestino. La cancellazione effettiva di tutti i preferiti è preceduta da un alert in cui viene chiesta conferma all'utente sull'operazione da eseguire. C'è, infine, la pagina della cronologia molto simile per veste grafica e funzionalità a quella dei preferiti, si differenzia da questa solo per la modalità di inserimento dei contatti. Un contatto, infatti, viene aggiunto nella cronologia nel Anastasia Kokonozi 566/201 Pagina 73 di 117 momento stesso in cui viene visualizzato il suo dettaglio. Oltre alla funzionalità di ricerca di un contatto è disponibile anche la “login”. Si tratta per ora solo di una maschera che verifica la correttezza dei dati inseriti per permettere l'accesso ad un'area riservata. L'implementazione dell'area riservata fa parte dei possibili sviluppi futuri. In prospettiva di un'eventuale futuro porting su altre piattaforme, sono stati inseriti in tutte le pagine delle icone nell'header che permettono di tornare nel menù principale ed alla pagina principale. Ciò è stato fatto tenendo in considerazione il fatto che non tutti i dispositivi (ad esempio quelli con sistema operativo iOS) hanno disposizione il tasto per tornare indietro (back button). Per incoraggiare l'utilizzo di tale sistema di navigazione, il tasto back button (anche se disponibile su Android) è stato disabilitato per tutte le pagine diverse dal menù principale. Anastasia Kokonozi 566/201 Pagina 74 di 117 Premendo questo tasto, infatti, dal menù principale viene offerta all'utente la possibilità di uscire dell'app, terminandola e liberando la memoria (possibilità offerta dal sistema operativo Android, ma non disponibile su tutti i sistemi operativi mobile). Anastasia Kokonozi 566/201 Pagina 75 di 117 Conclusioni Concludendo, i requisiti funzionali richiesti per lo sviluppo dell'applicativo sono stati completamente soddisfatti. L'approccio scelto non è stato quello standard dello sviluppo nativo con Java, ma c'è stato un approccio nuovo che permetterà di proseguire questo lavoro sotto diversi aspetti. Il lavoro svolto può, innanzitutto, essere portato su altre piattaforme preoccupandosi quasi esclusivamente dell'installazione e configurazione dei vari ambienti di sviluppo. Inoltre potrebbe diventare un tassello di un puzzle più ampio. Ovvero la rubrica telefonica potrebbe essere considerata come una delle funzionalità da inserire in un'eventuale applicazione ufficiale dell'Università degli Studi di Napoli Federico II. Con la struttura delle API creata sarà possibile modificare la base dati utilizzata, passando a quella di produzione (che è in fase di realizzazione e dovrebbe trattarsi di un WebService SOAP) senza bisogno di modificare l'applicazione. Sarebbe interessante, infine, valutare la reale possibilità/utilità di servizi evoluti da inserire nella rubrica, quali le chiamate tramite un sistema voip, la chat o altro. In definitiva ritengo che PhoneGap sia un ottimo framework per lo sviluppo di applicazioni mobile multipiattaforma perché, oltre a garantire la creazione di applicazioni native, è un prodotto in rapida evoluzione ed un ottimo supporto online per i programmatori. Tutto ciò lascia intendere un incessante investimento lavorativo rivolto a questo prodotto che, in questo modo, risulta qualitativamente valido. Anastasia Kokonozi 566/201 Pagina 76 di 117 APPENDICE A. Installazione dell'applicazione Dal momento che l'applicazione non è stata rilasciata ufficialmente su Google Play, il market di Android, va seguita una determinata procedura per l'installazione dell'applicazione su smartphone o tablet. Come prima cosa bisogna andare nelle impostazioni del dispositivo e, sotto il menù “Applicazioni” spuntare la voce “Sorgenti sconosciute”. Quest'impostazione Anastasia Kokonozi 566/201 Pagina 77 di 117 consente l'installazione di applicazione non ufficiali, quindi non pubblicate sul Market di Android. Per motivi di sicurezza normalmente è disattivata. Dopo bisogna aprire il browser del telefono e puntare la URL dalla quale scaricare l'applicazione: http://SERVER/rubrica.apk In questo modo l'applicazione viene scaricata sul dispositivo. Basta, quindi, aprire il gestore di file di sistema, entrare nella directory dov'è stata scaricata l'applicazione (download in questo caso) e cliccare sul nome del file. Partirà l'installazione dell'applicazione, al termine della quale sarà possibile lanciare l'applicazione dal menù delle applicazioni alla voce Rubrica Unina. L'applicazione è disponibile per le versioni di Android a partire dalla 2.1 Anastasia Kokonozi 566/201 Pagina 78 di 117 B. Porting dell'applicazione Per portare l'applicazione sulle altre piattaforme, tutto ciò che serve è l'installazione/configurazione dell'ambiente di sviluppo della piattaforma scelta. Per tutte le piattaforme è disponibile, come per Android, uno script per la creazione di un nuovo progetto (sempre sia per Windows, che per Mac OS X, che per Linux). In base alla piattaforma scelta sarà di seguito riportato l'ambiente di sviluppo necessario: • Bada: Bada SDK, IDE Eclipse, Eclipse Plugin • BlackBerry: BlackBerry Java SDK, IDE Eclipse, Eclipse Plugin • iOS: iOS SDK, IDE Xcode, su sistema operativo Mac OS X • Windows Phone: Windows Phone SDK, IDE Visual Studio Express, su sistema operativo Windows Sui siti dei rispettivi produttori sono riportate tutte le istruzioni per l'installazione e configurazione dell'SDK e dell'IDE. Una volta configurato l'ambiente di sviluppo si può usare lo script per la creazione di un nuovo progetto, esattamente come fatto per la versione Android. Al termine va lanciato l'IDE di riferimento e creato un nuovo progetto partendo dalla directory creata dallo script. A questo punto basta sostituire la cartella assets/www con quella dell'applicazione e testarne il corretto funzionamento. In base alla piattaforma su cui si sta sviluppando, le API di PhoneGap possono avere delle lievi differenze, come ampiamente documentato sul sito di PhoneGap, e quindi bisognerà effettuare dei test ed eventualmente apportare alcune modifiche per ripristinare il corretto funzionamento dell'applicazione. Inoltre il sito di PhoneGap offre un ulteriore strumento per la compilazione, denominato PhoneGap Build. Si tratta di un prodotto a pagamento che, tramite un'interfaccia web, permette di caricare l'applicazione realizzata con PhoneGap per una piattaforma e provvede alla conversione del codice per tutte le altre piattaforme Anastasia Kokonozi 566/201 Pagina 79 di 117 senza bisogno, così, di installare e configurare vari ambienti di sviluppo. Anastasia Kokonozi 566/201 Pagina 80 di 117 C. Testare l'applicazione con l'emulatore L’Android SDK include un emulatore di un dispositivo mobile, un dispositivo mobile virtuale che viene eseguito sulla propria macchina. L’emulatore consente di sviluppare e testare applicazioni Android senza l’utilizzo di un dispositivo fisico. L’emulatore di Android simula tutte le funzioni hardware e software di un dispositivo mobile tipico, con la differenza che non può effettuare chiamate telefoniche reali. Esso fornisce una serie di pulsanti di navigazione e di controllo, che si possono “premere” con il mouse o la tastiera per generare eventi per le applicazioni. Esso prevede, inoltre, uno schermo in cui vengono visualizzate le applicazioni Android attive. Per testare le applicazioni più facilmente, l’emulatore utilizza configurazioni Anastasia Kokonozi 566/201 Pagina 81 di 117 Android Virtual Device (AVD). Un Android Virtual Device (AVD) è una configurazione per un emulatore che permette di plasmare un vero e proprio dispositivo attraverso la definizione di opzioni hardware e software per essere emulate dall’emulatore Android. Un AVD è composto da: • un profilo hardware: definisce le caratteristiche hardware del dispositivo virtuale. Ad esempio, è possibile definire la quantità di memoria che ha, se il dispositivo ha una fotocamera, se utilizza una tastiera fisica QWERTY o un pad alfanumerico, etc. etc. • una mappatura di un’immagine del sistema: è possibile definire quale versione della piattaforma Android verrà eseguita sul dispositivo virtuale. È possibile scegliere una versione della piattaforma standard di Android o l’immagine del sistema fornita con un add-on dell’SDK • altre opzioni: è possibile specificare la skin (aspetto grafico) dell’emulatore che si desidera utilizzare con l’AVD, consentendo di controllare le dimensioni dello schermo, l’aspetto, e così via. È inoltre possibile specificare la scheda SD emulata da utilizzare con l’AVD • un’area di memorizzazione ad hoc sulla macchina di sviluppo: i dati utenti del dispositivo (applicazioni installate, impostazioni, e così via) e la scheda SD emulata vengono memorizzate in questa area È possibile creare tanti AVD quanti se ne ha bisogno, in base ai tipi di dispositivi che si desidera plasmare. Per testare a fondo un’applicazione, occorre creare un AVD per ogni configurazione del dispositivo (ad esempio, differenti dimensioni dello schermo e diverse versioni della piattaforma) con la quale l’applicazione è compatibile, e testare l’applicazione su ciascuno di essi. Il modo più semplice per creare un AVD è quello di utilizzare l’interfaccia grafica del AVD Manager. È possibile avviare questo strumento sia da Eclipse cliccando su Window > AVD Manager, sia dalla riga di comando richiamando il tool android con l’opzione avd. In alternativa è possibile creare un AVD dalla riga di comando Anastasia Kokonozi 566/201 Pagina 82 di 117 attraverso l’uso del tool android. Una volta che un’applicazione è in esecuzione sull’emulatore, è possibile utilizzare i servizi della piattaforma Android per richiamare altre applicazioni, accedere alla rete, riprodurre file audio e video, archiviare e recuperare dati, mandare notifiche all’utente, ed eseguire il rendering delle transizioni grafiche e dei temi. L’emulatore include anche una varietà di funzionalità di debug, come una console da cui è possibile registrare l’output del kernel, simulare gli interrupt di un’applicazione (come la ricezione di SMS o di telefonate), e simulare gli effetti di latenza e caduta di segnale sulla rete dati. Le immagini del sistema Android disponibili tramite l’Android SDK Manager contengono il codice per il kernel Linux di Android, le librerie native, la Dalvik VM, e i vari pacchetti Android (come il framework di Android e le applicazioni preinstallate). L’emulatore fornisce la traduzione binaria dinamica del codice macchina del dispositivo al sistema operativo e all’architettura del processore della propria macchina. L’emulatore di Android supporta molte caratteristiche hardware che possono essere presenti sui dispositivi mobili, tra cui: • unità di gestione della memoria (MMU) • display LCD • una o più tastiere (una tastiera Qwerty e i pulsanti del telefono associati) • un chip audio con funzioni di ingresso e di uscita • un modem GSM, tra cui una scheda SIM simulata • partizioni di memoria flash (emulate attraverso i file di immagine del disco sulla propria macchina) • una fotocamera, utilizzando una webcam collegata alla propria macchina • sensori come l’accelerometro, utilizzando i dati da un dispositivo Android collegato tramite USB Anastasia Kokonozi 566/201 Pagina 83 di 117 La seguente tabella elenca le opzioni hardware che si possono impostare al momento della creazione di un nuovo AVD e che vengono memorizzate nel file di configurazione dell’AVD (il file config.ini nella directory locale dell’AVD). Caratteristica Descrizione Device ram size La quantità di RAM fisica sul dispositivo, in megabyte. Il hw.ramSize valore di default è “96”. Touch-screen support Se c’è un touch screen nel dispositivo. Il valore di default hw.touchScreen è “yes”. Trackball support Se c’è un trackball nel dispositivo. Il valore di default hw.trackBall è “yes”. Keyboard support Se il dispositivo ha una tastiera QWERTY. Il valore di default è hw.keyboard “yes”. DPad support Se il dispositivo ha pulsanti DPad. Il valore di default è hw.dPad “yes”. GSM modem support Se c’è un modem GSM nel dispositivo. Il valore di default hw.gsmModem è “yes”. Camera support Se il dispositivo ha una fotocamera. Il valore di default hw.camera è “yes”. Maximum horizontal camera pixels Il valore di default è “640”. hw.camera.maxHorizon tal-Pixels Il valore di default è “480”. hw.camera.maxVertical Pixels Maximum vertical camera pixels Anastasia Kokonozi 566/201 Proprietà Pagina 84 di 117 GPS support Se c’è GPS nel dispositivo. Il hw.gps valore di default è “yes”. Se il dispositivo può registrare Audio recording support audio. Il valore di default è hw.audioInput “yes”. Se il dispositivo può riprodurre Audio playback support audio. Il valore di default è hw.audioOutput “yes”. Battery support Se il dispositivo può essere eseguito su una batteria. Il hw.battery valore di default è “yes”. Accelerometer Se c’è un accelerometro nel dispositivo. Il valore di default hw.accelerometer è “yes”. SD Card support Se il dispositivo supporta l’inserimento/rimozione di hw.sdCard schede SD virtuali. Il valore di default è “yes”. Se si usa una partizione cache Cache partition support sul dispositivo. Il valore di disk.cachePartition default è “yes”. Cache partition size Il valore di default è “66MB”. disk.cachePartition.size Imposta la densità utilizzata Abstracted LCD density dallo schermo dell’AVD. Il hw.lcd.density valore di default è “160”. Sia quando si avvia l’emulatore, che in fase di esecuzione, è possibile utilizzare una varietà di comandi e opzioni per controllare il suo comportamento. Queste opzioni possono essere specificate solo dalla riga di comando attraverso l’uso del comando emulator. Ecco la sintassi del comando: Anastasia Kokonozi 566/201 Pagina 85 di 117 emulator -avd <avd_name> [-<option> [<value>]] [-<qemu args>] La seguente tabella elenca tutti i comandi emulator e spiega il loro significato e utilizzo. Categoria Comando -avd <avd_name> o AVD @<avd_name> Descrizione Obbligatorio. Specifica l’AVD da caricare per questa istanza dell’emulatore. È necessario creare una configurazione AVD prima di lanciare l’emulatore. -cache <filepath> Utilizza <filepath> come immagine della partizione di cache di lavoro. Un percorso assoluto o relativo alla directory di lavoro corrente. Se non è specificato nessun file di cache, il comportamento predefinito dell’emulatore è quello di utilizzare un file temporaneo. -data <filepath> Utilizza <filepath> come immagine del disco dei dati utente di lavoro. Opzionalmente, è possibile specificare un percorso relativo alla directory di lavoro corrente. Se -data non viene utilizzato, l’emulatore cerca un file chiamato userdata-qemu.img nella zona di memoria dell’AVD utilizzato. Disk Image Quando si azzera l’immagine dei dati utente (attraverso -wipe-data), copia il contenuto di questo file nella nuova immagine del disco dei dati utente. Di -initdata <filepath> default, l’emulatore copia <system>/userdata.img. Opzionalmente, è possibile specificare un percorso relativo alla directory di lavoro corrente. -wipe-data Anastasia Kokonozi 566/201 Ripristina l’immagine del disco corrente Pagina 86 di 117 dei dati utenti (cioè il file specificato da -datadir e -data, o il file di default). L’emulatore cancella tutti i dati dal file immagine dei dati utente, quindi copia il contenuto del file del dato -inidata nel file dell’immagine prima dei iniziare. -nocache Avvia l’emulatore senza una partizione di cache. -ramdisk <filepath> Utilizza <filepath> come immagine della ramdisk. Il valore di default è <system>/ramdisk.img. Opzionalmente, è possibile specificare un percorso relativo alla directory di lavoro corrente. -sdcard <filepath> Utilizza <filepath> come immagine della scheda SD. Il valore di default è <system>/sdcard.img. Opzionalmente, è possibile specificare un percorso relativo alla directory di lavoro corrente. -verbose Abilita un output dettagliato. Equivalente a -debug-init. È possibile definire le opzioni di output predefinite, utilizzate dalle istanze dell’emulatore, nella variabile di ambiente Android ANDROID_VERBOSE. Per fare ciò, si definiscono le opzioni che si voglio-no utilizzare in un elenco separato da virgole, specificando solo la radice di ogni opzione: -debug-<tags>. Ecco un esempio che mostra ANDROID_ VERBOSE definita con le opzioni -debug-init e -debug-modem: ANDROID_VERBOSE=init,modem Debug -debug <tags> Anastasia Kokonozi 566/201 Abilita/disabilita i messaggi di debug per il tag di debug specificato. <tags> è un elenco separato da spazi/virgole/colonne di nomi di componenti di debug. Pagina 87 di 117 Media -debug-<tag> Abilita/disabilita i messaggi di debug per il tag di debug specificato. -debug-no-<tag> Disattiva i messaggi di debug per il tag di debug specificato. -logcat <logtags> Abilita l’output di logcat con i tag dati. Se la variabile di ambiente ANDROID_ LOG_TAGS è definita e non vuota, il suo valore sarà utilizzato per abilitare l’output di logcat per impostazione di default. -shell Crea una console shell di root sul terminale corrente. È possibile utilizzare anche se il demone adb nel sistema emulato è rotto. Premendo CTRL+C dalla shell, viene fermato l’emulatore invece che la shell. -show-kernel <name> Visualizza i messaggi del kernel. -trace <name> Abilita il profiling del codice (premere F9 per iniziare), scritto in un file specificato. -audio <backend> Utilizza il backend audio specificato. -audio-in <backend> Utilizza il specificato. -audio-out <backend> Utilizza il specificato. -noaudio Disabilita il supporto audio nell’istanza dell’emulatore corrente. -radio <device> Reindirizza l’interfaccia radio modem a un dispositivo host. -useaudio Abilita il supporto audio nell’istanza dell’emulatore corrente. Abilitato per Anastasia Kokonozi 566/201 backend backend audio-input audio-output Pagina 88 di 117 default. Network Utilizza i server DNS specificati. Il valore di <servers> deve essere un -dns-server <servers> elenco separato da virgole di un massimo di 4 nomi di server DNS o indirizzi IP. Effettua tutte le connessioni TCP attraverso un proxy HTTP/HTTPS specifico. Il valore di <proxy> può essere uno dei seguenti: http://<server>:<port> -http-proxy <proxy> http://<username>:<password>@<serve r>:<port> Il prefisso http:// può essere omesso. Se il comando -http-proxy <proxy> non viene fornito, l’emulatore cerca la variabile di ambiente http_proxy e automaticamente utilizza qualsiasi valore corrispondente al formato <proxy> descritto in precedenza. -netdelay <delay> Imposta l’emulazione della latenza di rete a <delay>. Il valore di default è none. -netspeed <speed> Imposta l’emulazione della velocità di rete a <speed>. Il valore di default è full. -netfast Scorcaitoia per -netspeed full -netdelay none -port <port> Anastasia Kokonozi 566/201 Imposta il numero di porta della console per questa istanza dell’emulatore a <port>. Il numero di porta della console deve essere un numero intero pari tra 5554 e 5584, inclusi. <port>+1 deve essere libero e sarà riservato ad ADB. Pagina 89 di 117 -report-console <socket> Sistema -cpu-delay <delay> Riporta la porta della console assegnata per questa istanza dell’emulatore ad una terza parte remota prima di avviare l’emulazione. Rallenta la velocità della CPU emulata di <delay>. I valori supportati per <delay> sono numeri interi tra 0 e 1000. Reindirizza dispositivo. -gps <device> il GPS NMEA al Utilizzare questo commando per emulare un GPS compatibile con NMEA collegato ad un dispositivo esterno o a un socket. Il formato di <device> deve essere una specificazione QEMU del dispositivo seriale. Passa argomenti al software emulatore qemu. -qemu -qemu -enable-kvm Quando si utilizza questa opzione, assicurarsi che sia l’ultima opzione specificata, dal momento che tutte le opzioni successive sono interpretate come opzioni specifiche qemu. Abilita l’accelerazione KVM della macchina virtuale dell’emulatore. Questa opzione è efficace solo quando il sistema è impostato per utilizzare l’accelerazione KVM della macchina virtuale. È possibile specificare una dimensione della memoria (-m <size>) per la macchina virtuale, che deve corrispondere alla dimensione della memoria dell’emulatore: -qemu -m 512 -enable-kvm -qemu -m 1024 -enable-kvm -qemu -h Anastasia Kokonozi 566/201 Visualizza l’help di qemu. Pagina 90 di 117 -nojni Disabilita i controlli JNI nella Dalvik runtime. Attiva l’accelerazione l’emulatore. -gpu on -radio <device> grafica per Questa opzione è disponibile solo per gli emulatori che utilizzano un’immagine di sistema con API Level 15, revision 3 e superiori. Reindirizza la modalità radio al dispositivo specificato. Il format di <device> deve essere una specificazione QEMU del dispositivo seriale. Imposta il fuso orario per il dispositivo emulato a <timezone>, invece che al fuso orario dell’host. <timezone> deve essere specificato nel formato zoneinfo. -timezone <timezone> Per esempio: “America/Los_Angeles” “Europe/Paris” -version Visualizza il numero dell’emulatore. -dpi-device <dpi> Ridimensiona la risoluzione dell’emulatore in modo che corrisponda alle dimensioni dello schermo di un dispositivo fisico. Il valore di default è 165. UI -no-boot-anim Anastasia Kokonozi 566/201 di versione Disabilita l’animazione di boot durante l’avvio dell’emulatore. Disabilitare l’animazione di boot può velocizzare il tempo di avvio per l’emulatore. Pagina 91 di 117 -no-window Disabilita la visualizzazione finestra grafica dell’emulatore. -scale <scale> Ridimensiona la finestra dell’emulatore. <scale> è un numero tra 0.1 e 3 che rappresenta il fattore di scala desiderato. È inoltre possibile specificare la scala come un valore DPI se si aggiunge il suffisso “dpi” al valore della scala. Un valore “auto” dice all’emulatore di selezionare la migliore dimensione della finestra. -raw-keys Disabilita la tastiera Unicode reversemapping. -noskin Non viene utilizzata dell’emulatore. -keyset <file> Utilizza il file keyset specificato invece di quello predefinito. Il file keyset definisce l’elenco delle combinazioni di tasti tra l’emulatore e la tastiera dell’host. -onion <image> Utilizza l’immagine overlay sullo schermo. Nessun supporto per JPEG. Solo PNG è supportato. -onion-alpha <percent> Specifica il valore della translucenza della skin (in percentuale). Il valore di default è 50. -onion-rotation <position> -skin <skinID> -skindir <dir> Anastasia Kokonozi 566/201 alcuna della skin Specifica la rotazione della skin. <position> deve essere uno dei valori 0, 1, 2, 3. Queste opzioni dell’emulatore sono deprecate. Utilizzare gli AVD per impostare le opzioni della skin, piuttosto che utilizzare questa opzione dell’emulatore. L’utilizzo di questa Pagina 92 di 117 opzione può provocare inaspettati e in alcuni casi fuorvianti risultati, poiché la densità con cui si rende la skin può non essere definita. Gli AVD consentono di associare ogni skin con una densità predefinita e non tener conto di quella predefinita se necessario. Help -help Stampa un elenco di tutte le opzioni dell’emulatore. -help-all Stampa l’help per tutte le opzioni di avvio. -help-<option> Stampa l’help per una specifica opzione di avvio. -help-debug-tags Stampa un elenco di tutti i tag per -debug <tags>. -help-disk-images Stampa l’help per l’utilizzo delle immagini del disco dell’emulatore. -help-environment Stampa l’help per le variabili di ambiente dell’emulatore. -help-keys Stampa la mappatura corrente delle tasti. -help-keyset-file Stampa l’help per definire un file di mappa-tura delle chiavi personalizzato. -help-virtual-device Stampa l’help per l’utilizzo dell’Android Virtual Device. Anastasia Kokonozi 566/201 Pagina 93 di 117 D. Pubblicazione dell'applicazione Quando lo sviluppo ed il testing dell'applicazione è completato, possiamo pubblicarla su Google Play Store. E' il nuovo store digitale per dispositivi Android che ha sostituito l’Android Market (su cui erano disponibili solo applicazioni e non aveva un'interfaccia web). Da esso è possibile scaricare ed acquistare non solo applicazioni ma anche giochi, libri, film e musica da usare su smartphone e tablet equipaggiati con il sistema operativo Android. Usare Google Play Store è facile e gratuito, basta avere un account Google ed effettuare l’accesso con quest’ultimo. L’accesso può essere effettuato da qualsiasi dispositivo: non solo telefonini e tablet ma anche PC (in questo caso, gli oggetti scaricati vengono inviati al dispositivo di destinazione non appena questo viene connesso ad Internet). Per procedere alla pubblicazione possiamo seguire la guida che Google ci mette a disposizione. Di cosa abbiamo bisogno? • L'applicazione salvata in un file con estensione .apk di dimensione massima 50 MB • Essere registrati a Google • Un paio di screenshots (almeno) • Icona applicazione ad alta risoluzione Il file apk è un file che contiene tutti i dati dell'applicazione. Senza saperlo, per provare la nostra applicazione, abbiamo sempre inviato al dispositivo (fisico o virtuale) un file apk generato automaticamente che, però, non è firmato digitalmente (o meglio è firmato con chiave di debug). Google Play Store non accetterà mai un file apk senza firma digitale, quindi vediamo come crearlo firmato. Con Eclipse generare il file apk firmato è semplice: cliccare con il tasto destro del mouse Anastasia Kokonozi 566/201 Pagina 94 di 117 sull'applicazione e selezionare “Export”. Nella prossima schermata controllare che nel campo Project ci sia proprio l'applicazione che vogliamo esportare e proseguire. A questo punto deve essere creato un portachiavi dove salvare tutte le firme digitali (chiavi). Mettere la spunta su Create new keystore, scegliere un percorso a piacere nel campo Location, una password di almeno 6 caratteri (inserendola in entrambi i campi) e cliccare su Next. Compilare, infine, i campi della firma digitale inserendo un alias, una password, la validità della chiave (minimo 25 anni), nome e cognome. Finita questa fase, scegliere il percorso dove vogliamo che il file apk venga generato e cliccare su Finish. Anastasia Kokonozi 566/201 Pagina 95 di 117 Il file generato è già in grado di essere installato su qualsiasi dispositivo anche senza essere inviato su Google Play Store. Volendo inviarlo la procedura da seguire è necessario, innanzitutto, acquistare la licenza per sviluppatori Android (il costo attuale è di 25$ una tantum). Nel momento in cui viene effettuato il caricamento dell'applicazione tramite l'account sviluppatore registrato, è necessario specificare una serie di impostazioni: • verificare che il funzionamento dell'applicazione rispetti le linee guida di Google • verificare che l'applicazione non superi la dimensione massima consentita (50MB) • definire la distribuzione (paese, versioni di android supportate, risoluzioni supportate, etc) • definire la tipologia di applicazione, gratuita o a pagamento, e nel caso venga pubblicata a pagamento impostare il prezzo di vendita • caricare il materiale promozionale (descrizione, screenshots, video, etc) A questo punto è possibile inviare l'applicazione allo Store. Prima di essere pubblicata, però, sarà soggetta alla revisione da parte del team di Google, che verificherà che siano stati rispettati tutti i presupposti e prerequisiti. Anastasia Kokonozi 566/201 Pagina 96 di 117 E. Codice dell'applicazione index.html (pagina iniziale) <!DOCTYPE HTML> <html> <head> <meta name="viewport" content="width=device-width, initial-scale=1" /> <meta http-equiv="Content-type" content="text/html; charset=utf-8"> <title>Rubrica</title> <!-- INCLUSIONE DEI CSS --> <link rel="stylesheet" href="../css/framework/jquery.mobile.css" type="text/css" media="screen" /> <link rel="stylesheet" href="../css/app/style.css" type="text/css" media="screen" /> <!-- INCLUSIONE DELLE LIBRERIE JAVASCRIPT --> <script type="text/javascript" charset="utf-8" src="../js/framework/cordova.js"></script> <script type="text/javascript" charset="utf-8" src="../js/framework/jquery.min.js"></script> <script type="text/javascript" charset="utf-8" src="../js/framework/jquery.mobile.min.js"></script> <!-- INCLUSIONE DEGLI SCRIPT --> <script type="text/javascript" charset="utf-8" src="../js/app/library.js"></script> <script type="text/javascript" charset="utf-8" src="../js/app/main.js"></script> <script type="text/javascript" charset="utf-8" src="../js/app/login.js"></script> <script type="text/javascript" charset="utf-8" src="../js/app/rubrica.js"></script> <script type="text/javascript" charset="utf-8" src="../js/app/searchResult.js"></script> <script type="text/javascript" charset="utf-8" src="../js/app/contatto.js"></script> <script type="text/javascript" charset="utf-8" src="../js/app/preferiti.js"></script> <script type="text/javascript" charset="utf-8" src="../js/app/cronologia.js"></script> <script type="text/javascript" charset="utf-8" src="../js/app/mappa.js"></script> <script type="text/javascript" charset="utf-8" src="../js/app/chiamata.js"></script> <!-- INCLUSIONE DELLE LIBRERIE DI GOOGLE MAPS --> <script type="text/javascript" charset="utf-8" src="http://maps.google.com/maps/api/js?sensor=true"></script> <!-- INCLUSIONE DEI FILES DI CONFIGURAZIONE --> <script type="text/javascript" charset="utf-8" src="../config/common.js"></script> <script type="text/javascript" charset="utf-8" src="../config/urls.js"></script> <script type="text/javascript" charset="utf-8" src="../config/text.js"></script> <!-- ATTENDERE IL CARICAMENTO DI PHONEGAP --> <script type="text/javascript"> document.addEventListener("deviceready", onDeviceReady, false); </script> </head> <body> <div data-role="page" id="main" data-dom-cache="true"> <div data-role="header" data-position="fixed" data-theme="b" id="headerMain"> <div><h1>Rubrica Unina</h1></div> </div> <div data-role="content" id="contentMain"> <div id="searchButtonsMain"> <a href="#" onclick="showSpinner(); refreshToPage('rubrica');" data-role="button" data-theme="b" data-icon="search" data-iconpos="right">Rubrica telefonica</a> <a href="#" onclick="showSpinner(); refreshToPage('login');" data-role="button" data-theme="b" data-icon="gear" data-iconpos="right">Login</a> </div> Anastasia Kokonozi 566/201 Pagina 97 di 117 <div id="loghiMain"> <div id="logoCsiMain"> <img src="../img/logo_csi.gif" /> </div> <div id="logoUninaMain"> <img src="../img/logo_unina.gif" /> </div> </div> </div> <div data-role="footer" data-position="fixed" data-theme="b" id="footerMain"></div> </div> </body> </html> main.js (javascript associato alla pagina principale) function onDeviceReady() { log("FUNCTION: onDeviceReady()"); init(); } function init() { log("FUNCTION: init()"); $.support.cors = true; $.mobile.allowCrossDomainPages = true; connectionType = checkConnection(); if (connectionType == null || connectionType == "none") { log(error["noConnection"]); notify(error["noConnection"], "Attenzione!", "Riprova", "init", "Esci", "closeApp"); } else { log("Connection Type: " + connectionType); document.addEventListener("backbutton", manageBackButton, false); var initialScreenSize = window.innerHeight; window.addEventListener("resize", function() { if(window.innerHeight < initialScreenSize){ $("[data-role=footer]").hide(); } else { $("[data-role=footer]").show(); } }); } } rubrica.html (pagina della ricerca) <div data-role="page" id="rubrica"> <div data-role="header" data-position="fixed" data-theme="b" id="headerRubrica"> <div id="headerLeftRubrica"> <a href="#" onclick="slideToPage('index');"><img src="../img/icons/home.png" /></a> </div> <div><h1>Rubrica Unina</h1></div> </div> Anastasia Kokonozi 566/201 Pagina 98 di 117 <div data-role="content" id="contentRubrica"> <div data-role="fieldcontain"> Struttura: <p><ul id="struttura" data-role="listview" data-inset="true" datafilter="true" data-filter-placeholder="&nbsp;" data-filter-theme="d"></ul></p> <input id="struttura-value" type="hidden" value="" /> Cognome: <p><ul id="cognome" data-role="listview" data-inset="true" datafilter="true" data-filter-placeholder="&nbsp;" data-filter-theme="d"></ul></p> <input id="cognome-value" type="hidden" value="" /> Nome: <p><ul id="nome" data-role="listview" data-inset="true" data-filter="true" data-filter-placeholder="&nbsp;" data-filter-theme="d"></ul></p> <input id="nome-value" type="hidden" value="" /> Numero: <p><ul id="numero" data-role="listview" data-inset="true" data-filter="true" data-filter-placeholder="&nbsp;" data-filter-theme="d"></ul></p> <input id="numero-value" type="hidden" value="" /> </div> <div id="searchButtonsRubrica"> <a href="#" data-role="button" data-theme="b" data-icon="search" data-inline="true" data-iconpos="right" id="cercaContatto">Cerca</a> </div> </div> <div data-role="footer" data-position="fixed" data-theme="b" id="footerRubrica"> <div data-role="navbar"> <ul> <li><div><a href="#" class="ui-btn-active ui-statepersist">Cerca</a></div></li> <li><div><a href="#" onclick="refreshToPage('preferiti');">Preferiti</a></div></li> <li><div><a href="#" onclick="refreshToPage('cronologia');">Cronologia</a></div></li> </ul> </div> </div> </div> rubrica.js (javascript associato alla pagina della ricerca) var var var var strutturaToSearch = ""; cognomeToSearch = ""; nomeToSearch = ""; numeroToSearch = ""; $(document).delegate("#rubrica", "pagecreate", function() { searchResultToLoad = true; $("#struttura").on("listviewbeforefilter", function (e,data) { autocomplete('struttura', data, urls['cercaStruttura'], '1'); }); $("#cognome").on("listviewbeforefilter", function (e,data) { autocomplete('cognome', data, urls['cercaCognome'], '2'); }); $("#nome").on("listviewbeforefilter", function (e,data) { autocomplete('nome', data, urls['cercaNome'], '3'); }); $("#numero").on("listviewbeforefilter", function (e,data) { autocomplete('numero', data, urls['cercaNumero'], '4'); }); $("#cercaContatto").on("click", function (e,data) { checkInputs(); if (($("#struttura-value").val() == "") && ($("#cognome-value").val() == "") && ($("#nome-value").val() == "") && ($("#numero-value").val() == "")) { Anastasia Kokonozi 566/201 Pagina 99 di 117 notify(error["campiVuoti"], "Attenzione!", "Ok", "doNothing"); } else { strutturaToSearch = $("#struttura-value").val(); cognomeToSearch = $("#cognome-value").val(); nomeToSearch = $("#nome-value").val(); numeroToSearch = $("#numero-value").val(); refreshToPage('searchResult'); } }); }); function checkInputs() { $('input[data-type="search"]').each(function (i) { if ((i+1) == 1) { var val = $("#struttura-value").val(); if (val == "") { $("#struttura-value").val($(this).val()); } } else if ((i+1) == 2) { var val = $("#cognome-value").val(); if (val == "") { $("#cognome-value").val($(this).val()); } } else if ((i+1) == 3) { var val = $("#nome-value").val(); if (val == "") { $("#nome-value").val($(this).val()); } } else if ((i+1) == 4) { var val = $("#numero-value").val(); if (val == "") { $("#numero-value").val($(this).val()); } } }); } function setChoice(fieldName,data,searchId) { $('#' + fieldName).html(""); $('#' + fieldName).listview("refresh"); $('#' + fieldName).trigger("updatelayout"); $('#' + fieldName + '-value').val(data); $('input[data-type="search"]').each(function (i) { if (i == (searchId - 1)) { $(this).val(data); } }); } function autocomplete(fieldName,data,url,searchId) { log("autocomplete('" + fieldName + "', '" + data + "', '" + url + "', '" + searchId + "')"); var $ul = $('#' + fieldName), $input = $(data.input), value = $input.val(), html = ""; $ul.html(""); if (value && value.length > 2) { showSpinner(); $.ajax({ url: url + value, dataType: "json", async: true, timeout: 15000, success: function(data) { if (data.result) { $.each(data.result, function (i,val) { html += "<li class=\"liContatto\" datafieldname=\"" + fieldName + "\" data-searchid=\"" + searchId + "\">" + val + "</li>"; }); $ul.html(html); Anastasia Kokonozi 566/201 Pagina 100 di 117 $ul.listview("refresh"); $ul.trigger("updatelayout"); $(".liContatto").on("click", function (e,data) { setChoice($(this).data("fieldname"), $ (this).html(), $(this).data("searchid")); }); } hideSpinner(); }, error: function() { hideSpinner(); } }); } } searchResult.html (pagina contenente i risultati della ricerca) <div data-role="page" id="searchResult" data-dom-cache="true"> <div data-role="header" data-position="fixed" data-theme="b" id="headerSearchResult"> <div id="headerLeftSearchResult"> <a href="#" onclick="slideToPage('index');"><img src="../img/icons/home.png" /></a> </div> <div><h1>Rubrica Unina</h1></div> <div id="headerRightSearchResult"> <a href="#" onclick="refreshToPage('rubrica');"><img src="../img/icons/backward.png" /></a> </div> </div> <div data-role="content" id="contentSearchResult"> <ul data-role="listview" id="contentSearchResultUL"></ul> <input type="hidden" id="uidRubrica" value="" /> <input type="hidden" id="nominativoRubrica" value="" /> <input type="hidden" id="strutturaRubrica" value="" /> <input type="hidden" id="emailRubrica" value="" /> <input type="hidden" id="telefonoRubrica" value="" /> <div id="noSearchResult" align="center"> Siamo spiacenti, la tua ricerca non ha prodotto risultati. </div> </div> <div data-role="footer" data-position="fixed" data-theme="b" id="footerSearchResult"> <div data-role="navbar"> <ul> <li><div><a href="#" onclick="refreshToPage('rubrica');">Cerca</a></div></li> <li><div><a href="#" onclick="refreshToPage('preferiti');">Preferiti</a></div></li> <li><div><a href="#" onclick="refreshToPage('cronologia');">Cronologia</a></div></li> </ul> </div> </div> </div> Anastasia Kokonozi 566/201 Pagina 101 di 117 searchResult.js (javascript associato alla pagina contenente i risultati della ricerca) $(document).delegate("#searchResult", "pageshow", function(e,data) { if (searchResultToLoad) { caricaContatti(strutturaToSearch,cognomeToSearch,nomeToSearch,numeroToSearch); searchResultToLoad = false; } }); function caricaContatti(struttura,cognome,nome,numero) { log("caricaContatti('" + struttura + "', '" + cognome + "', '" + nome + "', '" + numero + "')"); showLoader(); var params = ""; var i = 0; $.each( { struttura: struttura, cognome: cognome, nome: nome, numero: numero }, function(key, val) { if (val) { i++; if (i > 1) { params = params + "&" + key + "=" + val; } else { params = params + key + "=" + val; } } }); $.ajax({ type: "GET", url: urls['listaUtenti'], data: params, dataType: "json", async: true, success: function(data) { if (data.error) { loadNoSearchResultBox(); hideLoader(); } else { if ($("#contentSearchResultUL").html() != "") { $("#contentSearchResultUL").html(""); } $.each(data.result, function(i, val) { html = '<li class="contacts" data-uid="' + val.uid + '" data-nominativo="' + val.cognome + ' ' + val.nome + '" data-struttura="' + val.dipartimento + '" data-email="' + val.email + '" data-telefono="' + val.telefono + '"><a href="#" onclick="refreshToPage(\'contatto\');"><h3>' + val.cognome + ' ' + val.nome + '</h3><p>' + val.dipartimento + '<br/>EMAIL: ' + val.email + '<br/>TELEFONO: ' + val.telefono + '</p></a></li>'; $("#contentSearchResultUL").append(html); }); $("#contentSearchResultUL").listview("refresh"); $("#contentSearchResultUL").trigger("updatelayout"); $("#searchResult").page({ domCache: true }); $(".contacts").click(function(e) { $("#uidRubrica").val($(this).attr("data-uid")); $("#nominativoRubrica").val($(this).attr("datanominativo")); $("#strutturaRubrica").val($(this).attr("datastruttura")); $("#emailRubrica").val($(this).attr("data-email")); $("#telefonoRubrica").val($(this).attr("data-telefono")); }); hideLoader(); } Anastasia Kokonozi 566/201 Pagina 102 di 117 }, error: function() { hideLoader(); notify(error["network"], "Attenzione!", "Riprova", "ricaricaContatti", "Annulla", "backFromSearchResult"); } }); } function ricaricaContatti() { log("FUNCTION: ricaricaContatti()"); caricaContatti(strutturaToSearch,cognomeToSearch,nomeToSearch,numeroToSearch); } function backFromSearchResult() { refreshToPage('rubrica'); } function loadNoSearchResultBox() { $('#contentSearchResultUL').remove(); $('#uidRubrica').remove(); $('#nominativoRubrica').remove(); $('#strutturaRubrica').remove(); $('#emailRubrica').remove(); $("#telefonoRubrica").remove(); $('#noSearchResult').show(); } contatto.html (pagina con i dettagli del contatto selezionato) <div data-role="page" id="contatto"> <div data-role="header" data-position="fixed" data-theme="b" id="headerContatto"> <div id="headerLeftContatto"> <a href="#" onclick="slideToPage('index');"><img src="../img/icons/home.png" /></a> </div> <div><h1>Rubrica Unina</h1></div> <div id="headerRightContatto"> <a href="#" onclick="backFromContatto()"><img src="../img/icons/backward.png" /></a> </div> </div> <div data-role="content" id="contentContatto" style="display:none"> <div id="contentContattoNominativo"></div> <div id="contentContattoStruttura"></div> <div id="contentContattoEmail"></div> <div id="contentContattoTelefono"></div> <div align="center"> <div id="searchButtonsContatto" data-role="controlgroup"> <a href="#" data-role="button" data-theme="b" id="inviaEmail"><div id="inviaEmailIcon">&nbsp;</div>Invia Email</a> <a href="#" onclick="slideToPage('chiamata');" datarel="dialog" data-transition="none" data-role="button" data-theme="b" id="chiamaContatto"><div id="chiamaContattoIcon">&nbsp;</div>Chiama il Contatto</a> <div id="buttonPreferito"></div> <a href="#" onclick="slideToPage('mappa');" datarole="button" data-theme="b" id="vediMappa"><div id="vediMappaIcon">&nbsp;</div>Visualizza Mappa</a> </div> </div> </div> <div data-role="footer" data-position="fixed" data-theme="b" id="footerContatto"> <div data-role="navbar"> <ul> <li><div><a href="#" Anastasia Kokonozi 566/201 Pagina 103 di 117 onclick="refreshToPage('rubrica');">Cerca</a></div></li> <li><div><a href="#" onclick="refreshToPage('preferiti');">Preferiti</a></div></li> <li><div><a href="#" onclick="refreshToPage('cronologia');">Cronologia</a></div></li> </ul> </div> </div> </div> contatto.js (javascript associato alla pagina con i dettagli del contatto selezionato) var var var var var var var previousPage; ultimoContatto = ""; uid = ""; nominativo = ""; struttura = ""; email = ""; telefono = ""; var dialogNominativo = ""; var dialogNumero = ""; $(document).delegate("#contatto", "pagebeforeshow", function(e,data) { if ((data.prevPage.attr("id") != "mappa") && (data.prevPage.attr("id") != "chiamata")) { previousPage = data.prevPage.attr("id"); } if (data.prevPage.attr("id") == "searchResult") { uid = $("#uidRubrica",data.prevPage).val(); nominativo = $("#nominativoRubrica",data.prevPage).val(); struttura = $("#strutturaRubrica",data.prevPage).val(); email = $("#emailRubrica",data.prevPage).val(); telefono = $("#telefonoRubrica",data.prevPage).val(); aggiungiCronologia(uid, nominativo, struttura, email, telefono); } else if (data.prevPage.attr("id") == "preferiti") { uid = $("#uidPreferiti",data.prevPage).val(); nominativo = $("#nominativoPreferiti",data.prevPage).val(); struttura = $("#strutturaPreferiti",data.prevPage).val(); email = $("#emailPreferiti",data.prevPage).val(); telefono = $("#telefonoPreferiti",data.prevPage).val(); aggiungiCronologia(uid, nominativo, struttura, email, telefono); } else if (data.prevPage.attr("id") == "cronologia") { uid = $("#uidCronologia",data.prevPage).val(); nominativo = $("#nominativoCronologia",data.prevPage).val(); struttura = $("#strutturaCronologia",data.prevPage).val(); email = $("#emailCronologia",data.prevPage).val(); telefono = $("#telefonoCronologia",data.prevPage).val(); } else { uid = ultimoContatto; } }); $(document).delegate("#contatto", "pageshow", function(e,data) { ultimoContatto = uid; caricaContatto(uid); }); function backFromContatto() { log("FUNCTION: backFromContatto()"); if (previousPage == "preferiti") { refreshToPage("preferiti"); } else if (previousPage == "cronologia") { Anastasia Kokonozi 566/201 Pagina 104 di 117 refreshToPage("cronologia"); } else { slideToPage("searchResult"); } } function caricaContatto(uid) { log("FUNCTION: caricaContatto(" + uid + ")"); showLoader(); $.ajax({ type: "GET", url: urls['cercaUtente'], data: 'uid=' + uid, dataType: "json", async: true, success: function(data) { if (data.error) { hideLoader(); } else { dialogNominativo = data.result.cognome + " " + data.result.nome; dialogNumero = data.result.telefono; $('#contentContattoNominativo').html(data.result.cognome + " " + data.result.nome); $('#contentContattoStruttura').html("<b>Struttura: </b>" + data.result.dipartimento); $('#contentContattoEmail').html("<b>Email: </b>" + data.result.email); $('#contentContattoTelefono').html("<b>Telefono: </b>" + data.result.telefono); $('#inviaEmail').attr("href", "mailto:" + data.result.email); if (searchPreferito(data.result.uid)) { $("#buttonPreferito").html('<a href="#" data-uid="' + data.result.uid + '" data-nominativo="' + data.result.cognome + ' ' + data.result.nome + '" data-struttura="' + data.result.dipartimento + '" data-email="' + data.result.email + '" data-telefono="' + data.result.telefono + '" data-role="button" data-theme="b" id="rimuoviPreferito"><div id="rimuoviPreferitoIcon">&nbsp;</div>Rimuovi dai Preferiti</a>'); $("#rimuoviPreferito").click(function() { rimuoviPreferito($(this).attr("data-uid"), $ (this).attr("data-nominativo"), $(this).attr("data-struttura"), $(this).attr("data-email"). $(this).attr("data-telefono")); }); } else { $('#buttonPreferito').html('<a href="#" data-uid="' + data.result.uid + '" data-nominativo="' + data.result.cognome + ' ' + data.result.nome + '" data-struttura="' + data.result.dipartimento + '" data-email="' + data.result.email + '" data-telefono="' + data.result.telefono + '" data-role="button" data-theme="b" id="aggiungiPreferito"><div id="aggiungiPreferitoIcon">&nbsp;</div>Aggiungi ai Preferiti</a>'); $("#aggiungiPreferito").click(function() { aggiungiPreferito($(this).attr("data-uid"), $ (this).attr("data-nominativo"), $(this).attr("data-struttura"), $(this).attr("data-email"), $(this).attr("data-telefono")); }); } $('#contentContatto').trigger("create"); $('#contentContatto').show(); hideLoader(); } }, error: function() { hideLoader(); notify(error["network"], "Attenzione!", "Riprova", "ricaricaContatto", "Annulla", "backFromContatto"); } }) } Anastasia Kokonozi 566/201 Pagina 105 di 117 function ricaricaContatto() { log("FUNCTION: ricaricaContatto()"); caricaContatto(uid); } function searchPreferito(uid) { log("FUNCTION: searchPreferito(" + uid + ")"); var found = false; var retrievedObject = window.localStorage.getItem("preferitiRubrica"); if (retrievedObject) { var preferiti = JSON.parse(retrievedObject); $.each(preferiti, function(key, val) { if (val.uid == uid) { found = true; } }); return found; } else { return found; } } function searchCronologia(uid) { log("FUNCTION: searchCronologia(" + uid + ")"); var found = false; var retrievedObject = window.localStorage.getItem("cronologiaRubrica"); if (retrievedObject) { var preferiti = JSON.parse(retrievedObject); $.each(preferiti, function(key, val) { if (val.uid == uid) { found = true; } }); return found; } else { return found; } } function aggiungiPreferito(uid, nominativo, struttura, email, telefono) { log("FUNCTION: aggiungiPreferito('" + uid + "', '" + nominativo + "', '" + struttura + "', '" + email + "', '" + telefono + "')"); var contatto = {uid: uid, nominativo: nominativo, struttura: struttura, email: email, telefono: telefono}; var retrievedObject = window.localStorage.getItem("preferitiRubrica"); if (retrievedObject) { var preferiti = JSON.parse(retrievedObject); var count = preferiti.length; preferiti[count] = contatto; } else { var preferiti = new Array(); preferiti[0] = contatto; } window.localStorage.setItem("preferitiRubrica", JSON.stringify(preferiti)); notify(text["preferitoAggiunto"], label["preferitoAggiunto"], "Ok", "doNothing"); $("#buttonPreferito").html('<a href="#" data-uid="' + uid + '" data-nominativo="' + nominativo + '" data-struttura="' + struttura + '" data-email="' + email + '" datatelefono="' + telefono + '" data-role="button" data-theme="b" id="rimuoviPreferito"><div id="rimuoviPreferitoIcon">&nbsp;</div>Rimuovi dai Preferiti</a>'); $("#rimuoviPreferito").click(function() { rimuoviPreferito($(this).attr("data-uid"), $(this).attr("data-nominativo"), $(this).attr("data-struttura"), $(this).attr("data-email"), $(this).attr("data-telefono")); Anastasia Kokonozi 566/201 Pagina 106 di 117 }); $('#contentContatto').trigger("create"); } function rimuoviPreferito(uid, nominativo, struttura, email, telefono) { log("FUNCTION: rimuoviPreferito('" + uid + "', '" + nominativo + "', '" + struttura + "', '" + email + "', '" + telefono + "')"); var retrievedObject = window.localStorage.getItem("preferitiRubrica"); var preferiti = JSON.parse(retrievedObject); var tmpPreferiti = new Array(); var count = 0; $.each(preferiti, function(key, val) { if (val.uid != uid) { tmpPreferiti[count] = val; count = count + 1; } }); window.localStorage.removeItem("preferitiRubrica"); window.localStorage.setItem("preferitiRubrica", JSON.stringify(tmpPreferiti)); notify(text["preferitoRimosso"], label["preferitoRimosso"], "Ok", "doNothing"); $('#buttonPreferito').html('<a href="#" data-uid="' + uid + '" data-nominativo="' + nominativo + '" data-struttura="' + struttura + '" data-email="' + email + '" datatelefono="' + telefono + '" data-role="button" data-theme="b" id="aggiungiPreferito"><div id="aggiungiPreferitoIcon">&nbsp;</div>Aggiungi ai Preferiti</a>'); $("#aggiungiPreferito").click(function() { aggiungiPreferito($(this).attr("data-uid"), $(this).attr("data-nominativo"), $(this).attr("data-struttura"), $(this).attr("data-email"), $(this).attr("data-telefono")); }); $('#contentContatto').trigger("create"); } function aggiungiCronologia(uid, nominativo, struttura, email, telefono) { log("FUNCTION: aggiungiCronologia('" + uid + "', '" + nominativo + "', '" + struttura + "', '" + email + "', '" + telefono + "')"); if (searchCronologia(uid)) { return; } var contatto = {uid: uid, nominativo: nominativo, struttura: struttura, email: email, telefono: telefono}; var retrievedObject = window.localStorage.getItem("cronologiaRubrica"); if (retrievedObject) { var cronologia = JSON.parse(retrievedObject); var count = cronologia.length; if (count < 20) { cronologia[count] = contatto; } else { var tmpCronologia = new Array(); for (var i=0; i < 20; i++) { if (i<19) { tmpCronologia[i] = cronologia[i+1]; } else { tmpCronologia[i] = contatto; } } cronologia = tmpCronologia; } } else { var cronologia = new Array(); cronologia[0] = contatto; } window.localStorage.setItem("cronologiaRubrica", JSON.stringify(cronologia)); } Anastasia Kokonozi 566/201 Pagina 107 di 117 chiamata.html (pagina contente la maschera per effettuare una chiamata) <div data-role="dialog" data-close-btn="none" id="chiamata"> <div data-role="header" data-theme="b"> <h1>&nbsp;</h1> </div> <div data-role="content" id="contentChiamata"> <p><div id="contentChiamataNominativo" align="center"></div></p> <p><div id="contentChiamataNumero" align="center"></div></p> <p>&nbsp;</p> <a href="#" id="chiamataTelefono" data-role="button" datatheme="b">Chiama</a> <a href="#" id="chiamataSkype" data-role="button" data-theme="b">Chiama con Skype</a> <p>&nbsp;</p> <a href="#" onclick='$("#chiamata").dialog("close");' data-role="button" data-theme="c">Chiudi</a> </div> </div> chiamata.js (javascript associato alla pagina per effettuare una chiamata) $(document).delegate("#chiamata", "pagebeforeshow", function(e,data) { $("#contentChiamataNominativo").html("<h2>" + dialogNominativo + "</h2>"); $("#contentChiamataNumero").html("<h3>" + dialogNumero + "</h3>"); $("#chiamataTelefono").attr("href", "tel:+39" + dialogNumero); $("#chiamataSkype").attr("href", "skype:+39" + dialogNumero + "?call"); }); mappa.html (pagina contente la mappa associata al contatto) <div data-role="page" id="mappa"> <div data-role="header" data-position="fixed" data-theme="b" id="headerMappa"> <div id="headerLeftMappa"> <a href="#" onclick="slideToPage('index');"><img src="../img/icons/home.png" /></a> </div> <div><h1>Rubrica Unina</h1></div> <div id="headerRightMappa"> <a href="#" onclick="refreshToPage('contatto')"><img src="../img/icons/backward.png" /></a> </div> </div> <div data-role="content" id="contentMappa" style="padding: 0 !important"> <div id="map_canvas"></div> </div> <div data-role="footer" data-position="fixed" data-theme="b" id="footerMappa"> <div data-role="navbar"> <ul> <li><div><a href="#" onclick="refreshToPage('rubrica');">Cerca</a></div></li> <li><div><a href="#" onclick="refreshToPage('preferiti');">Preferiti</a></div></li> <li><div><a href="#" onclick="refreshToPage('cronologia');">Cronologia</a></div></li> </ul> Anastasia Kokonozi 566/201 Pagina 108 di 117 </div> </div> </div> mappa.js (javascript associato alla pagina contente la mappa del contatto) $(document).delegate("#mappa", "pageshow", function(e,data) { showLoader(); caricaMappa(); }); function caricaMappa() { var myLatlng = new google.maps.LatLng(40.83965,14.18851); var myOptions = { zoom: 14, mapTypeControl: false, streetViewControl: false, navigationControl: true, navigationControlOptions: { style: google.maps.NavigationControlStyle.SMALL }, center: myLatlng, mapTypeId: google.maps.MapTypeId.ROADMAP } map = new google.maps.Map(document.getElementById("map_canvas"),myOptions); aggiungiMarker(40.83965,14.18851,'<strong>Polo delle Scienze e Tecnologie<br/>Scienze MM.FF.NN.'); google.maps.event.addListenerOnce(map, 'idle', function(){ hideLoader(); }); } function aggiungiMarker(parallelo, meridiano, html) { var mPos=new google.maps.LatLng(parallelo, meridiano); var marker = new google.maps.Marker({map: map, position: mPos}); var infowindow = new google.maps.InfoWindow(); infowindow.setContent(html); google.maps.event.addListener(marker, 'click', function() { infowindow.open(map, marker); }); } cronologia.html (pagina contente la cronologia di ricerca) <div data-role="page" id="cronologia"> <div data-role="header" data-position="fixed" data-theme="b" id="headerCronologia"> <div id="headerLeftCronologia"> <a href="#" onclick="slideToPage('index');"><img src="../img/icons/home.png" /></a> </div> <div><h1>Rubrica Unina</h1></div> <div id="headerRightCronologia"> <a href="#" onclick="svuotaCronologia();"><img src="../img/icons/cestino.png" /></a> </div> </div> Anastasia Kokonozi 566/201 Pagina 109 di 117 <div data-role="content" id="contentCronologia"> <ul data-role="listview" id="contentCronologiaUL"></ul> <input type="hidden" id="uidCronologia" value="" /> <input type="hidden" id="nominativoCronologia" value="" /> <input type="hidden" id="nominativoCronologia" value="" /> <input type="hidden" id="emailCronologia" value="" /> <div id="noCronologia" align="center"> Non hai ancora cercato nessun contatto </div> </div> <div data-role="footer" data-position="fixed" data-theme="b" id="footerCronologia"> <div data-role="navbar"> <ul> <li><div><a href="#" onclick="refreshToPage('rubrica');">Cerca</a></div></li> <li><div><a href="#" onclick="refreshToPage('preferiti');">Preferiti</a></div></li> <li><div><a href="#" class="ui-btn-active ui-statepersist">Cronologia</a></div></li> </ul> </div> </div> </div> cronologia.js (javascript associato alla pagina contente la cronologia di ricerca) $(document).delegate("#cronologia", "pagebeforeshow", function(e,data) { var cronologia = getCronologia(); if (cronologia != false) { caricaCronologia(cronologia); } else { $('#contentCronologiaUL').remove(); $('#uidCronologia').remove(); $('#noCronologia').show(); } }); function getCronologia() { if (window.localStorage.getItem("cronologiaRubrica")) { return JSON.parse(window.localStorage.getItem("cronologiaRubrica")); } else { return false; } } jQuery.fn.reverse = [].reverse; function caricaCronologia(cronologia) { var html = ""; var count = cronologia.length - 1; cronologia = cronologia.reverse(); $.each(cronologia, function(key, val) { html = html + '<li class="contacts" data-uid="' + val.uid + '" datanominativo="' + val.nominativo + '" data-struttura="' + val.struttura + '" data-email="' + val.email + '" data-telefono="' + val.telefono + '"><a href="#" onclick="refreshToPage(\'contatto\');"><h3>' + val.nominativo + '</h3><p>' + val.struttura + '<br/>EMAIL: ' + val.email + '<br/>TELEFONO: ' + val.telefono + '</p></a></li>'; if (key == count) { $("#contentCronologiaUL").html(html); $("#contentCronologiaUL").listview("refresh"); $("#contentCronologiaUL").trigger("updatelayout"); } $(".contacts").click(function(e) { $("#uidCronologia").val($(this).attr("data-uid")); $("#nominativoCronologia").val($(this).attr("data-nominativo")); Anastasia Kokonozi 566/201 Pagina 110 di 117 $("#strutturaCronologia").val($(this).attr("data-struttura")); $("#emailCronologia").val($(this).attr("data-email")); $("#telefonoCronologia").val($(this).attr("data-telefono")); }); }); } function svuotaCronologia() { if (window.localStorage.getItem("cronologiaRubrica")) { notify(text["svuotaCronologia"], label["attenzione"], "Ok", "clearCronologia", "Annulla", "doNothing"); } } function clearCronologia() { window.localStorage.removeItem("cronologiaRubrica"); $('#contentCronologiaUL').remove(); $('#uidCronologia').remove(); $('#nominativoCronologia').remove(); $('#strutturaCronologia').remove(); $('#emailCronologia').remove(); $("#telefonoCronologia").remove(); $('#noCronologia').show(); } preferiti.html (pagina contente la lista dei preferiti) <div data-role="page" id="preferiti"> <div data-role="header" data-position="fixed" data-theme="b" id="headerPreferiti"> <div id="headerLeftPreferiti"> <a href="#" onclick="slideToPage('index');"><img src="../img/icons/home.png" /></a> </div> <div><h1>Rubrica Unina</h1></div> <div id="headerRightPreferiti"> <a href="#" onclick="svuotaPreferiti();"><img src="../img/icons/cestino.png" /></a> </div> </div> <div data-role="content" id="contentPreferiti"> <ul data-role="listview" id="contentPreferitiUL"></ul> <input type="hidden" id="uidPreferiti" value="" /> <input type="hidden" id="nominativoPreferiti" value="" /> <input type="hidden" id="strutturaPreferiti" value="" /> <input type="hidden" id="emailPreferiti" value="" /> <div id="noPreferiti" align="center"> Non hai ancora aggiunto nessun contatto ai preferiti </div> </div> <div data-role="footer" data-position="fixed" data-theme="b" id="footerPreferiti"> <div data-role="navbar"> <ul> <li><div><a href="#" onclick="refreshToPage('rubrica');">Cerca</a></div></li> <li><div><a href="#" class="ui-btn-active ui-statepersist">Preferiti</a></div></li> <li><div><a href="#" onclick="refreshToPage('cronologia');">Cronologia</a></div></li> </ul> </div> </div> </div> Anastasia Kokonozi 566/201 Pagina 111 di 117 preferiti.js (javascript associato alla pagina contente la lista dei preferiti) $(document).delegate("#preferiti", "pagebeforeshow", function(e,data) { var preferiti = getPreferiti(); if (preferiti != false) { caricaPreferiti(preferiti); } else { $('#contentPreferitiUL').remove(); $('#uidPreferiti').remove(); $('#noPreferiti').show(); } }); function getPreferiti() { if (window.localStorage.getItem("preferitiRubrica")) { return JSON.parse(window.localStorage.getItem("preferitiRubrica")); } else { return false; } } function caricaPreferiti(preferiti) { var html = ""; var count = preferiti.length - 1; $.each(preferiti, function(key, val) { html = html + '<li class="contacts" data-uid="' + val.uid + '" datanominativo="' + val.nominativo + '" data-struttura="' + val.struttura + '" data-email="' + val.email + '" data-telefono="' + val.telefono + '"><a href="#" onclick="refreshToPage(\'contatto\');"><h3>' + val.nominativo + '</h3><p>' + val.struttura + '<br/>EMAIL: ' + val.email + '<br/>TELEFONO: '+ val.telefono + '</p></a></li>'; if (key == count) { $("#contentPreferitiUL").html(html); $("#contentPreferitiUL").listview("refresh"); $("#contentPreferitiUL").trigger("updatelayout"); } $(".contacts").click(function(e) { $("#uidPreferiti").val($(this).attr("data-uid")); $("#nominativoPreferiti").val($(this).attr("data-nominativo")); $("#strutturaPreferiti").val($(this).attr("data-struttura")); $("#emailPreferiti").val($(this).attr("data-email")); $("#telefonoPreferiti").val($(this).attr("data-telefono")); }); }); } function svuotaPreferiti() { if (window.localStorage.getItem("preferitiRubrica")) { notify(text["svuotaPreferiti"], label["attenzione"], "Ok", "clearPreferiti", "Annulla", "doNothing"); } } function clearPreferiti() { window.localStorage.removeItem("preferitiRubrica"); $('#contentPreferitiUL').remove(); $('#uidPreferiti').remove(); $('#nominativoPreferiti').remove(); $('#strutturaPreferiti').remove(); $('#emailPreferiti').remove(); $("#telefonoPreferiti").remove(); $('#noPreferiti').show(); } Anastasia Kokonozi 566/201 Pagina 112 di 117 login.html (pagina contente la maschera per la login) <div data-role="page" id="login"> <div data-role="header" data-position="fixed" data-theme="b" id="headerLogin"> <div id="headerLeftLogin"> <a href="#" onclick="slideToPage('index');"><img src="../img/icons/home.png" /></a> </div> <div><h1>Rubrica Unina</h1></div> </div> <div data-role="content" id="contentLogin"> <div data-role="fieldcontain"> <p> <div id="usernameLabel">Username:</div> <input type="text" name="usernameLogin" id="usernameLogin" value="" /> </p> <p> <div id="passwordLabel">Password:</div> <input type="password" name="passwordLogin" id="passwordLogin" value="" /> </p> </div> <div id="buttonsLogin"> <a href="#" data-role="button" data-theme="b" data-icon="gear" datainline="true" data-iconpos="right" id="submitLogin">Login</a> </div> <div id="loghiLogin"> <div id="logoCsiLogin"> <img src="../img/logo_csi.gif" /> </div> <div id="logoUninaLogin"> <img src="../img/logo_unina.gif" /> </div> </div> </div> <div data-role="footer" data-position="fixed" data-theme="b" id="footerLogin"></div> </div> login.js (javascript associato alla pagina contente la maschera per la login) $(document).delegate("#login", "pagebeforeshow", function(e,data) { $("#submitLogin").click(function(e) { checkLogin(); }); }); function checkLogin() { log("FUNCTION: checkLogin()"); showLoader(); $.ajax({ type: "GET", url: urls['login'], data: 'uid=' + $('#usernameLogin').val() + '&password=' + $ ('#passwordLogin').val(), dataType: "json", success: function(data) { hideLoader(); if (data.error) { Anastasia Kokonozi 566/201 Pagina 113 di 117 notify(error["datiErrati"], "Attenzione!", "Ok", "doNothing"); } else { slideToPage("areaPrivata"); } }, error: function() { hideLoader(); notify(error["network"], "Attenzione!", "Riprova", "checkLogin", "Annulla", "doNothing"); } }); } areaPrivata.html (pagina riservata all'area privata, non implementata) <div data-role="page" id="areaPrivata" data-dom-cache="true"> <div data-role="header" data-position="fixed" data-theme="b" id="headerAreaPrivata"> <div id="headerLeftAreaPrivata"> <a href="#" onclick="slideToPage('index');"><img src="../img/icons/home.png" /></a> </div> <div><h1>Rubrica Unina</h1></div> </div> <div data-role="content" id="contentAreaPrivata" align="center"> <div id="contentAreaPrivataInner"> Area privata <img src="../img/areaPrivata.png"/> </div> </div> <div data-role="footer" data-position="fixed" data-theme="b" id="footerAreaPrivata"></div> </div> library.js (insieme di funzioni comuni, utilizzate in diversi punti dell'applicazione) function checkConnection() { log("FUNCTION: checkConnection()"); return navigator.connection.type; } function manageBackButton() { log("FUNCTION: manageBackButton()"); if ($.mobile.activePage.attr('id') == "main") { notify(text['exit'], 'Attenzione!', 'Ok', 'closeApp', 'Annulla', 'doNothing'); } } function slideToPage(page) { log("FUNCTION: slideToPage('" + page + "')"); if (!loading) { $.mobile.changePage(page + ".html", {showLoadMsg: false, transition: "none"}); } } function refreshToPage(page) { log("FUNCTION: refreshToPage('" + page + "')"); Anastasia Kokonozi 566/201 Pagina 114 di 117 if (!loading) { $.mobile.changePage(page + ".html", {reloadPage: true, transition: "none"}); } } function showSpinner() { log("FUNCTION: showSpinner()"); $.mobile.showPageLoadingMsg(); } function hideSpinner() { log("FUNCTION: hideSpinner()"); $.mobile.hidePageLoadingMsg(); } function showLoader() { log("FUNCTION: showLoader()"); loading = true; $.mobile.showPageLoadingMsg("a", "Caricamento in corso ...", false); } function hideLoader() { log("FUNCTION: hideLoader()"); $.mobile.hidePageLoadingMsg(); loading = false; } function doNothing() { log("FUNCTION: doNothing()"); } function closeApp() { log("FUNCTION: closeApp()"); navigator.app.exitApp(); } function notify(msg, label, button1, method1, button2, method2) { log("FUNCTION: notify(" + msg + ", " + label + ", " + button1 + ", " + method1 + ", " + button2 + ", " + method2 + ")"); if (msg == null || msg == undefined) { msg = "Siamo spiacenti si è verificato un errore!"; } if (button2 != null) { if (method2 == null) { method2 = "doNothing"; } buttons = button1 + "," + button2; navigator.notification.confirm( msg, function(buttonIndex) { if (buttonIndex == 1) { notifyCallback(method1); } else { notifyCallback(method2); } }, label, buttons ); } else { navigator.notification.confirm( msg, function() { notifyCallback(method1); }, label, button1 ); } } Anastasia Kokonozi 566/201 Pagina 115 di 117 function notifyCallback(method) { eval(method)(); } function log(msg) { if (development) { console.log(msg); } } Anastasia Kokonozi 566/201 Pagina 116 di 117 Bibliografia • Android, Portale Android per gli sviluppatori ◦ http://developer.android.com/index.html • Android, Guida allo sviluppo ◦ http://developer.android.com/guide/index.html • Android, Pubblicazione su Google Play ◦ http://developer.android.com/guide/publishing/publishing.html • Forum HTML.IT, guide e forum di supporto per sviluppatori ◦ http://www.html.it • JQuery, JQuery Mobile ◦ http://www.jquerymobile.com/ • PhoneGap ◦ http://www.phonegap.com • PhoneGap, gruppo di supporto per gli sviluppatori ◦ https://groups.google.com/forum/?fromgroups#!forum/phonegap • Autori Vari, Wikipedia ◦ http://www.wikipedia.it • Autori Vari, StackOverflow ◦ http://www.stackoverflow.com Anastasia Kokonozi 566/201 Pagina 117 di 117