UNIVERSITÀ POLITECNICA DELLE MARCHE FACOLTÀ DI INGEGNERIA Corso di Laurea triennale in Ingegneria Informatica e dell’Automazione TESI DI LAUREA PROGETTO E SVILUPPO DI APPLICAZIONI MHP PER PRENOTAZIONE DI SERVIZI TRAMITE IL CANALE DI RITORNO Relatore: Candidato: Prof. Aldo Franco Dragoni Fabio Talamonti Correlatore: Prof. Paolo Puliti Anno Accademico 2005/2006 INDICE INTRODUZIONE 5 CAPITOLO 1 INTRODUZIONE ALLA TV DIGITALE TERRESTRE 7 1.1 Televisione Digitale Terrestre: DVB-T 7 1.2 Vantaggi e svantaggi del digitale terrestre 8 1.3 La piattaforma per i servizi della TV digitale 10 CAPITOLO 2 LO STANDARD MHP 12 2.1 La piattaforma MHP 12 2.2 Architettura della piattaforma MHP 13 2.2.1 Applicazioni 14 2.2.2 Software di sistema 16 2.2.3 Risorse e periferiche 17 2.3 Profili MHP 17 2.4 Protocollo di trasporto 21 2.4.1 Il protocollo DSM-CC Object Carousel 21 2.5 Fruizione di un servizio interattivo MHP 24 2.6 Il Set-Top Box (STB) 26 CAPITOLO 3 APPLICAZIONI MHP: LE XLET 28 3.1 L’ambiente Java delle Xlet 28 3.2 Ciclo di vita di una Xlet 29 3.3 Contesto di una Xlet 32 -2- 3.4 Interfaccia grafica delle Xlet 34 CAPITOLO 4 SVILUPPO DI UN’APPLICAZIONE MHP 4.1 Presentazione del progetto 37 37 4.1.1 Registrati 38 4.1.2 Prenota 39 4.1.3 Info 40 4.2 Realizzazione della Xlet 41 4.2.1 Il JDBC 41 4.2.2 Le socket 43 4.3 Il Server 45 4.3.1 SimpleServer.java 46 4.3.2 ServerRegistration.java 49 4.3.3 ServerFilm.java 51 4.3.4 ServerPrenotation.java 54 4.3.5 ServerPrenotaSingolo.java 57 4.3.6 PrenotaSingolo.java 58 4.3.7 ObjectRegistration 59 4.3.8 ObjectPrenotation.java 61 4.3.9 ObjectFilm.java 62 4.3.10 ObjectPosti.java 63 4.3.11 ObjectPrenFilm.java 64 4.4 La Xlet 65 4.4.1 Main.java 65 4.4.2 BackgroundController.java 70 4.4.3 GestoreSfondi.java 80 4.4.4 InterfacciaGrafica.java 81 -3- 4.4.5 Keypad.java 90 4.4.6 KeypadButton.java 97 4.4.7 Comunica.java 98 4.4.8 Strumenti.java 100 4.4.9 Registra.java 101 4.4.10 Prenota.java 116 4.4.11 Film.java 125 4.4.12 PlateaPrenota.java 131 4.4.13 Platea.java 132 4.4.14 Info.java 141 CONCLUSIONI 145 BIBLIOGRAFIA 147 -4- INTRODUZIONE Questo lavoro si prefigge l’obiettivo di illustrare il procedimento per sviluppare un’applicazione MHP per la televisione digitale terrestre, mostrando il risultato finale ed i vari passi per arrivarci. Prima di fare ciò, è necessaria un’introduzione alla TV digitale terrestre, e in particolare allo standard MHP, sul quale si basano le applicazioni per la televisione. Si inizierà con un po’ di storia del digitale terrestre, in particolare il progetto DVB; verranno poi illustrati vantaggi e svantaggi di questa nuova tecnologia, seguiti da una doverosa spiegazione del funzionamento della piattaforma per i servizi della TV digitale. Si passerà, poi, ad introdurre lo standard MHP, illustrando l’architettura della piattaforma e i profili MHP, senza tralasciare il protocollo di trasporto e l’apparecchio con il quale è possibile vedere la TV digitale: il Set-Top Box (STB). Ci sarà, inoltre, una doverosa spiegazione del funzionamento delle Xlet, cioè le applicazioni vere e proprie, tramite la descrizione dell’ambiente java, del ciclo di vita, del contesto e dell’interfaccia grafica delle Xlet, ed anche per mezzo di alcuni esempi. Infine, si presenterà il progetto sviluppato, che ha come scopo principale di mostrare un esempio di applicazione MHP che preveda l’utilizzo del canale di ritorno. Si inizierà con una descrizione dell’applicazione, seguita da un’illustrazione dei vari passi che sono stati necessari alla realizzazione del progetto. Si passerà, in seguito, a mostrare il codice Java, partendo dal server e proseguendo con la Xlet. Questo progetto evidenzia come la televisione digitale terrestre abbia ampi margini di ampliamento e di sviluppo orientati, ad esempio, verso l’utilizzo di -5- Internet anche senza avere un computer, oppure verso servizi di prenotazione (come nel caso del progetto qui considerato) relativi a cinema, ristoranti, ecc. Inoltre, l’applicazione sviluppata, consente di notare come lo standard MHP possa essere facilmente integrato con altre tecnologie, come i database residenti su server. La televisione digitale terrestre, dunque, rappresenta una tecnologia destinata a svilupparsi nel futuro, e che potrebbe diventare di uso quotidiano in tutte le case, ma anche negli uffici, aziende, ecc. Per questo motivo è importante avere delle nozioni di base su questo importante scenario del futuro. -6- CAPITOLO 1 INTRODUZIONE ALLA TV DIGITALE TERRESTRE 1.1 TELEVISIONE DIGITALE TERRESTRE: DVB-T La trasmissione digitale costituisce una tappa importantissima nello sviluppo tecnologico dei sistemi televisivi. Essa consente di trasformare l’apparecchio televisivo in una piattaforma per lo sviluppo dei servizi interattivi, che si aggiungono così alla funzione tradizionale di diffusione circolare dei segnali. In Europa, all’inizio delle attività in questo campo, c’è il progetto Digital Video Broadcasting (DVB) promosso dalla Commissione Europea per definire standard comuni. Il progetto, cui hanno partecipato 170 società coinvolte nei diversi settori dell'industria televisiva, ha raggiunto l'obiettivo di stabilire un unico standard condiviso su scala europea per le trasmissioni televisive digitali via satellite (DVB-S), via cavo (DVB-C) e via terra (DVBT). Questi standard sono stati ora adottati anche dal Giappone e da altri paesi non europei. -7- Capitolo 1 Introduzione alla TV digitale terrestre Figura 1: Stato della televisione digitale terrestre nel mondo 1.2 VANTAGGI E SVANTAGGI DEL DIGITALE TERRESTRE I vantaggi del digitale terrestre possono essere individuati in questi punti: potenziamento del servizio televisivo in termini di quantità e di qualità: ciò riguarda una migliore qualità dell’immagine e del suono permettendo, inoltre, l’utilizzo di televisori di grande formato. Si ha, inoltre, un notevole aumento del numero di canali disponibili: su ogni canale televisivo viene trasmesso un flusso di dati che trasporta nello stesso momento con la tecnica del multiplex un certo numero di programmi televisivi diversi. Non esiste un numero fisso di programmi televisivi che si possono trasmettere perché questo è funzione della larghezza di banda occupata da ciascun programma, tenendo presente che la larghezza di banda massima a disposizione per singolo canale è circa 24 Mbit/sec. In un canale si possono così trasmettere quattro programmi da 6 Mbit/sec l'uno, oppure dodici programmi da 2 Mbit/sec ciascuno, ovviamente con una minore qualità delle immagini. È ovviamente possibile trasmettere anche un solo programma da 24 Mbit/sec ad alta qualità, per esempio con una definizione maggiore. Attualmente l'ipotesi è solamente teorica, perché la trasmissione, per essere realmente fruibile, avrebbe bisogno anche di un ricevitore in grado di fornire in uscita un segnale ad alta definizione, e anche il display (televisore o monitor) dovrebbe essere ad alta definizione. Interattività: la trasmissione digitale consentirà di interagire con la televisione; ciò consiste nella possibilità di dialogare attraverso il decoder, munito di modem, con l'emittente. Mentre con le trasmissioni analogiche gli impianti funzionano da semplici ricevitori, con le trasmissioni digitali è possibile interagire con l'emittente attraverso un decoder che adotta lo standard compatibile con il digitale terrestre, detto MHP. Si potrà partecipare a programmi televisivi a quiz, rispondere a domande e sondaggi, interrogare il -8- Capitolo 1 Introduzione alla TV digitale terrestre portale su alcuni servizi come quelli del proprio comune, eseguire operazioni bancarie, eccetera. Meno stazioni di trasmissione: per trasmettere il segnale serve molta meno potenza, circa un decimo di quella necessaria per l'analogico ed è prevista una riduzione del numero di stazioni trasmittenti sul territorio, una per provincia. Nonostante il rischio legato all’elettrosmog non sia stato dimostrato essere effettivamente pericoloso si tratta comunque di una positiva novità del sistema digitale. Va inoltre evidenziata la possibilità di trasmettere in isofrequenza, tecnica che non era possibile implementare nelle trasmissioni televisive analogiche. Questa evoluzione tecnologica permette di inviare lo stesso segnale sulla medesima frequenza contemporaneamente da più siti trasmittenti. Vi sono, però, anche alcuni significativi svantaggi: Costi aggiuntivi: Gli svantaggi del digitale terrestre sono legati soprattutto al passaggio dal sistema analogico. Anche se in futuro il decoder potrà essere integrato nei televisori oggi è necessario comprarne uno a parte per ogni televisore. Inoltre, serve un canale di comunicazione aggiuntivo (via modem o cellulare) per interagire con i servizi trasmessi (anche questo potrebbe in futuro essere integrato con il televisore). Inoltre, gli apparecchi ad alta definizione per cui il digitale è pensato sono ancora costosi. Ricezione: Attualmente il sistema italiano è in fase sperimentale e molte zone non sono ancora coperte dal segnale digitale oppure sono soggette a temporanei blackout. A differenza del segnale analogico che ha una degradazione graduale (vale a dire, c'è una transizione continua tra la ricezione perfetta e l'impossibilità di vedere una qualunque immagine), il segnale digitale è un sistema quasi "onoff". Questo significa che sopra una certa soglia di rapporto segnale/rumore il segnale viene visualizzato perfettamente, con il software che riesce a -9- Capitolo 1 Introduzione alla TV digitale terrestre ricostruirlo grazie alla ridondanza dei dati inviati via etere. Al ridursi del rapporto segnale/rumore il numero di errori di decodifica (detto BER) cresce fino al punto in cui la correzione dell'errore diviene impossibile, con un passaggio repentino a una non fruibilità assoluta del segnale. In questa situazione, appaiono spesso disturbi tipici della codifica MPEG, quali "quadrettoni colorati" in luogo dell'immagine video e fischi e altri rumori anomali in luogo del normale audio. La disposizione ministeriale che prevede solamente una stazione di trasmissione per provincia potrebbe creare problemi nelle zone montuose. I ripetitori installati sono attualmente una piccola frazione della copertura necessaria. 1.3 LA PIATTAFORMA PER I SERVIZI DELLA TV DIGITALE Figura 2: Schema di trasmissione per i servizi della TV digitale La piattaforma dei servizi per la TV digitale terrestre è mostrata in figura 2. - 10 - Capitolo 1 Introduzione alla TV digitale terrestre Un ruolo fondamentale è ricoperto dal Centro Servizi, direttamente collegato con un fornitore di Servizi. Questo può essere interpretato come più unita distinte che svolgono ruoli separati, che possono consistere in fornitori di dati, fornitori di applicativi MHP o enti esterni con cui le applicazioni necessitano di interagire. Solitamente il fornitore di servizi è colui che si occupa di rilasciare le informazioni (foto, file A/V, testi,ecc…) che verranno puoi incluse nelle applicazioni MHP. Il fornitore di servizi potrebbe essere in certi casi anche colui che progetta direttamente l’applicativo MHP ma che necessita di un’unità in grado di comunicare con il broadcaster e che lo appoggi nella gestione dell’interattività con canale di ritorno. E’ infatti con le applicazioni che fanno uso del canale di ritorno che il ruolo del Centro Servizi si fa fondamentale. Il Centro Servizi appoggiandosi alla rete IP mette a disposizione un numero telefonico a cui le applicazioni effettuano la chiamata con il modem PSTN integrato. Offre anche un sistema di autenticazione a livello di collegamento tramite un nome utente e una password, che pero solitamente è comune a tutti gli utenti. Sarà compito del programmatore creare un metodo di autenticazione dell’utente a livello più alto magari tramite l’uso di una Smart Card per applicazioni che richiedono accesso con autenticazione. E’ comunque il Centro Servizi a fornire l’IP che verrà poi utilizzato dal ricevitore per lo scambio di informazioni, e quindi dovrà essere in grado di sostenere più connessioni contemporanee mettendo a disposizione un adeguato pool di indirizzi IP pubblici e tramite un sistema di primari ISDN di capacità adeguata alle possibili chiamate telefoniche contemporanee. - 11 - CAPITOLO 2 LO STANDARD MHP 2.1 LA PIATTAFORMA MHP I due concetti fondamentali della TV digitale sono la multimedialità e l’interattività. Il DVB si è, dunque, preso il compito di definire le specifiche per un servizio televisivo multimediale che consenta l’interattività con l’utente tramite il canale di ritorno. Il consorzio, ha perciò definito uno standard denominato MHP (Multimedia Home Platform), al quale devono conformarsi sia i centri di emissione sia i decoder degli utenti. MHP definisce una generica interfaccia tra le applicazioni digitali interattive ed i terminali sui quali le applicazioni vengono eseguite. Un decoder compatibile MHP è una macchina capace di eseguire le primitive previste nelle API (Application Programming Interface) specificate nello standard, nascondendo alle applicazioni i dettagli sulle specifiche risorse hardware e software dello stesso decoder. Così, è possibile realizzare e modificare con molta flessibilità programmi televisivi digitali che diventano eseguibili su tutti i decoder compatibili MHP. Esistono diverse tipologie di API (librerie nelle quali sono raccolte un insieme di primitive informatiche) per MHP: - generiche: Java 1.1, che sono le seguenti: javax.tv.service e javax.service.navigation per l’interazione con il servizio; javax.service.guide per la gestione dell’EPG (Electronic Program Guide); - 12 - Capitolo 2 Lo standard MHP javax.service.transport per la gestione dello strema MPEG-2; javax.tv.xlet che fornisce le interfacce usate dalle applicazioni (Xlet) e permette loro la comunicazione con l’application manager (vedi par. 2.2.2); javax.media, javax.tv per il media control; - per l’accesso a MPEG a basso livello: org.davic.mpeg; - per la gestione delle risorse: org.davic.resources; - per l’application lifecycle: org.dvb.application; - per le comunicazioni: org.dvb.dsmcc, java.io; - per la grafica: org.havi.ui; - per la sicurezza: dvb.signature, dvb.certificate; Per le sue peculiarità, MHP si prefigura come un elemento in grado di consentire la convergenza delle tecnologie di radiodiffusione con quelle Internet. 2.2 ARCHITETTURA DELLA PIATTAFORMA MHP - 13 - Capitolo 2 Lo standard MHP Figura 3: Architettura della piattaforma MHP L’architettura della piattaforma MHP, come mostrato in Figura 3, è definita in tre livelli: - Risorse o periferiche - Software di sistema - Applicazioni 2.2.1 Applicazioni Un’applicazione è la realizzazione di un servizio interattivo formato da moduli programmabili che richiedono funzionalità specifiche residenti nell’hardware o nel software del terminale. Esse sono la parte fondamentale nella filosofia MHP. Lo standard MHP supporta diverse tipologie di applicazioni, come: - EPG (Electronic Program Guide) che si presenta come una pagina Web interattiva dedicata alla guida multimediale, alla sintonia dei programmi ed all’illustrazione del loro contenuto; - servizi di teletext avanzato, che consiste in un aumento di contenuti anche sotto forma di immagini, grafici, ipertesti, clip audio e video, giochi, - 14 - Capitolo 2 Lo standard MHP rendendo questo servizio informativo del tutto analogo a pagine di siti web; - applicazioni collegate al contenuto dei programmi o sincronizzate con il contenuto televisivo, come banner pubblicitari, giochi interattivi ed altro; - servizi di e-commerce, servizi bancari, che permettono di effettuare e controllare gli acquisti da remoto, o gestire ordinarie operazioni su conti correnti, compravendita di azioni ed altro; - servizi transattivi, quali quelli forniti già su Internet da vari enti, come Poste Italiane, INPS, Ferrovie dello Stato, ASL, ecc. Questi servizi non riguardano quelli propriamente televisivi, dato che utilizzano il terminale solo come alternativa al computer; essi, però, costituiscono un esempio di convergenza tecnologica che potrà avere importanti sviluppi in futuro, specie se si potesse allacciare il decoder televisivo a canali a larga banda (ADSL e simili); - giochi. Le applicazioni possono essere di tre tipi: Residenti: forniscono servizi diversi a seconda del decoder considerato; Installabili: contengono funzionalità aggiuntive che possono integrare il funzionamento delle applicazioni residenti; Scaricabili: vengono, di solito, offerte dagli operatori di servizi televisivi per arricchire l’offerta al cliente. Devono essere conformi allo standard. - 15 - Capitolo 2 Lo standard MHP Figura 4: Esempi di applicazioni MHP 2.2.2 Software di sistema Il software di sistema comprende diversi moduli che devono essere costruiti sulle risorse informatiche del singolo decoder: - insieme di programmi (sistema operativo, programmi di gestione delle periferiche, ecc.) che usano le risorse disponibili per nasconderle alle applicazioni, fornendo ad esse una rappresentazione logica in termini di primitive software; - piattaforma nota come DVB-Java (DVB-J) che si interfaccia con le applicazioni attraverso le API MHP; essa include, a sua volta: una macchina virtuale Java JVM (Java Virtual Machine), secondo le specifiche della Sun Microsystem; pacchetti software con funzionalità generali (API Java della Sun) e specifiche (API Java per la TV, per DAVIC e per HAVI). - gestore di applicazioni (application manager), noto anche come navigatore, che gestisce la piattaforma MHP e le applicazioni che girano su di essa. - 16 - Capitolo 2 Lo standard MHP 2.2.3 Risorse e periferiche Le risorse e le periferiche del terminale MHP sono standardizzate solo in parte. La dotazione standard prevede le seguenti risorse: - demodulatore hardware MPEG; - moduli di accesso condizionato; - processore; - memorie Ram e Flash, hard disk. Le interfacce, invece, sono le seguenti: - modem; - lettore smart-card; - lettore DVD. 2.3 PROFILI MHP Il sistema MHP ha fornito il concetto di profilo per dare una mano nell’implementazione dello standard. Ogni profilo si riferisce ad una specifica area di applicazioni e conseguentemente definisce i requisiti dei Set Top Box necessari a supportarlo. Attualmente esistono tre profili MHP, definiti in due set di specifiche. Infatti dato che i primi due sono molto simili tra loro, il consorzio ha deciso di includerli nella stessa release di specifiche. I tre profili (vedi figura 5) attorno ai quali ruota tutta la piattaforma sono: Enhanced Broadcast Profile: definito nelle specifiche MHP 1.0,è designato a rispecchiare i vari modi e le funzionalità dei sistemi middleware esistenti e le applicazioni che girano su di essi. Questo profilo richiede un Set Top Box con nessuna o limitate capacità di gestione del canale di ritorno; è il profilo base e permette solamente l’arricchimento del contenuto audio-video con informazioni e immagini visualizzabili e - 17 - Capitolo 2 Lo standard MHP navigabili sullo schermo. Per questo motivo non sono richieste performance particolari da parte dei Set Top Box. Interactive TV Profile: definito nelle specifiche MHP 1.0, è il profilo intermedio che permette di utilizzare il canale di ritorno (di tipo PSTN, ADSL, GPRS, Ethernet, ecc.) per fornire servizi con interattività superiore rispetto al profilo base. Questo profilo, infatti, supporta anche il caricamento di applicazioni MHP tramite il canale di ritorno (ma solo dalla versione 1.1), caratteristica che nel profile Enhanced è possibile solo attraverso il canale broadcast; Internet Access Profile: definito nelle specifiche MHP 1.1, richiede un Set Top Box molto più sofisticato, con potenza di calcolo e memoria interni maggiori che nei primi due profili; permette, tramite il canale di ritorno, un’interattività totale e un completo accesso ai contenuti di Internet. Il profilo Internet Access contiene un elemento HTML opzionale chiamato DVB-HTML, permettendo il supporto di HTML, CSS, DOM, EMCA Script. Questo profilo necessita di performance di alto livello essendo obbligatoria l’adozione di un browser internet e di un cliente e-mail embedded nel Set Top Box. - 18 - Capitolo 2 Lo standard MHP Figura 5: MHP - Profili implementativi Lo standard di base è costituito dalla specifica MHP 1.0. La specifica 1.0.X contiene: - l'architettura base di MHP; - informazioni dettagliate sui profili “Enhanced Broadcasting” ed “Interactive TV”; - diversi formati contenuti in MHP, che includono JPEG, MPEG-2 video e audio; - protocolli di trasporto, che includono DSM-CC per la trasmissione broadcast e IP per il canale di ritorno; - modelli di applicazione DVB-J; - modelli di applicazione DVB-HTML; - allegati al profilo DSM-CC, una presentazione testuale e varie API; MHP 1.0.X specifica l'ambiente dove si possono eseguire le applicazioni per la tv interattiva digitale, indipendentemente dall'hardware e dal software sottostante, che sono specifici del produttore di STB. - 19 - Capitolo 2 Lo standard MHP La specifica MHP 1.0 fornisce un insieme di caratteristiche e funzioni richieste dai profili ‘enhanced broadcasting' ed ‘interactive broadcasting'. In seguito è stata emanata la specifica MHP 1.1 per implementare il profilo “Internet Access”. MHP 1.1.X contiene: - informazioni dettagliate sui profili “Interactive TV” ed “Internet Access”; - la disponibilità per l'immagazzinamento delle applicazioni nella memoria persistente; - download delle applicazioni mediante i canali broadcast o di interazione; - estensioni al DVB-J per supportare meglio le applicazioni e l'accesso a lettori di smart card non certificati; - specifiche di DVB-HTML; - supporta la gestione di plug-in interoperabili (per il supporto di formati di applicazioni non conformi); - supporto per i riferimenti bidirezionali tra il contenuto di MHP ed il contenuto internet; MHP 1.1 è stata sviluppata basandosi sulla specifica MHP 1.0 con lo scopo di supportare meglio l'uso del canale di interazione e per specificare gli elementi che promuovono l'interoperabilità con il contenuto internet. Infatti MHP 1.1 è semplicemente un'altra versione della specifica MHP 1.0, basata sugli stessi file sorgente. Perciò in gran parte il contenuto di MHP 1.0 è ripetuto in MHP 1.1. Con MHP 1.1, grazie al profilo Internet Access, le applicazioni possono controllare le operazioni basilari dei client residenti su internet (web browser, e-mail). Per aggiungere queste funzionalità ed integrare il formato applicativo DVB-J, MHP 1.1 ha definito un nuovo tipo di applicazione opzionale: DVB-HTML, che è un linguaggio di mark-up basato su HTML; permette ad un ricevitore di presentare le applicazioni della tv interattiva digitale in HTML. Le specifiche - 20 - Capitolo 2 Lo standard MHP DVB-HTML presentano le stesse estensioni e restrizioni delle specifiche del linguaggio W3C esistente. 2.4 PROTOCOLLO DI TRASPORTO Affinché sia in grado di comunicare con il mondo esterno, è necessario che il terminale supporti vari tipi di rete; per questo le specifiche MHP introducono il supporto di diversi protocolli, così come definiti nelle specifiche DVB, a cui la piattaforma fa riferimento. I protocolli definiti in questo standard forniscono il supporto generico a una varietà di servizi sia di tipo broadcast che interactive; le specifiche infatti supportano i protocolli DSM-CC data carousel e object carousel e tutti i protocolli basati su IP. Risulta interessante che i protocolli IP, intrinsecamente usabili sul canale interactive, possono essere usati anche sul canale broadcast grazie al supporto dalla Multiprotocol Encapsulation (MPE). Le varie configurazioni di rete supportate sono raggruppate in due classi: - i servizi broadcast sono implementati su sistemi che consistono di un canale di downstream dal Service Provider all’utente finale; - i servizi interattivi invece si riferiscono a sistemi che consistono di un canale di downstream associato ad un canale di ritorno. I file dati sono trasmessi utilizzando i protocolli DSM-CC o IP incapsulato in MPEG-2 così come definito nelle specifiche DVB relativamente alla MPE. Le applicazioni MHP vengono trasportate all’utente finale attraverso il protocollo DVB Object Carousel. 2.4.1 Il protocollo DSM-CC Object Carousel Il protocollo DSM-CC è stato originariamente progettato per l’uso con macchine di rete VTR (Video Tape Recorder – Registrazione video in rete). - 21 - Capitolo 2 Lo standard MHP Da allora si è sviluppato molto e adesso include il controllo di server video MPEG in rete (con funzioni di play, stop, pausa del video e dell’audio), il supporto per la trasmissione di dati utilizzando MPEG-2, timecode per il video MPEG, il broadcasting di dati semplici e interi file system. I sistemi broadcast sono per loro natura unidirezionali. I dati vengono inviati da un trasmettitore e il ricevitore non può richiedere dati specifici. Questo significa che un ricevitore non può accedere ad uno specifico file su un server come avviene nei normali PC. Per questo è necessaria un’altra soluzione che permetta al ricevitore MHP di accedere ai dati sul flusso broadcast. L’approccio è quello di trasmettere periodicamente ogni file del file-system e il ricevitore non deve fare altro che attendere il file di cui ha bisogno. Il miglior esempio di questa soluzione è il sistema teletext digitale: ad ogni pagina è associato un numero univoco e le pagine vengono trasmesse una dopo l’altra ciclicamente. Quando l’utente digita il numero della pagina da ricevere, il ricevitore deve attendere che la pagina venga trasmessa prima di poterla decodificare e presentare al display. Questo tipo di soluzione va sotto il nome di carosello (ogni pagina viene trasmessa a turno periodicamente e il ricevitore deve attendere il ritorno di quella pagina nel flusso broadcast per poterla prelevare e usarla). Certamente questa soluzione non è molto efficiente, ed infatti si sono nel tempo sviluppate tecniche per migliorarne le prestazioni. Nel DSM-CC, i dati vengono trasmessi in blocchi chiamati “moduli” invece che in pagine, ma il principio è lo stesso del sistema di teletext. I dati da trasmettere vengono suddivisi in moduli, vengono aggiunte informazioni a ciascun modulo, quindi ogni modulo viene trasmesso a turno. Il DSM-CC supporta due tipi di carosello. Il più semplice dei due è il data carousel, che fornisce al broadcaster un modo per trasmettere blocchi di dati al ricevitore. Non sono però presenti dati aggiuntivi che informano sul tipo di dati e su cosa si stia trasmettendo ed è quindi compito del ricevitore - 22 - Capitolo 2 Lo standard MHP rielaborare i dati in una forma sensata. In situazioni più complesse il data carousel non risulta molto utile e in questi casi è l’object carousel costituisce una soluzione migliore. L’object carousel è costruito come protocollo direttamente sopra il data carousel e fornisce funzionalità vicine a quelle di un file-system standard. Ogni object carousel consiste di un albero di directory suddiviso in una serie di moduli, che possono contenere uno o più file o directory. Ogni modulo può contenere molteplici file con una dimensione totale minore di 64Kb (l’inserimento di più file in un modulo di dimensione maggiore di 64Kb non è permesso, così come suddividere un unico file su più moduli), quindi un file più grande di 64Kb andrà a riempire il suo proprio modulo che conterrà solamente quel file. Questi moduli vengono trasmessi in broadcast l’uno dopo l’altro fino alla fine. A questo punto la trasmissione comincia da capo con il primo modulo. Per l’accesso ad un preciso file, il ricevitore deve attendere fino a che non riceve il modulo contenente quel file; a questo punto può elaborare il modulo ed avere accesso al file desiderato. Questo metodo può non essere molto efficiente quando le dimensioni totali dei dati da trasmettere sono considerevoli, ma la maggior parte dei ricevitori è in grado di mantenere memorizzati nella cache parte dei dati e questo velocizza notevolmente le operazioni. Alcuni moduli possono essere trasmessi più di una volta all’interno dell’object carousel. Infatti, trasmettendo alcuni moduli più spesso di altri, il tempo di accesso ai file più comunemente usati viene ridotto notevolmente. D’altro canto, però, agendo in questo modo, cresce la dimensione totale del carosello, causando un aumento del tempo di accesso ai dati meno usati. Nel progetto di layout di un carosello si deve considerare attentamente questo compromesso che, se ottimizzato, può portare ad una riduzione notevole del tempo di caricamento di un’applicazione. - 23 - Capitolo 2 Lo standard MHP Nello stesso carosello è possibile inserire più applicazioni che verranno così trasmesse contemporaneamente dando all’utente la possibilità di scegliere, ad esempio, se sfogliare la guida EPG piuttosto che misurarsi in un gioco interattivo. Ovviamente spetta all’object carousel generator il compito di condividere tra le varie applicazioni la banda destinata al carosello. All’interno di un transport stream DVB possono essere trasportati uno o più Object Carousel, nello stesso modo in cui vengono gestiti gli stream del video, dell’audio e della service information. Ad ogni stream Object Carousel viene allocato un PID univoco che viene referenziato nelle tabelle DVB-SI. Uno stream Object Carousel occupa tipicamente una banda di payload di circa 1Mbps, ma nelle applicazioni reali questo valore può essere limitato dall’abilità del ricevitore di decodificare e elaborare le applicazioni. 2.5 FRUIZIONE DI UN SERVIZIO INTERATTIVO MHP Nella figura 6, viene illustrato in maniera molto schematica l’ambiente coinvolto nell’esecuzione di un’applicazione MHP. Nel momento in cui si fruisce dalla TV digitale entrano in gioco due gestori di contenuti: il primo è quello che fornisce i contenuti sotto forma di programmi televisivi, il broadcaster, il secondo è quello che fornisce i contenuti sotto forma di dati (estratti conto, informazioni pubblicitarie, ecc.). Il primo distribuisce i propri servizi broadcast, sfruttando un canale dedicato (unidirezionale) che dipende dallo standard utilizzato, mentre il secondo distribuisce i propri servizi utilizzando Internet come luogo di scambio. - 24 - Capitolo 2 Lo standard MHP Figura 6: Architettura di fruizione di un servizio interattivo MHP Durante un programma televisivo è possibile, attraverso la pressione di un apposito tasto del telecomando, richiedere l’esecuzione di un’applicazione, ove essa sia disponibile. Di solito il fornitore informa l’utente della presenza di un’applicazione nel carosello per mezzo di un segnale visivo, un’etichetta lampeggiante o un’icona corrispondente ad un tasto colorato del telecomando. Nel momento in cui l’applicazione si trova nella fase di Active, l’utente può iniziare ad usufruire del servizio che, a seconda del tipo, è dotato di un maggiore o minore grado di interattività. L’utente effettua delle richieste interagendo con l’applicazione che le inoltra, per mezzo del canale di ritorno, al centro servizi, il quale restituisce i contenuti voluti al lato client che li ripropone all’interno dello schermo televisivo. - 25 - Capitolo 2 Lo standard MHP 2.6 IL SET-TOP BOX (STB) Il decoder interattivo (Set-Top Box) è l'apparecchio indispensabile per ricevere i canali digitali e utilizzare i software interattivi. Questo dispositivo permette innanzitutto la decodifica del segnale da digitale ad analogico per gli apparecchi televisivi analogici che attualmente nel nostro paese rappresentano la totalità del mercato. Infine consente di eseguire applicazioni Java grazie alla presenza di una Personal JVM embedded. Come mostra la Figura 7 un decoder è formato da una parte hardware (CPU, memoria, modem, ecc.), da un sistema operativo real time e da un middleware MHP. Figura 7: Schema hardware/software di un STB Un attore importante che troviamo all'interno del decoder è il cosiddetto navigatore (o application manager). Il suo compito è fondamentale, infatti esso si occupa di segnalare all'utente la lista delle applicazioni MHP disponibili sul canale televisivo. Inoltre se l'utente effettua una selezione da tale lista, il navigatore inizia la fase di download in memoria dei moduli relativi all'applicazione scelta. Importante sottolineare che non tutti i moduli che compongono l'applicazione verranno scaricati, infatti il download sarà - 26 - Capitolo 2 Lo standard MHP relativo solo a quelli necessari alla partenza della Xlet. Successivamente e solo se realmente necessari verranno scaricati anche quelli rimanenti. Tale meccanismo ricorda il modello di caricamento delle pagine del televideo ed è pensato allo scopo di ridurre i tempi di attesa del telespettatore. Un dispositivo tecnologico presente sul decoder che rende operativo il concetto di canale di ritorno definito nello standard è il modem nei decoder di fascia bassa e l'ADSL, Gprs, Ethernet, etc. sui decoder di fascia alta. Il canale di ritorno permette di interagire con servizi esterni, su server dedicati o internet e scaricare contenuti su richiesta del telespettatore. Nel caso di modem V90 i tempi di connessione e recupero dati sono abbastanza lunghi(30-40 sec solo per la chiamata telefonica e l'autenticazione presso un ISP), ma nel caso di connessioni "always on" come con ADSL allora gli scenari possibili sono molto più interessanti, come la possibilità di scaricare musica e video su richiesta dell'utente. - 27 - CAPITOLO 3 APPLICAZIONI MHP: LE XLET 3.1 L’AMBIENTE JAVA DELLE XLET Le applicazioni sviluppate per MHP vengono chiamate applicazioni DVB-J. Sono scritte in Java e consistono in un insieme di classi trasmesse in broadcast. Spesso si fa riferimento a tali applicazioni con il nome di Xlet. In pratica, un’applicazione Java convenzionale non si adatta bene all’ambiente televisivo digitale. Questo modello presuppone l’esecuzione di una sola applicazione in una data macchina virtuale, e che l’applicazione stessa abbia il controllo completo del proprio ciclo di vita. In un ricevitore televisivo digitale, invece, è frequente che più applicazioni vengano eseguite nello stesso momento. A tal proposito viene considerato il funzionamento delle applet che, a differenza delle normali applicazioni, possono essere avviate da una sorgente esterna, il web browser, ed alcune di esse possono essere eseguite sulla stessa pagina web nello stesso momento. Ovviamente il sistema televisivo digitale è diverso dal Web, perciò devono essere adottati alcuni cambiamenti affinché questi concetti si adattino al ricevitore digitale. Una Xlet quindi non è un’applicazione Java standard: è una particolare applicazione concepita per essere scaricata ed eseguita sui decoder interattivi; esse sono state introdotte da Sun nella specifica JavaTV, ed adottate come formato applicativo Java per MHP. Sono molto simili ad applet: come nelle applet, l’interfaccia Xlet permette ad un software specifico esterno, l’application manager nel caso del ricevitore MHP, di controllarne il ciclo di vita, avviando o fermando l’applicazione. L’interfaccia Xlet si trova nel package javax.tv.xlet. - 28 - Capitolo 3 Applicazioni MHP: le Xlet Come le classi applet, l’interfaccia Xlet contiene i metodi che permettono all’application manager di inizializzare, avviare, mettere in pausa e distruggere una applicazione. Poiché potrebbero esserci più Xlet che vengono eseguite nello stesso momento, esse non possono effettuare alcuni compiti che potrebbero influenzare la Java Virtual Machine; in particolare, una Xlet non deve mai richiamare il metodo ‘System.exit()’ per terminare l’applicazione . La differenza più importante tra applet e Xlet è che una Xlet può essere messa in pausa ed essere riavviata in un secondo momento. La ragione è semplice: nel caso di un ricevitore digitale, diverse applicazioni potrebbero essere eseguite nello stesso momento, mentre le capacità dell’hardware permettono ad una sola applicazione di essere visibile; quelle non visibili perciò devono essere messe in pausa con lo scopo di mantenere le risorse libere per l’unica applicazione mostrata. 3.2 CICLO DI VITA DI UNA XLET Le interfacce javax.tv.xlet.Xlet e javax.tv.xlet.XletContext servono per interagire con l’application manager in merito al ciclo di vita e al contesto in cui la Xlet viene eseguita. Una Xlet di fatto implementa i quattro metodi presenti nell’interfaccia javax.tv.xlet.Xlet: initXlet: questo metodo viene invocato dall’application manager per inizializzare l’Xlet; startXlet: è la funzione che serve per eseguire l’Xlet; pauseXlet: usata per mettere in pausa l’Xlet; destroyXlet: invocata quando l’application manager termina l’Xlet. - 29 - Capitolo 3 Applicazioni MHP: le Xlet Il ciclo di vita di una Xlet è dunque caratterizzato da quattro stati: Loaded: la Xlet viene creata ma non ancora inizializzata. Se durante questa fase viene rilevata un’eccezione, si passa direttamente allo stato Destroyed. Una Xlet può trovarsi in questo stato solo una volta durante tutto il suo ciclo di vita. Paused: la Xlet viene inizializzata, e può trovarsi in questo stato sia dopo che il metodo initXlet ritorna con successo dallo stato Loaded, oppure dopo che il metodo pauseXlet ritorna con successo dallo stato Active. In questo stato, la Xlet deve limitare al massimo l'utilizzo delle risorse condivise e soprattutto non impegnare la GUI televisiva. Active: in questo stato la Xlet è attiva e utilizza le risorse necessarie per fornire i suoi servizi; se è dotata di GUI, allora dovrebbe essere l'unica applicazione abilitata a ricevere gli eventi dal telecomando. Destroyed: in questo stato la Xlet deve rilasciare tutte le risorse in uso per predisporsi alla terminazione. Figura 8: Ciclo di vita di una Xlet Una sequenza tipica del ciclo di vita può essere la seguente: - 30 - Capitolo 3 Applicazioni MHP: le Xlet 1. L’application manager carica la classe principale della Xlet, su segnalazione del broadcaster, e ne crea un’istanza: l’applicazione si trova nello stato Loaded. 2. Quando l’utente sceglie di avviare l’Xlet, o un’altra applicazione segnala che l’Xlet deve partire automaticamente, l’application manager invoca il metodo initXlet. L’Xlet usa il ‘context’ per inizializzarsi, ed eventualmente per richiedere alcune risorse, come le immagini. Quando l’inizializzazione è completata, l’Xlet si trova nello stato Paused, ed è pronta per essere avviata. 3. Una volta che il metodo initXlet ritorna con successo, l’application manager richiama il metodo startXlet. Questo comporta il passaggio dallo stato Paused allo stato Active, così l’Xlet sarà capace di interagire con l’utente ed utilizzare le risorse necessarie per fornire i servizi per cui è stata creata. 4. Durante l’esecuzione, l’application manager può invocare il metodo pauseXlet; questo comporta il passaggio dell’applicazione dallo stato Active allo stato Paused. L’applicazione potrà tornare nello stato Active richiamando il metodo startXlet: questa situazione può accadere svariate volte in tutta la vita di una Xlet. 5. Alla fine del ciclo di vita, l’application manager invoca il metodo destroyXlet, che comporta il passaggio allo stato Destroyed, liberando tutte le risorse. A seguito di questa operazione, l’istanza di questa Xlet non può essere più richiamata. Il costruttore della classe principale di ogni Xlet deve essere lasciato vuoto: quando il middleware avvia un’applicazione, all’inizio crea un’istanza della classe principale. In questo modo invoca il costruttore di default, se esiste, ed esegue il codice contenuto. - 31 - Capitolo 3 Applicazioni MHP: le Xlet In realtà una Xlet possiede un altro metodo che deve essere usato per questo tipo di setup: il metodo initXlet(), che permette un controllo migliore delle operazioni. Tutte le inizializzazioni devono essere fatte nell’implementazione di questo metodo. Durante l’inizializzazione, se qualcosa fallisse verrebbe lanciata un’eccezione, passando direttamente allo stato Destroyed. 3.3 CONTESTO DI UNA XLET Ogni Xlet ha a disposizione un “contesto di applicazione”, l’XletContext, che è un’istanza della classe javax.tv.xlet.XletContext. Questo context è simile alla classe AppletContext associata ad una applet: in entrambi i casi il contesto viene usato dall’applicazione per accedere alle proprietà dell’ambiente circostante e per comunicare i cambiamenti di stato all’application manager. L’interfaccia javax.tv.xlet.XletContext prevede i seguenti metodi: - notifyDestroyed; - notifyPaused; - getXletProperty; - resumeRequest. I metodi notifyDestroyed e notifyPaused permettono ad una Xlet di notificare al decoder che l’applicazione sta per terminare o per mettersi in pausa; in questo modo, il ricevitore conosce lo stato di ogni applicazione e può effettuare le azioni appropriate. Questi metodi devono essere richiamati immediatamente prima che l’Xlet entri negli stati Paused o Destroyed, a causa del fatto che il ricevitore potrebbe effettuare alcune operazioni per le quali l’applicazione non è detto che sia pronta. Una Xlet può richiedere di passare dallo stato Paused allo stato Active usando il metodo resumeRequest. Questo può accadere quando si verifica un certo - 32 - Capitolo 3 Applicazioni MHP: le Xlet evento, rilevato ad esempio nello stream MPEG. Questo metodo richiede che un’applicazione venga fatta partire nuovamente, anche se il software del ricevitore potrebbe scegliere di ignorare questa richiesta a causa di limiti nelle risorse. Il metodo getXletProperty permette alla Xlet di accedere alle proprietà segnalate dal broadcaster. Attualmente è stata definita una sola proprietà da JavaTV e da MHP, XletContext.ARGS, che permette ad un’applicazione di accedere agli argomenti che le vengono segnalati tramite AIT. Di seguito viene mostrato cosa succede quando una Xlet chiede di cambiare il proprio stato tramite l’Xlet context. Si consideri una Xlet che vuole mettersi in pausa e successivamente richiede di riavviarsi. 1. Per prima cosa, l’Xlet notifica al suo Xlet context che si è messa in pausa, invocando il metodo XletContext.notifyPaused(). 2. L’Xlet context inoltra questa informazione all’application manager presente nel middleware. 3. L’application manager aggiorna il suo stato interno per notificare che l’applicazione si è messa in pausa, quindi l’Xlet si ferma. 4. Quando un’applicazione vuole riavviare le operazioni, ad esempio perché è passato un certo tempo prefissato oppure perché l’utente ha premuto un tasto, viene richiamato dalla Xlet il metodo XletContext.requestResume(). 5. Come prima, l’Xlet context passa tale richiesta all’application manager. 6. L’application manager può quindi decidere se riavviare l’applicazione oppure no. Se la riavvia, aggiorna il proprio stato interno per notificare il cambiamento, quindi chiama il metodo startXlet() sulla Xlet: come tutte le altre operazioni che controllano il ciclo di vita della Xlet, questo metodo viene richiamato direttamente dall’application manager e non attraverso l’Xlet context. 7. Infine l’Xlet riavvierà le proprie operazioni. - 33 - Capitolo 3 Applicazioni MHP: le Xlet 3.4 INTERFACCIA GRAFICA DELLE XLET Ogni Xlet deve implementare l’interfaccia Xlet, presente nel package javax.tv.xlet delle API JavaTV di Sun. Per costruire l’interfaccia grafica devono essere importati altri package: - java.awt.*; - java.awt.event.*; - org.havi.ui.*; - org.havi.ui.event.*; - org.dvb.ui.*; - org.dvb.event; Questi package servono per fornire le necessarie funzionalità grafiche ed i rispettivi eventi per implementare l’interfaccia utente. In pratica, il modello grafico definito dallo standard MHP viene supportato dai package sopra citati, tra i quali spicca quello Havi. La classe fondamentale per gestire il layout su schermo è la HScene, presente nei pacchetti forniti da Havi. HSceneFactory è la classe che permette di richiamare l’unica istanza HScene. Il modello grafico definito dallo standard MHP è basato su tre differenti layer. Al livello più basso si trova il Background layer, che può contenere un colore o una immagine statica (rappresentata da un particolare frame MPEG-2). Nel secondo livello è situato il Video layer, rappresentato dal flusso A/V del canale TV o da una qualsiasi altra fonte in formato MPEG-2. Al livello più alto infine si trova il Graphic layer, che contiene la grafica creata nella Xlet, e può essere strutturato su più livelli sovrapposti. - 34 - Capitolo 3 Applicazioni MHP: le Xlet Figura 9: I layer grafici dell'MHP Questo modello grafico è supportato dai package messi a disposizione dallo standard, tra i quali quello più importante viene fornito da Havi. In tale package, il container di più alto livello nella Xlet è rappresentato dalla classe org.havi.ui.HScene, che possiede caratteristiche specifiche per i decoder digitali. La classe che ci permette di richiedere l'unica istanza della HScene è fornita dallo stesso package e si chiama org.havi.ui.HSceneFactory. Oltre ad Havi e AWT è possibile utilizzare un package specifico DVB, org.dvb.ui, dove è possibile trovare classi di utilità come la DvbColor, che permette di gestire anche le trasparenze tramite il parametro alpha, non presente nella classe java.awt.Color. Il font di sistema supportato in MHP è il Tiresias. I formati grafici supportati sono: png, jpg, gif e mpg(I-Frame). Gli eventi associati al telecomando definiti in MHP sono i tasti numerici, le frecce di navigazione, il tasto OK ed i 4 tasti colorati: rosso, verde, giallo e blu. - 35 - Capitolo 3 Applicazioni MHP: le Xlet Lo standard non definisce l’evento per il tasto EXIT, anche se è presente sulla quasi totalità dei decoder, quindi genera degli eventi non-standard e non sempre coerenti sui vari decoder. MHP fornisce due possibilità per la gestione degli eventi nelle Xlet: il classico meccanismo java, fornito in AWT, che si realizza implementando l'interfaccia java.awt.event.KeyListener.; le classi del package org.dvb.event. Le classi principali del package org.dvb.event sono la EventManager e la UserEventRepository, che gestiscono rispettivamente la registrazione degli eventi ed il repository degli eventi a cui si è realmente interessati. Questo approccio ha il vantaggio di non imporre ad una Xlet di richiedere il focus tramite la HScene, come invece prevede il modello AWT. - 36 - CAPITOLO 4 SVILUPPO DI UN’APPLICAZIONE MHP 4.1 PRESENTAZIONE DEL PROGETTO Lo scopo del progetto è quello di realizzare un’applicazione MHP eseguibile su un decoder per la televisione digitale terrestre, sfruttando il canale di ritorno del decoder stesso. Si è pertanto pensato di creare un’applicazione che permetta la prenotazione di posti in un cinema. L’applicazione è stata sviluppata tramite un programma per scrivere il codice Java (Eclipse 3.1.2). Parallelamente la xlet appena scritta veniva verificata su un emulatore del sistema televisivo digitale terrestre: XleTView. Si è, inoltre, utilizzato un database MySQL per la memorizzazione degli utenti e dei posti nelle varie sale. Per la verifica su Set-Top Box si è utilizzato il X75-DEV di ADB, un particolare decoder per lo sviluppo, dotato di una presa seriale per il caricamento delle applicazioni da computer, e di canale di ritorno PSTN ed ETHERNET. Il progetto è stato realizzato utilizzando quest’ultimo. L’applicazione si presenta con un menù di scelta a sinistra, e con lo schermo televisivo al centro in basso. - 37 - Sviluppo di un’applicazione MHP Capitolo 4 Figura 10: Schermata iniziale Con le frecce UP e DOWN del telecomando si scorre il menù, e con il tasto OK si sceglie l’operazione da effettuare. 4.1.1 Registrati Premendo OK sul pulsante REGISTRATI si accede alla seguente schermata. Figura 11: Registrazione - 38 - Sviluppo di un’applicazione MHP Capitolo 4 Con le freccie UP e DOWN si scorrono i campi Username, password, ecc. L’utente può compilare i campi premendo i tasti numerici del telecomando, più volte a seconda della lettera da inserire. La tastiera, infatti, è stata implementata come quella di un cellulare, cioè premendo più volte lo stesso tasto si sceglie una lettera differente. Premendo il pulsante ROSSO, si procede all’inserimento del nuovo utente nel database. E’ necessario, però, compilare tutti i campi. Se ciò non è accaduto e l’utente preme il pulsante ROSSO, verrà visualizzato un messaggio di errore che specifica il campo da compilare. Premendo il tasto VERDE, invece, si torna alla schermata iniziale. 4.1.2 Prenota Premuto OK sul pulsante PRENOTA, si accede ad una schermata di inserimento di username e password, con lo stesso metodo della registrazione. Premendo il tasto ROSSO si verifica che nel database sia presente l’utente inserito. Se ciò non è verificato viene visualizzato un messaggio di errore, altrimenti viene presentata la seguente schermata. Figura 12: Scegli il film - 39 - Sviluppo di un’applicazione MHP Capitolo 4 Con i tasti UP e DOWN è possibile posizionarsi sul film per il quale prenotare il posto, e premendo OK si accede alla schermata che visualizza la mappa della platea, con i posti verdi (liberi) e rossi (occupati). Con le frecce UP, DOWN, RIGHT, LEFT si scorrono i posti, e con il tasto OK si prenota. Verrà poi visualizzato un messaggio per confermare la riuscita o l’errore nella prenotazione. Premendo il tasto BLU, invece, viene aggiornata la mappa dei posti, e con il tasto VERDE si torna alla schermata di partenza. Figura 13: Scegli il posto 4.1.3 Info Premendo OK sul pulsante INFO, si accede ad una schermata che visualizza semplicemente alcune informazioni utili sul cinema. - 40 - Sviluppo di un’applicazione MHP Capitolo 4 Figura 14: Info 4.2 REALIZZAZIONE DELLA XLET 4.2.1 Il JDBC Il cuore del progetto sta nella connessione della Xlet al database MySQL. Con il java, ciò è possibile, tramite il JDBC (Java DataBase Connectivity), che è un middleware per database che consente l'accesso alle basi di dati da qualsiasi programma scritto con il linguaggio di programmazione Java, indipendentemente dal tipo di DBMS utilizzato. È costituita da una API, raggruppata nel package java.sql, che serve ai client per connettersi a un database. Fornisce metodi per interrogare e modificare i dati. È orientata ai database relazionali ed è Object Oriented. La piattaforma Java 2 Standard Edition contiene le API JDBC, insieme all'implementazione di un bridge JDBC-ODBC, che permette di connettersi a database relazionali che supportino ODBC. Il funzionamento è il seguente: - 41 - Sviluppo di un’applicazione MHP Capitolo 4 Il metodo Class.forName() carica la classe del driver JDBC. La linea seguente carica il driver per mioDbms nell'applicazione. Class.forName( "com.mioDbms.mioDriver" ); Poi, il metodo DriverMangager.getConnection() crea una connessione. Connection conn = DriverManager.getConnection( "jdbc:mioDbms:altri dati utili per il driver", "mioLogin", "miaPassword" ); La stringa da utilizzare dipende dal driver JDBC che useremo. Inizia sempre con "jdbc:", il resto varia a seconda del prodotto scelto. Una volta stabilita la connessione, occorre passare una istruzione. Statement stmt = conn.createStatement(); stmt.executeUpdate( "INSERT INTO miaTabella( nome ) VALUES ( 'roberto' ) " ); I dati vengono prelevati dal database col classico meccanismo delle query. L'esempio sottostante mostra come creare ed eseguire un'interrogazione: Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery( "SELECT * FROM miaTabella" ); while ( rs.next() ) { int numeroColonne = rs.getMetaData().getColumnCount(); for ( int i = 1 ; i <= numeroColonne ; i++ ) { // I numeri di colonna iniziano da 1. // Vi sono diversi metodi che convertono il valore di una colonna //in un certo tipo. // Vedi la documentazione per una lista delle conversioni valide. System.out.println( "COLONNA " + i + " = " + rs.getObject(i) ); } - 42 - Sviluppo di un’applicazione MHP Capitolo 4 } rs.close(); stmt.close(); E’ stato però riscontrato e verificato un grande problema: lo standard MHP non supporta il JDBC. 4.2.2 Le socket Per ovviare al problema sopra citato si è deciso di ricorrere alla seguente soluzione: nello stesso PC in cui c’è il database MySQL viene fatto girare un server (sempre scritto in Java), che fa da tramite tra l’applicazione MHP e il database. Il server accede al database tramite il JDBC, e la Xlet accede al server per mezzo delle socket. Per realizzare un canale di comunicazione Client/Server occorre la creazione di un programma Server che funge da ascolto su una determinata porta, e di un programma Client che, remotamente, lo invochi. Per la creazione del programma Server in Java è necessario seguire i seguenti passaggi: Importare i package java.io e java.net per poter utilizzare le classi Socket e ServerSocket Intercettare l'eccezione IOException che deve essere gestita (tramite try e catch) o propagata (tramite throws), dato che vengono richiamati metodi delle classi che appartengono ai package java.io e java.net. Creare un oggetto ServerSocket, utilizzando il costruttore ServerSocket(int port) che si aspetta come parametro la porta del Server in ascolto per il servizio da attivare. Ad esempio: ServerSocket server = new ServerSocket(9999); - 43 - Sviluppo di un’applicazione MHP Capitolo 4 Da notare che il numero corrispondente alla porta di ascolto non è una stringa. Utilizzare il metodo accept() dell'oggetto ServerSocket per poter accettare le chiamate Client su un oggetto Socket: Socket client = server.accept(); Il metodo che viene invocato ritorna un'oggetto utilizzato Socket che potrà essere per le comunicazioni con il client. Creare due oggetti DataInputStream e DataOutputStream per attivare uno stream di comunicazione con il cliente (ovviamente il DataInputStream servirà per leggere le richieste del client, e il DataOutputStream per inviare risposte al client): DataInputStream is = new DataInputStream(server.getInputStream()); DataOutputStream os = new DataOutputStream( server.getOutputStream()); Richiamare il metodo close() sugli oggetti dello stream e sull'oggetto ServerSocket per chiudere i canali di comunicazione: os.close(); is.close(); server.close(); A livello client i passaggi sono i seguenti: Importare i package java.io, java.net per utilizzare le classi Stream (per i canali di comunicazione) e Socket (per il nostro scopo) . Intercettare l'eccezione IOException che deve essere gestita (tramite try e catch) o propagata (tramite throws), dato che vengono richiamati metodi delle classi che appartengono ai package java.io e java.net. Creare un oggetto Socket e specificare l'indirizzo IP ed il numero di porta in ascolto sul server, caratteristiche imposte dal TCP per la comunicazione - 44 - Sviluppo di un’applicazione MHP Capitolo 4 server/client. Ad esempio, per connettersi al server descritto negli esempi sopra, si deve effettuare un'inizializzazione nel seguente modo: Socket client = new Socket("localhost",9999); A livello client l'indirizzo IP viene ricavato mentre la porta viene assegnata in modo dinamico in base alla disponibilità. Creare il canale di comunicazione con il server per inviare e ricevere messaggi tramite gli Stream di Byte in input ed output tramite le classi DataInputStream e DataOutputStream, associandole ai metodi getInputStream e getOutputStream della classe Socket. DataInputStream is = new DataInputStream(client.getInputStream()); DataOutputStream os = new DataOutputStream( client.getOutputStream()); Chiudere gli stream di comunicazione e l'oggetto socket. is.close(); os.close(); client.close(); 4.3 IL SERVER Il server è composto dai seguenti file: SimpleServer.java, ServerRegistration.java, ServerPrenotaSingolo.java, ObjectRegistration, ServerFilm.java, ObjectPrenotation, ServerPrenotation.java, PrenotaSingolo.java, ObjectPrenFilm, ObjectPosti, ObjectFilm. Il server funziona nel seguente modo: SimpleServer è la classe che va in esecuzione. Il server si mette in ascolto, attendendo che la Xlet gli mandi un oggetto, che viene poi letto. A seconda del tipo di oggetto ricevuto, il server chiama la classe relativa. - 45 - Sviluppo di un’applicazione MHP Capitolo 4 4.3.1 SimpleServer.java import java.net.*; import java.io.*; public class SimpleServer { private int port; private ServerSocket server; private Object obj; private String ris; private Object film; private Object posti; private Object prenotaS; public SimpleServer (int port){ ris=""; this.port = port; if(!startServer()) System.err.println("Errore durante la creazione del Server"); } private boolean startServer(){ try{ server = new ServerSocket(port); }catch (IOException ex){ ex.printStackTrace(); return false; } System.out.println("Server creato con successo!"); - 46 - Sviluppo di un’applicazione MHP Capitolo 4 return true; } //metodo che viene chiamato quando va in esecuzione il server public void runServer(){ while (true){ try{ // Il server resta in attesa di una richiesta System.out.println("Server in attesa di richieste..."); Socket s1 = server.accept(); System.out.println("Un client si e' connesso..."); InputStream is = s1.getInputStream(); ObjectInputStream ois = new ObjectInputStream(is); try{ //viene letto l’oggetto ricevuto obj=(Object)(ois.readObject()); System.out.println("oggetto letto"); } catch (ClassNotFoundException e) { System.out.println("Error: "+e.getMessage()); } // a seconda del tipo di oggetto letto, viene chiamata la // classe relativa if (obj instanceof ObjectRegistration) { ris=(new ServerRegistration((ObjectRegistration)obj)).start(); OutputStream s1out = s1.getOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(s1out); // Il server invia la risposta al client oos.writeObject(ris); // Chiude lo stream di output e la connessione - 47 - Sviluppo di un’applicazione MHP Capitolo 4 oos.close(); } if(obj instanceof ObjectPrenotation) { film=(new ServerFilm((ObjectPrenotation)obj)).start(); OutputStream s1out = s1.getOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(s1out); oos.writeObject(film); oos.close(); } if (obj instanceof PrenotaSingolo) { prenotaS=(new ServerPrenotaSingolo(( PrenotaSingolo)obj)).start(); OutputStream s1out = s1.getOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(s1out); oos.writeObject(prenotaS); oos.close(); } if (obj instanceof ObjectPrenFilm) { posti=(new ServerPrenotation((ObjectPrenFilm)obj)).start(); OutputStream s1out = s1.getOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(s1out); oos.writeObject(posti); oos.close(); } s1.close(); System.out.println("Chiusura connessione effettuata\n"); }catch (IOException ex){ ex.printStackTrace(); } - 48 - Sviluppo di un’applicazione MHP Capitolo 4 } } public static void main(String[] args) { SimpleServer ss = new SimpleServer(8000); ss.runServer(); } } 4.3.2 ServerRegistration.java Questo file si occupa della fase di registrazione: legge dall’oggetto ObjectRegistration i dati necessari, si connette al database MySQL e inserisce nella tabella Cliente i dati del nuovo utente (a meno che non sia già presente nel database). import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.Statement; public class ServerRegistration { private ObjectRegistration obj; private String ris; public ServerRegistration(ObjectRegistration object) { this.obj=object; ris=""; } - 49 - Sviluppo di un’applicazione MHP Capitolo 4 public String start() { //Crea la connessione con il database MySQL try { Class.forName("com.mysql.jdbc.Driver"); System.out.println("eseguito class.forname"); } catch (ClassNotFoundException e1){} try{ String user="talax"; String pass="gufo"; String url="jdbc:mysql://localhost/cinema"; Connection con = DriverManager.getConnection(url,user,pass); Statement stmt=con.createStatement(); System.out.println(((ObjectRegistration)obj).getUser()); //Controlla che l’utente da inserire non sia già presente. String query= "SELECT user FROM cliente WHERE (user='"+((ObjectRegistration)obj).getUser()+"');"; ResultSet rm=stmt.executeQuery(query); while (rm.next()) { ris=rm.getString(1); } //Se l’utente non è presente, si procede all’inserimento. if (ris=="") { String query2= "INSERT INTO cliente (user,password,nome,cognome,data_n, citta,indirizzo) VALUES ('"+(( ObjectRegistration)obj).getUser()+ - 50 - Sviluppo di un’applicazione MHP Capitolo 4 "','"+((ObjectRegistration)obj).getPassword() +"','"+((ObjectRegistration)obj).getNome() +"','"+((ObjectRegistration)obj).getCognome ()+"','"+((ObjectRegistration)obj).getData()+ "','"+((ObjectRegistration)obj).getCitta()+ "','"+((ObjectRegistration)obj).getIndirizzo() +"');"; stmt.executeUpdate(query2); ris="Registrazione effettuata con successo!"; System.out.println("query effettuata"); } else { ris="Username esistente."; } rm.close(); stmt.close(); con.close(); } catch(Exception exc) { ris="Errore!"; } return ris; } } 4.3.3 ServerFilm.java Questo file si occupa della prima parte della prenotazione. Ha il compito di controllare se username e password inseriti dall’utente sono corretti. - 51 - Sviluppo di un’applicazione MHP Capitolo 4 import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class ServerFilm { private ObjectPrenotation obj; private Object film; public ServerFilm(ObjectPrenotation obj) { this.obj=obj; } public Object start() { try { Class.forName("com.mysql.jdbc.Driver"); System.out.println("eseguito class.forname"); } catch (ClassNotFoundException e1) {film="Errore nella connessione\nal database";} try{ String user="talax"; String pass="gufo"; String url="jdbc:mysql://localhost/cinema"; Connection con = DriverManager.getConnection(url,user,pass); Statement stmt=con.createStatement(); - 52 - Sviluppo di un’applicazione MHP Capitolo 4 //Controlla che user e password siano corretti. String query="SELECT user FROM cliente WHERE user= '"+obj.getUser()+"' AND password='"+ obj.getPassword()+"';"; ResultSet rm=stmt.executeQuery(query); String ris1=""; while(rm.next()) { ris1=ris1+rm.getString(1); } rm.close(); if (ris1=="") { System.out.println("user pwd errati"); film="Username o password errati."; } else { /* Se user e password sono corretti, viene creato l’oggetto ObjectFilm contenente la lista dei film del cinema */ System.out.println("user pwd esatti"); String query2= "SELECT nome FROM film;"; ResultSet rm2=stmt.executeQuery(query2); System.out.println("eseguita query2"); String[] nome=new String[4]; int i=0; while (rm2.next()) { nome[i]=rm2.getString(1); System.out.println("nome film: "+nome[i]); - 53 - Sviluppo di un’applicazione MHP Capitolo 4 i++; } rm2.close(); film=new ObjectFilm(nome); } } catch (SQLException exc) { film="Errore nella prenotazione"; System.out.println("SQLException: "+exc.getMessage()); } return film; } } 4.3.4 ServerPrenotation.java Questa classe si occupa della seconda parte della prenotazione. Viene recuperata dal database la mappa dei posti relativi al film scelto. import java.sql.*; public class ServerPrenotation { private ObjectPrenFilm obj; private Object posti; private int codice; public ServerPrenotation(ObjectPrenFilm obj) { this.obj=obj; } public Object start() { try { - 54 - Sviluppo di un’applicazione MHP Capitolo 4 Class.forName("com.mysql.jdbc.Driver"); System.out.println("eseguito class.forname"); } catch (ClassNotFoundException e1) {posti="Errore nella connessione\nal database";} try{ String user="talax"; String pass="gufo"; String url="jdbc:mysql://localhost/cinema"; Connection con = DriverManager.getConnection(url,user,pass); Statement stmt=con.createStatement(); System.out.println("user pwd esatti"); codice=this.obj.getCodice(); String query2= "SELECT count(*) FROM posti_"+codice+";"; ResultSet rm2=stmt.executeQuery(query2); System.out.println("eseguita query2"); int length=0; while (rm2.next()) { length=Integer.parseInt(rm2.getString(1)); System.out.println("length: "+length); } rm2.close(); String query3= "SELECT * FROM posti_"+codice+";"; ResultSet rm3=stmt.executeQuery(query3); int[] num=new int[length]; String[] utente=new String[length]; - 55 - Sviluppo di un’applicazione MHP Capitolo 4 int[] libero=new int[length]; int j=0; while (rm3.next()) { utente[j]=rm3.getString(1); System.out.println(utente[j]); num[j]=Integer.parseInt(rm3.getString(2)); System.out.println(num[j]); libero[j]=Integer.parseInt(rm3.getString(3)); System.out.println(libero[j]); j++; } posti=new ObjectPosti(utente,num,libero,codice); rm3.close(); stmt.close(); con.close(); } catch(SQLException exc) { posti="Errore nella prenotazione"; System.out.println("SQLException: "+exc.getMessage()); } return posti; } } - 56 - Sviluppo di un’applicazione MHP Capitolo 4 4.3.5 ServerPrenotaSingolo.java Questo file consente di prenotare un singolo posto in platea, aggiornando il database relativo. import java.sql.*; public class ServerPrenotaSingolo { private PrenotaSingolo obj; private String ris; public ServerPrenotaSingolo(PrenotaSingolo obj) { this.obj=obj; } public Object start() { ris=""; try { Class.forName("com.mysql.jdbc.Driver"); System.out.println("eseguito class.forname"); } catch (ClassNotFoundException e1){} try{ String user="talax"; String pass="gufo"; String url="jdbc:mysql://localhost/cinema"; Connection con = DriverManager.getConnection(url,user,pass); Statement stmt=con.createStatement(); // Aggiorna il database dei posti in platea // (0=occupato, 1=libero) - 57 - Sviluppo di un’applicazione MHP Capitolo 4 String query= "UPDATE posti_"+obj.getCodice()+ " SET libero=0 ,utente='"+obj.getUser()+"' WHERE num_posto="+obj.getNumero()+";"; stmt.executeUpdate(query); ris="Prenotazione eseguita con successo!"; stmt.close(); con.close(); } catch (SQLException e) { System.out.println("Eccezione: "+e.getMessage()); ris="Errore prenotazione"; } return ris; } } 4.3.6 PrenotaSingolo.java Questo file rappresenta l’oggetto che viene passato a ServerPrenotaSingolo, e contiene il nome dell’utente, il numero del posto e il codice della platea. import java.io.*; public class PrenotaSingolo implements Serializable { private String user; private int numero; private int codice; public PrenotaSingolo (String user, int numero, int codice) { this.user=user; - 58 - Sviluppo di un’applicazione MHP Capitolo 4 this.numero=numero; this.codice=codice; } public String getUser() { return user; } public int getNumero() { return numero; } public int getCodice() { return codice; } } 4.3.7 ObjectRegistration Questo oggetto viene passato a ServerRegistration.java, e contiene tutti i dati dell’utente da inserire nel database. import java.io.*; public class ObjectRegistration implements Serializable{ private String user; private String password; private String nome; private String cognome; - 59 - Sviluppo di un’applicazione MHP Capitolo 4 private String data; private String citta; private String indirizzo; public ObjectRegistration(String u, String pwd, String nom, String cog, String data, String citta, String ind) { this.user=u; this.password=pwd; this.nome=nom; this.cognome=cog; this.data=data; this.citta=citta; this.indirizzo=ind; System.out.println("creato objectRegistration"); } public String getUser() { return this.user; } public String getPassword() { return this.password; } public String getNome() { return this.nome; - 60 - Sviluppo di un’applicazione MHP Capitolo 4 } public String getCognome() { return this.cognome; } public String getData() { return this.data; } public String getCitta() { return this.citta; } public String getIndirizzo() { return this.indirizzo; } } 4.3.8 ObjectPrenotation.java Questo oggetto viene passato a ServerFilm.java. Contiene semplicemente user e password per fare il login. import java.io.*; public class ObjectPrenotation implements Serializable { private String user; private String password; - 61 - Sviluppo di un’applicazione MHP Capitolo 4 public ObjectPrenotation (String user, String password){ this.user=user; this.password=password; } public String getUser() { return this.user; } public String getPassword() { return this.password; } } 4.3.9 ObjectFilm.java Questo oggetto viene creato in ServerFilm.java. Contiene la lista di film disponibili. import java.io.*; public class ObjectFilm implements Serializable{ private String[] nome; public ObjectFilm(String[] nome) { this.nome=nome; } public String getNome(int i) { return nome[i]; } - 62 - Sviluppo di un’applicazione MHP Capitolo 4 } 4.3.10 ObjectPosti.java Questo oggetto viene creato in ServerPrenotation.java, e contiene la mappa dei posti in platea, con nome utente, numero del posto, flag per sapere se il posto è libero o occupato, e codice della platea. import java.io.*; public class ObjectPosti implements Serializable{ private String[] utente; private int[] num_posto; private int[] libero; private int codice; public ObjectPosti(String[] utente, int[] num, int[] libero,int codice) { this.num_posto=num; this.libero=libero; this.utente=utente; this.codice=codice; } public String getUtente(int i) { return utente[i]; } public int getNumero(int i) { return num_posto[i]; } public int getLibero(int i) { return libero[i]; - 63 - Sviluppo di un’applicazione MHP Capitolo 4 } public int getLength() { return utente.length; } public int getCodice() { return codice; } } 4.3.11 ObjectPrenFilm.java Questo oggetto viene passato a ServerPrenotation.java, e contiene il codice della platea. import java.io.*; public class ObjectPrenFilm implements Serializable{ private int codice; public ObjectPrenFilm(int codice) { this.codice=codice; } public int getCodice() { return codice; } } - 64 - Sviluppo di un’applicazione MHP Capitolo 4 4.4 LA XLET La Xlet contiene i seguenti file sorgenti: BackgroundController.java, Comunica.java, Film.java, InterfacciaGrafica.java, GestoreSfondi.java, Keypad.java, KeypadButton.java, Info.java, Main.java Platea.java, PlateaPrenota.java, Prenota.java, Registra.java, Strumenti.java, ObjectFilm.java, ObjectPosti.java, ObjectPrenFilm.java, ObjectPrenotation.java, ObjectRegistration.java, PrenotaSingolo.java (dove questi ultimi sei file sono gli stessi presenti nel server). Il file principale, cioè quello che implementa Xlet, è Main.java. 4.4.1 Main.java import java.awt.event.*; import javax.tv.xlet.Xlet; import javax.tv.xlet.XletContext; import javax.tv.xlet.XletStateChangeException; import org.havi.ui.*; import org.havi.ui.event.*; //Classe iniziale è questa che implementa l'interfaccia Xlet e KeyListener e //che quindi sarà utilizzata dall'application manager per gestire il //ciclo di vita della xlet. Si occupa di inizializzare i vari componenti, //ascoltare gli eventi dal telecomando, creare l'oggetto scene fondamentale //per la parte grafica. public class Main implements Xlet, KeyListener { - 65 - Sviluppo di un’applicazione MHP Capitolo 4 private XletContext context; public static GestoreSfondi gestoreSfondi; private HScene scene; public int tastoPremuto = 0; private InterfacciaGrafica interfaccia; public static String user; public static String password; public static String numero; public static String ip; //Costruttore vuoto. public Main() { } // Definisco un metodo che inizializza la Xlet, e i suoi componenti. public void initXlet(XletContext xletContext) throws XletStateChangeException { //xletContext utilizzato per la gestione del ciclo di vita della xlet context = xletContext; HSceneFactory hsceneFactory = HSceneFactory.getInstance(); //estraggo l'oggetto scene su cui si appoggiano i componenti grafici. scene = hsceneFactory.getFullScreenScene( HScreen.getDefaultHScreen().getDefaultHGraphicsDevice()); //le dimensioni sono quelle standard di risoluzione di un televisore // analogico. scene.setSize(720, 576); - 66 - Sviluppo di un’applicazione MHP Capitolo 4 scene.setLayout(null); //aggiungo l'ascoltatore degli eventi in questo caso la classe stessa. scene.addKeyListener((KeyListener)this); gestoreSfondi = new GestoreSfondi(context); interfaccia = new InterfacciaGrafica(scene); user="user"; password="password"; numero="000000000"; ip="localhost"; //indirizzo del computer in cui è in esecuzione il server } // Definisco il metodo che permette di mandare in esecuzione la Xlet public void startXlet() throws XletStateChangeException { System.out.println("Eseguo startXlet"); scene.setVisible(true); scene.requestFocus(); interfaccia.disegnaInit(); gestoreSfondi.displayBackgroundInitImage(); } // Definisco il metodo per mettere in pausa la Xlet public void pauseXlet() { System.out.println("Xlet in pausa"); context.notifyPaused(); } // Definisco il metodo che distrugge la Xlet e rilascia le risorse. - 67 - Sviluppo di un’applicazione MHP Capitolo 4 public void destroyXlet(boolean flag) throws XletStateChangeException { if(flag){ System.out.println("Distruggi Xlet"); interfaccia.distruggi(); //rimuovo l'ascoltatore degli eventi. scene.removeKeyListener(this); scene.removeKeyListener((KeyListener)this); scene.setVisible(false); HSceneFactory.getInstance().dispose(scene); scene = null; gestoreSfondi.displayBackgroundExit(); context.notifyDestroyed(); } } // Ascoltatore degli eventi legati al telecomando. Gli eventi vengono //passati alla classe InterfacciaGrafica che poi li passerà alle //funzioni attive. public void keyPressed (KeyEvent key) { int pulsantePremuto = key.getKeyCode(); switch(pulsantePremuto){ //Tastierino numerico case KeyEvent.VK_0: case KeyEvent.VK_1: case KeyEvent.VK_2: case KeyEvent.VK_3: case KeyEvent.VK_4: - 68 - Sviluppo di un’applicazione MHP Capitolo 4 case KeyEvent.VK_5: case KeyEvent.VK_6: case KeyEvent.VK_7: case KeyEvent.VK_8: case KeyEvent.VK_9: case 520: //Tasti colorati case HRcEvent.VK_COLORED_KEY_0: //rosso case HRcEvent.VK_COLORED_KEY_1: //verde case HRcEvent.VK_COLORED_KEY_2: //giallo case HRcEvent.VK_COLORED_KEY_3: //blu //Tasti direzionali case HRcEvent.VK_UP: case HRcEvent.VK_DOWN: case HRcEvent.VK_RIGHT: case HRcEvent.VK_LEFT: case HRcEvent.VK_ENTER: //OK //Vengono passati gli eventi. interfaccia.keyPressed(key); break; //Alla pressione del tasto EXIT del telecomando si //richiama il metodo destroyXlet, per fermare la xlet. case HRcEvent.VK_ESCAPE: try{ destroyXlet(true); } catch (XletStateChangeException xsce){ System.out.println(xsce.toString()); - 69 - Sviluppo di un’applicazione MHP Capitolo 4 } break; default: break; } } public void keyTyped(KeyEvent ignored) { } public void keyReleased(KeyEvent ignored) { } } 4.4.2 BackgroundController.java // Importo le classi HAVi UI necessarie import org.havi.ui.*; // Poiché alcune configurazioni grafiche richiedono risorse scarse, // abbiamo bisogno di usare le DAVIC resource notification API import org.davic.resources.*; import javax.tv.service.selection.*; import javax.tv.media.AWTVideoSizeControl; import javax.tv.media.AWTVideoSize; import java.awt.Rectangle; import javax.tv.xlet.XletContext; - 70 - Sviluppo di un’applicazione MHP Capitolo 4 /** * Questa classe gestisce la visualizzazione di immagini di sfondo in un * semplice modo d’uso. */ class BackgroundController implements org.davic.resources.ResourceClient{ // Il background device che useremo per visualizzare l’immagine private HBackgroundDevice backdevice; // La configurazione per il background device private HStillImageBackgroundConfiguration backconfig; /** * Questo metodo inizializzerà i dispositivi video e grafici per * permettere loro di visualizzare l’immagine di sfondo. */ public boolean init() { // Per prima cosa abbiamo bisogno di prendere il background // device che useremo per visualizzare l’immagine. // Per fare questo, abbiamo bisogno di decidere prima quale // HScreen utilizzeremo. In questo caso, useremo quello di // default. HScreen screen = HScreen.getDefaultHScreen(); // Una volta che l’abbiamo, prendiamo il background device di // default per quello HScreen. HBackgroundDevice backdevice = screen.getDefaultHBackgroundDevice(); - 71 - Sviluppo di un’applicazione MHP Capitolo 4 // Una volta che abbiamo una referenza al dispositivo, possiamo // ottenere una configurazione per esso. HBackgroundConfigTemplate backgroundConfigurationTemplate = new HBackgroundConfigTemplate(); backgroundConfigurationTemplate.setPreference( HBackgroundConfigTemplate.FLICKER_FILTERING, HBackgroundConfigTemplate.REQUIRED); HBackgroundConfiguration backconfig; backconfig = backdevice.getBestConfiguration( backgroundConfigurationTemplate); // Possiamo riservare il dispositivo così che possiamo cambiare i // settaggi su di esso? if (backdevice.reserveDevice(this)) { // Se possiamo, settiamo la configurazione del background // device al primo che abbiamo preso sopra – questà è la // configurazione di default per questo dispositivo. try { backdevice.setBackgroundConfiguration( backconfig); } catch (Exception ex) { System.out.println("Can't initialise the background device"); - 72 - Sviluppo di un’applicazione MHP Capitolo 4 //Rilascia il dispositivo così che altre applicazioni // possano usarlo, se necessario. backdevice.releaseDevice(); return false; } // Dobbiamo verificare se possiamo mettere un’immagine // di sfondo, in questa configurazione, dato che non // possiamo farlo in qualsiasi configurazione. if(backconfig instanceof HStillImageBackgroundConfiguration) { // Possiamo usare questo. this.backconfig = (HStillImageBackgroundConfiguration)backconfig; this.backdevice = backdevice; return true; } else { // Se non possiamo, rilasciamo ancora il dispositivo // finché non lo usiamo. backdevice.releaseDevice(); } } return false; } - 73 - Sviluppo di un’applicazione MHP Capitolo 4 /** * Libera le risorse di cui abbiamo bisogno per visualizzare immagini * di sfondo. Alcune implementazioni lasciano l’immagine lì, ma c’è un * pericolo esplicito nelle specifiche MHP che questo non sia possibile * in tutte le implementazioni. Se vogliamo essere sicuri che la nostra * immagine sia ancora visibile, non facciamo questo. */ public void dispose(XletContext context) { // Controlla che abbiamo qualcosa da disporre if (backdevice != null) { this.hideVideo(context,0,0,720,576); // Rilascia il dispositivo e cancella ogni referenza. backdevice.releaseDevice(); backdevice = null; backconfig = null; } } /** * Visualizza un’immagine di sfondo. */ public void display(String filename) { - 74 - Sviluppo di un’applicazione MHP Capitolo 4 // Controlla che abbiamo le risorse di cui necessitiamo per // visualizzare l’immagine di sfondo. if(backconfig != null) { // Crea una nuova immagine di sfondo. L’immagine è // caricata dal filename che abbiamo passato. HBackgroundImage backimage = new HBackgroundImage(filename); // Ora visualizza l’immagine. Ciò può generare parecchie // eccezioni, così dobbiamo mettere il codice in un blocco //‘try’. try { backconfig.displayImage(backimage); } catch (java.io.IOException ioe) { System.out.println("Can't display background image – IO exception"); ioe.printStackTrace(); } catch (HPermissionDeniedException hpde) { System.out.println("Can't display background image – permission denied"); } catch (HConfigurationException hce) { System.out.println("Can't display background image – configuration exception"); hce.printStackTrace(); } - 75 - Sviluppo di un’applicazione MHP Capitolo 4 } } /** * Nasconde il video che potrebbe oscurare il nostro sfondo */ public void hideVideo(XletContext context,int x, int y, int larg, int alt) { // Lo sfondo potrebbe essere nascosto dal video quando parte la // Xlet. Per risolvere questo problema, dobbiamo fare due cose: // 1) fermare il video // 2) ridimensionare il video così che possiamo vedere // l’applicazione sotto ad esso. Questo non è sempre // necessario, ma alcuni emulatori non nascondo il video // quando lo fermiamo. // Prende una referenza al JavaTV ServiceContextFactory ServiceContextFactory factory; factory = ServiceContextFactory.getInstance(); // Da ciò, possiamo ottenere una referenza al service context // genitore della nostra Xlet. Per fare questo, abbiamo bisogno di // una referenza al context della nostra Xlet. ServiceContext myContext = null; try { myContext = factory.getServiceContext(context); } catch (Exception e) { e.printStackTrace(); - 76 - Sviluppo di un’applicazione MHP Capitolo 4 } if (myContext != null) { // Gli oggetti ServiceContentHandler sono responsabili per // presentare le diverse parti del servizio. Ciò include i // component multimediali. ServiceContentHandler[] handlers; handlers = myContext.getServiceContentHandlers(); for(int i=0; i < handlers.length ; i++) { if (handlers[i] instanceof ServiceMediaHandler) { // Questa è la parte Player del servizio, visto che gli // oggetti ServiceMediaHandler sono istanze degli // oggetti JMF Player. javax.media.Player p = (javax.media.Player) handlers[i]; // potremmo aver bisogno di tutto questo per veder il // nostro sfondo… // p.stop(); // p.deallocate(); // ...ma alcuni emulatori richiedono di più AWTVideoSizeControl awtVideoSizeControl; awtVideoSizeControl = (AWTVideoSizeControl) p.getControl(" javax.tv.media.AWTVideoSizeControl"); // in questo caso, impostiamo la dimensione del // video a 0, ma potremo settarla per visualizzare - 77 - Sviluppo di un’applicazione MHP Capitolo 4 // una parte dello schermo. awtVideoSizeControl.setSize(new AWTVideoSize (new Rectangle(0, 0, 720, 576), new Rectangle(x,y,larg,alt))); } } } else { System.out.println("Can't get our service context. May not be able to " + "resize the video, so our background may not be visible"); } } /********************************************************* * * Questi metodi sono ereditati dall’interfaccia ResourceClient e sono * usati per dire all’applicazione quando ha perso l’accesso alle sue * risorse. */ /** * Questo metodo viene chiamato quando il gestore di risorse richiede * che concedere la risorsa. Possiamo rifiutare di fare così, ed è quello * che facciamo in questo caso. */ - 78 - Sviluppo di un’applicazione MHP Capitolo 4 public boolean requestRelease(ResourceProxy proxy, Object requestData) { return false; } /** * Questo metodo viene chiamato quando il gestore di risorse ci * informa che dobbiamo rilasciare una risorsa. */ public void release(ResourceProxy proxy) { // rilascia il controllo del background device backdevice.releaseDevice(); } /** * Questo metodo viene chiamato quando il gestore di risorse ci dice * che abbiamo perso l’accesso ad una risorsa. */ public void notifyRelease(ResourceProxy proxy) { // rilascia il controllo del background device. Sebbene non // abbiamo il suo controllo, questo ci rende sicuri che tutto è // sincronizzato. backdevice.releaseDevice(); } } - 79 - Sviluppo di un’applicazione MHP Capitolo 4 4.4.3 GestoreSfondi.java import javax.tv.xlet.XletContext; //Gestisce gli sfondi della xlet. //Utilizzato dalla classe principale "Main". I metodi si appoggiano alla //classe BackgroundController per modificare l'immagine in background. public class GestoreSfondi { //Context della xlet in esecuzione. private XletContext xContext; private BackgroundController backgroundManager; public GestoreSfondi(XletContext context){ backgroundManager = new BackgroundController(); xContext=context; } // Carica l'immagine di sfondo della xlet. public void displayBackgroundInitImage() { if (backgroundManager.init()) { backgroundManager.hideVideo(xContext,230,300,300,212); backgroundManager.display("background2.mpg"); } } // Carica lo sfondo di uscita della Xlet. - 80 - Sviluppo di un’applicazione MHP Capitolo 4 public void displayBackgroundExit() { backgroundManager.dispose(xContext); } public void cutVideo() { if (backgroundManager.init()) { backgroundManager.hideVideo(xContext,54,300,0,0); backgroundManager.display("background2.mpg"); } } } 4.4.4 InterfacciaGrafica.java import java.awt.Color; import java.awt.Font; import java.awt.Toolkit; import java.awt.event.KeyEvent; import org.havi.ui.*; import org.havi.ui.event.HRcEvent; //Questa classe amministra la parte grafica del frame //principale gestendo l'accesso alle varie funzionalità e //passando gli eventi ai processi attivi. public class InterfacciaGrafica { private HScene scene; public static HScene scene2; private HIcon[] b_1; - 81 - Sviluppo di un’applicazione MHP Capitolo 4 private HIcon[] ba_1; private HText mess; private String [] messaggio=new String[3]; public static Keypad keypad; final int su = 1 ; final int giu = 2 ; final int frame1=1; final int frame2=2; private int frameattivo=0; private Prenota prenota; private Registra registra; private Info setup; //Utilizzata per il polimorfismo. private Strumenti strumento=null; //Costruttore. public InterfacciaGrafica(HScene scenemain){ //viene passato l'oggetto scene su cui verranno //aggiunti i vari componenti grafici. scene=scenemain; //scene2=scenemain; messaggio[0]="Riempi tutti i campi e conferma i dati."; messaggio[1]="Fai il login, scegli il film da vedere e il posto nella sala."; messaggio[2]="Leggi le informazioni sul Cinema Italia."; } - 82 - Sviluppo di un’applicazione MHP Capitolo 4 //Crea i componenti grafici e li visualizza. public void disegnaInit(){ mess = new HText (messaggio[0],210,100,450,50); mess.setFont(new Font("Arial",Font.PLAIN,20)); mess.setHorizontalAlignment(HVisible.HALIGN_LEFT); mess.setForeground(Color.white); mess.setBordersEnabled(false); mess.setBackgroundMode(HVisible.NO_BACKGROUND_FILL); scene.add(mess); //Crea la tastiera keypad=new Keypad(scene); keypad.setVisible(false); scene.add(keypad); b_1=new HIcon[3]; ba_1=new HIcon[3]; b_1[0]=new HIcon(Toolkit.getDefaultToolkit().getImage( "registra_off_g.png"), 50, 100, 150, 50); b_1[0].setVisible(false); scene.add(b_1[0]); b_1[1]=new HIcon(Toolkit.getDefaultToolkit().getImage( "prenota_off_g.png"), 50, 160, 150, 50); b_1[1].setVisible(true); scene.add(b_1[1]); b_1[2]=new HIcon(Toolkit.getDefaultToolkit().getImage( "info_off.png"),50,220,150,50); b_1[2].setVisible(true); - 83 - Sviluppo di un’applicazione MHP Capitolo 4 scene.add(b_1[2]); ba_1[0]=new HIcon(Toolkit.getDefaultToolkit().getImage( "registra_on_g.png"), 50, 100, 150, 50); ba_1[0].setVisible(true); scene.add(ba_1[0]); ba_1[1]=new HIcon(Toolkit.getDefaultToolkit().getImage( "prenota_on_g.png"), 50, 160, 150, 50); ba_1[1].setVisible(false); scene.add(ba_1[1]); ba_1[2]=new HIcon(Toolkit.getDefaultToolkit().getImage( "info_on.png"),50,220,150,50); ba_1[2].setVisible(false); scene.add(ba_1[2]); prenota = new Prenota(scene); scene.add(prenota); prenota.setVisible(false); registra = new Registra(scene); scene.add(registra); registra.setVisible(false); setup = new Info(scene); scene.add(setup); setup.setVisible(false); scene.setVisible(true); scene.requestFocus(); System.out.println("disegnata interfaccia"); } - 84 - Sviluppo di un’applicazione MHP Capitolo 4 //Grafica pulsanti.Gestisce la grafica dei pulsanti //in relazione al fatto che la funzione corrispondente //sia attiva/attivabile, oppure non selezionata. //In pratica gestisce i colori dei pulsanti; //colore rosso: funzione attivabile //colore giallo: funzione non attivabile. public void setframeattivo (int numero){ b_1[numero].setVisible(false); ba_1[numero].setVisible(true); mess.setTextContent(messaggio[frameattivo], HVisible.NORMAL_STATE); mess.setBounds(210,100+frameattivo*60,450,50); scene.repaint(); } //Viene oscurata la parte grafica e //si ferma il processo attivo. //Richiamato del metodo destroyXlet nel momento di //distruzione della xlet. public void distruggi(){ this.scene.removeAll(); this.scene.setVisible(false); } public void premutoEnter(){ //Metodo vuoto. - 85 - Sviluppo di un’applicazione MHP Capitolo 4 } //Ascoltatore degli eventi del telecomando. //La gestione delle funzionalità è stata fatta utilizzando le //interfacce Java. Questo permette di gestire i processi in // maniera dinamica, passando gli eventi solo al processo attivo. public void keyPressed (KeyEvent key) { int pulsantePremuto = key.getKeyCode(); switch(pulsantePremuto){ case KeyEvent.VK_0: case KeyEvent.VK_1: case KeyEvent.VK_2: case KeyEvent.VK_3: case KeyEvent.VK_4: case KeyEvent.VK_5: case KeyEvent.VK_6: case KeyEvent.VK_7: case KeyEvent.VK_8: case KeyEvent.VK_9: case 520: if (strumento!=null){ strumento.passaEvento(key);} break; //Tasto rosso. case HRcEvent.VK_COLORED_KEY_0: if (strumento!=null){ strumento.passaEvento(key);} break; - 86 - Sviluppo di un’applicazione MHP Capitolo 4 //Tasto verde. case HRcEvent.VK_COLORED_KEY_1: if (strumento!=null) { strumento.stop(); strumento=null; scene.add(mess);} break; //Tasto giallo. case HRcEvent.VK_COLORED_KEY_2: if (strumento!=null){ strumento.passaEvento(key);} break; //Tasto blu. case HRcEvent.VK_COLORED_KEY_3: if (strumento!=null){ strumento.passaEvento(key);} break; case HRcEvent.VK_UP: if (strumento != null) { strumento.passaEvento(key); } else { b_1[frameattivo].setVisible(true); if(frameattivo <= 2 && frameattivo > 0) frameattivo--; setframeattivo(frameattivo); } break; - 87 - Sviluppo di un’applicazione MHP Capitolo 4 case HRcEvent.VK_DOWN: if (strumento != null) { strumento.passaEvento(key); } else { b_1[frameattivo].setVisible(true); if(frameattivo >= 0 && frameattivo < 2) frameattivo++; setframeattivo(frameattivo); } break; case HRcEvent.VK_RIGHT: if (strumento!=null) { strumento.passaEvento(key);} break; case HRcEvent.VK_LEFT: if (strumento!=null) { strumento.passaEvento(key);} break; case HRcEvent.VK_ENTER: if (strumento!=null) { strumento.passaEvento(key);} else { scene.remove(mess); Main.gestoreSfondi.cutVideo(); - 88 - Sviluppo di un’applicazione MHP Capitolo 4 //Funzione registra. if(frameattivo == 0 && registra.isVisible() == false){ System.out.println("Registra"); if( strumento != null) strumento.stop(); strumento = registra; strumento.start(); } //Funzione prenota. if(frameattivo == 1 && prenota.isVisible() == false){ System.out.println("Prenota"); if( strumento != null) strumento.stop(); strumento = prenota; strumento.start(); } //Funzione setup. if(frameattivo == 2 && setup.isVisible() == false){ System.out.println("Setup"); if( strumento != null) strumento.stop(); strumento = setup; strumento.start(); } } break; default: - 89 - Sviluppo di un’applicazione MHP Capitolo 4 break; } } } 4.4.5 Keypad.java import java.awt.Color; import java.awt.Font; import org.havi.ui.*; import org.havi.ui.event.*; //Questa classe implementa la tastiera stile cellulare. public class Keypad extends HComponent { private KeypadButton[] button; private HContainer container; private HScene scene; private int bottone; private boolean iniziato; private long tempoInizio; private int numeroPremuto; private int letteraPremuta; int length=0; private char[] subChar=null; private String puls; private HText cancella; - 90 - Sviluppo di un’applicazione MHP Capitolo 4 public Keypad(HScene scenemain) { this.scene=scenemain; } public void start() { bottone=-1; iniziato=false; letteraPremuta=0; puls=""; System.out.println("creo tastiera"); container=new HContainer(0,0,720,576); System.out.println("creato container"); button=new KeypadButton[10]; System.out.println("creato array bottoni tastiera"); //Crea i tasti da visualizzare. button[0] = new KeypadButton(container, "0","_0@",109,476,95,20); button[1] = new KeypadButton(container,"1",".1",60,350,95,20); button[2] = new KeypadButton(container,"2","abc2äå",109,350,95,20); button[3] = new KeypadButton(container,"3","def3",158,350,95,20); button[4] = new KeypadButton(container,"4","ghi4",60,392,95,20); button[5] = new KeypadButton(container,"5","jkl5",109,392,95,20); - 91 - Sviluppo di un’applicazione MHP Capitolo 4 button[6] = new KeypadButton(container,"6","mno6ö",158,392,95,20); button[7] = new KeypadButton(container,"7","pqrs7",60,434,95,20); button[8] = new KeypadButton(container,"8","tuv8",109,434,95,20); button[9] = new KeypadButton(container,"9","wxyz9",158,434,95,20); cancella = new HText("Premi FRECCIA SINISTRA per cancellare una lettera.",190,400,500,40); cancella.setBackgroundMode( HVisible.NO_BACKGROUND_FILL); cancella.setBordersEnabled(false); cancella.setForeground(Color.white); cancella.setFont(new Font("Arial",Font.PLAIN,20)); System.out.println("creati tutti i bottoni tastiera"); for (int i=0;i<=0;i++) { button[i].setVisible(true); container.add(button[i]); } container.add(cancella); scene.add(container); } //Metodo chiamato quando si deve togliere la tastiera public void remove() { System.out.println("rimuovo tastiera"); container.removeAll(); scene.remove(container); - 92 - Sviluppo di un’applicazione MHP Capitolo 4 } public void setTextNumber(HText textbox, int numero, int ta) { int num; if (ta==4 || ta==5) { num=2; } else { num=4; } char[] old=(textbox.getName()).toCharArray(); if (old.length==num) {} else { textbox.setName(textbox.getName()+ String.valueOf(numero)); System.out.println("Name+puls=\n"+textbox.getName()); textbox.setTextContent(textbox.getName(), HVisible.NORMAL_STATE); } } //Imposta il testo a seconda del tasto premuto e a seconda //se il tasto è stato premuto più di una volta. public void setText(HText textbox, int pulsante){ if (pulsante==HRcEvent.VK_LEFT) { System.out.println("Premuta freccia sinistra"); char[] old=(textbox.getName()).toCharArray(); - 93 - Sviluppo di un’applicazione MHP Capitolo 4 System.out.println("inizializzato old"); String name=""; for (int i=0;i<old.length-1;i++) { name=name+String.valueOf(old[i]); } System.out.println("Name=\n"+name); System.out.println("riempito name"); textbox.setName(name+puls); System.out.println("Name+puls=\n"+name+puls); textbox.setTextContent(textbox.getName(), HVisible.NORMAL_STATE); } else { numeroPremuto=pulsante-48; subChar=button[numeroPremuto].getChar(); length=subChar.length; if(numeroPremuto==bottone && iniziato==true) { letteraPremuta++; if (letteraPremuta>=length) { letteraPremuta=0; } if (System.currentTimeMillis()-tempoInizio<900) { puls=String.valueOf(subChar[letteraPremuta]); System.out.println("Puls=\n"+puls); if (puls.equals("_")) { - 94 - Sviluppo di un’applicazione MHP Capitolo 4 puls=" "; System.out.println("Puls2=\n"+puls);} System.out.println("entrato in cambia carattere"); char[] old=(textbox.getName()).toCharArray(); System.out.println("inizializzato old"); String name=""; for (int i=0;i<old.length-1;i++) { name=name+String.valueOf(old[i]); } System.out.println("Name=\n"+name); System.out.println("riempito name"); textbox.setName(name+puls); System.out.println("Name+puls=\n"+name+puls); textbox.setTextContent(textbox.getName(), HVisible.NORMAL_STATE); tempoInizio=System.currentTimeMillis(); } else { letteraPremuta=0; tempoInizio=System.currentTimeMillis(); puls=String.valueOf(subChar[letteraPremuta]); if (puls.equals("_")) { puls=" "; System.out.println("Puls2=\n"+puls);} iniziato=true; textbox.setName(textbox.getName()+puls); textbox.setTextContent(textbox.getName(), HVisible.NORMAL_STATE); - 95 - Sviluppo di un’applicazione MHP Capitolo 4 System.out.println("premuto per la prima volta "+numeroPremuto); } } else { letteraPremuta=0; tempoInizio=System.currentTimeMillis(); puls=String.valueOf(subChar[letteraPremuta]); if (puls.equals("_")) { puls=" "; System.out.println("Puls2=\n"+puls);} iniziato=true; textbox.setName(textbox.getName()+puls); textbox.setTextContent(textbox.getName(), HVisible.NORMAL_STATE); System.out.println("premuto per la prima volta "+numeroPremuto); } bottone=numeroPremuto; puls=""; } } } - 96 - Sviluppo di un’applicazione MHP Capitolo 4 4.4.6 KeypadButton.java import java.awt.*; import org.havi.ui.*; //Classe che implementa i tasti della tastiera. public class KeypadButton extends HComponent{ private HText button; private HText subButton; private String numero; private String lettere; private HIcon border; private char[] subChar=null; public KeypadButton(HContainer container, String numero, String lettere, int x, int y, int width, int length) { super.setBounds(x,y,width,length); this.numero=numero; this.lettere=lettere; subChar=lettere.toCharArray(); border=new HIcon(Toolkit.getDefaultToolkit().getImage( "bordo2.jpg")); button=new HText(this.numero,x,y,width/2,length); button.setBackground(Color.yellow); button.setBackgroundMode(HVisible.BACKGROUND_FILL); button.setForeground(Color.blue); button.setBordersEnabled(true); button.setFont(new Font("Arial",Font.PLAIN,23)); subButton=new HText(this.lettere,x,y+20,width/2,length); - 97 - Sviluppo di un’applicazione MHP Capitolo 4 subButton.setBackground(Color.yellow); subButton.setBackgroundMode( HVisible.BACKGROUND_FILL); subButton.setForeground(Color.blue); subButton.setBordersEnabled(true); subButton.setFont(new Font("Arial",Font.PLAIN,16)); border.setBounds((button.getX())-2,(button.getY())-2,51,44); container.add(button); container.add(subButton); container.add(border); } public String getNumero() { return numero; } public char[] getChar() { return subChar; } } 4.4.7 Comunica.java import java.net.*; import java.io.*; //Questa classe permette alla xlet di comunicare con il server. public class Comunica { - 98 - Sviluppo di un’applicazione MHP Capitolo 4 private String IP; private int port; private Object obj; private Object objW; public Comunica(String sIP,int p){ port=p; IP=sIP; } public Object Connetti(Object object){ obj=null; objW=object; try{ // Apre una connessione verso un server in ascolto // sulla porta port all'indirizzo IP System.out.println("Apertura Socket..."); Socket sock = new Socket(IP,port); System.out.println("creato socket"); OutputStream s1out = sock.getOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(s1out); System.out.println("impostato outpustream"); oos.writeObject(objW); // Ricava lo stream di input dal socket sock // ed utilizza un oggetto wrapper di classe BufferedReader // per semplificare le operazioni di lettura InputStream is = sock.getInputStream(); ObjectInputStream ois = new ObjectInputStream(is); try{ - 99 - Sviluppo di un’applicazione MHP Capitolo 4 obj=ois.readObject();} catch (ClassNotFoundException e) { System.out.println("Error: "+e.getMessage()); } ois.close(); sock.close(); System.out.println("Chiusura connessione effettuata"); }catch (ConnectException connExc){ System.out.println("Errore nella connessione "); obj="Errore nella connessione"; } catch (IOException ex){ ex.printStackTrace(); } return obj; } } 4.4.8 Strumenti.java import java.awt.event.KeyEvent; import org.havi.ui.*; import org.havi.ui.HContainer; //Questa classe viene estesa dai vari strumenti //registra, prenota, info. public class Strumenti extends HComponent{ protected HScene scene; protected HContainer container; - 100 - Sviluppo di un’applicazione MHP Capitolo 4 public Strumenti(HScene scenemain) { scene = scenemain; } public void start() {} public void stop() { this.setVisible(false); container.removeAll(); container.setVisible(false); InterfacciaGrafica.keypad.remove(); scene.remove(container); System.out.println("Stop "+this.toString()); Main.gestoreSfondi.displayBackgroundInitImage(); scene.repaint(); } public void passaEvento(KeyEvent key) {} } 4.4.9 Registra.java import java.awt.Color; import java.awt.Font; import java.awt.Toolkit; import java.awt.event.KeyEvent; import org.havi.ui.*; import org.havi.ui.event.*; //Questa classe implementa tutta la fase della registrazione //di un nuovo utente. - 101 - Sviluppo di un’applicazione MHP Capitolo 4 public class Registra extends Strumenti{ private HText [] campoTesto; private HText riempi; private HText user; private HText password; private HText nome; private HText cognome; private HText data; private HText citta; private HText indirizzo; private HText regOK; private boolean reg; private HText invia; private HText indietro; private HIcon border; private int testoAttivo; public Registra(HScene scenemain) { super(scenemain); } public void start() { reg=false; border=new HIcon(Toolkit.getDefaultToolkit().getImage("bordo.jpg")); testoAttivo=0; - 102 - Sviluppo di un’applicazione MHP Capitolo 4 riempi=new HText("",400,450,200,30); riempi.setFont(new Font("Arial",Font.PLAIN,20)); riempi.setHorizontalAlignment(HVisible.HALIGN_CENTER); riempi.setForeground(Color.orange); riempi.setBackgroundMode( HVisible.NO_BACKGROUND_FILL); riempi.setBordersEnabled(false); container=new HContainer(0,0,720,576); campoTesto=new HText [9]; user= new HText ("Username:",340,100,100,20); user.setFont(new Font("Arial",Font.PLAIN,20)); user.setHorizontalAlignment(HVisible.HALIGN_RIGHT); user.setForeground(Color.white); user.setBordersEnabled(false); user.setBackgroundMode(HVisible.NO_BACKGROUND_FILL); container.add(user); campoTesto[0]=new HText("",450,100,170,20); campoTesto[0].setFont(new Font("Arial",Font.PLAIN,20)); campoTesto[0].setHorizontalAlignment( HVisible.HALIGN_RIGHT); campoTesto[0].setForeground(Color.black); campoTesto[0].setBackground(Color.white); campoTesto[0].setBordersEnabled(true); campoTesto[0].setBackgroundMode( HVisible.BACKGROUND_FILL); container.add(campoTesto[0]); System.out.println("Aggiunto user"); - 103 - Sviluppo di un’applicazione MHP Capitolo 4 password= new HText ("Password:",340,130,100,20); password.setFont(new Font("Arial",Font.PLAIN,20)); password.setHorizontalAlignment(HVisible.HALIGN_RIGHT); password.setForeground(Color.white); password.setBordersEnabled(false); password.setBackgroundMode( HVisible.NO_BACKGROUND_FILL); container.add(password); campoTesto[1]=new HText("",450,130,170,20); campoTesto[1].setFont(new Font("Arial",Font.PLAIN,20)); campoTesto[1].setHorizontalAlignment( HVisible.HALIGN_RIGHT); campoTesto[1].setForeground(Color.black); campoTesto[1].setBackground(Color.white); campoTesto[1].setBordersEnabled(true); campoTesto[1].setBackgroundMode( HVisible.BACKGROUND_FILL); container.add(campoTesto[1]); System.out.println("Aggiunto password"); nome= new HText ("Nome:",340,170,100,20); nome.setFont(new Font("Arial",Font.PLAIN,20)); nome.setHorizontalAlignment(HVisible.HALIGN_RIGHT); nome.setForeground(Color.white); nome.setBordersEnabled(false); nome.setBackgroundMode( HVisible.NO_BACKGROUND_FILL); container.add(nome); campoTesto[2]=new HText("",450,170,170,20); - 104 - Sviluppo di un’applicazione MHP Capitolo 4 campoTesto[2].setFont(new Font("Arial",Font.PLAIN,20)); campoTesto[2].setHorizontalAlignment( HVisible.HALIGN_RIGHT); campoTesto[2].setForeground(Color.black); campoTesto[2].setBackground(Color.white); campoTesto[2].setBordersEnabled(true); campoTesto[2].setBackgroundMode( HVisible.BACKGROUND_FILL); container.add(campoTesto[2]); System.out.println("Aggiunto nome"); cognome= new HText ("Cognome:",340,200,100,20); cognome.setFont(new Font("Arial",Font.PLAIN,20)); cognome.setHorizontalAlignment(HVisible.HALIGN_RIGHT); cognome.setForeground(Color.white); cognome.setBordersEnabled(false); cognome.setBackgroundMode (HVisible.NO_BACKGROUND_FILL); container.add(cognome); campoTesto[3]=new HText("",450,200,170,20); campoTesto[3].setFont(new Font("Arial",Font.PLAIN,20)); campoTesto[3].setHorizontalAlignment (HVisible.HALIGN_RIGHT); campoTesto[3].setForeground(Color.black); campoTesto[3].setBackground(Color.white); campoTesto[3].setBordersEnabled(true); campoTesto[3].setBackgroundMode( HVisible.BACKGROUND_FILL); container.add(campoTesto[3]); - 105 - Sviluppo di un’applicazione MHP Capitolo 4 System.out.println("Aggiunto cognome"); data= new HText ( "Data di nascita (gg-mm-aaa):",190,230,250,20); data.setFont(new Font("Arial",Font.PLAIN,20)); data.setHorizontalAlignment(HVisible.HALIGN_RIGHT); data.setForeground(Color.white); data.setBordersEnabled(false); data.setBackgroundMode(HVisible.NO_BACKGROUND_FILL); container.add(data); campoTesto[4]=new HText("",450,230,35,20); campoTesto[4].setFont(new Font("Arial",Font.PLAIN,20)); campoTesto[4].setHorizontalAlignment( HVisible.HALIGN_RIGHT); campoTesto[4].setForeground(Color.black); campoTesto[4].setBackground(Color.white); campoTesto[4].setBackgroundMode( HVisible.BACKGROUND_FILL); campoTesto[4].setBordersEnabled(true); container.add(campoTesto[4]); campoTesto[5]=new HText("",495,230,35,20); campoTesto[5].setFont(new Font("Arial",Font.PLAIN,20)); campoTesto[5].setHorizontalAlignment( HVisible.HALIGN_RIGHT); campoTesto[5].setForeground(Color.black); campoTesto[5].setBackground(Color.white); campoTesto[5].setBackgroundMode( HVisible.BACKGROUND_FILL); campoTesto[5].setBordersEnabled(true); - 106 - Sviluppo di un’applicazione MHP Capitolo 4 container.add(campoTesto[5]); campoTesto[6]=new HText("",540,230,80,20); campoTesto[6].setFont(new Font("Arial",Font.PLAIN,20)); campoTesto[6].setHorizontalAlignment( HVisible.HALIGN_RIGHT); campoTesto[6].setForeground(Color.black); campoTesto[6].setBackground(Color.white); campoTesto[6].setBackgroundMode( HVisible.BACKGROUND_FILL); campoTesto[6].setBordersEnabled(true); container.add(campoTesto[6]); System.out.println("Aggiunto data"); citta= new HText ("Città:",340,260,100,20); citta.setFont(new Font("Arial",Font.PLAIN,20)); citta.setHorizontalAlignment(HVisible.HALIGN_RIGHT); citta.setForeground(Color.white); citta.setBordersEnabled(false); citta.setBackgroundMode(HVisible.NO_BACKGROUND_FILL); container.add(citta); campoTesto[7]=new HText("",450,260,170,20); campoTesto[7].setFont(new Font("Arial",Font.PLAIN,20)); campoTesto[7].setHorizontalAlignment( HVisible.HALIGN_RIGHT); campoTesto[7].setForeground(Color.black); campoTesto[7].setBackground(Color.white); campoTesto[7].setBackgroundMode( HVisible.BACKGROUND_FILL); campoTesto[7].setBordersEnabled(true); - 107 - Sviluppo di un’applicazione MHP Capitolo 4 container.add(campoTesto[7]); System.out.println("Aggiunto città"); indirizzo= new HText ("Indirizzo:",340,290,100,20); indirizzo.setFont(new Font("Arial",Font.PLAIN,20)); indirizzo.setHorizontalAlignment(HVisible.HALIGN_RIGHT); indirizzo.setForeground(Color.white); indirizzo.setBordersEnabled(false); indirizzo.setBackgroundMode( HVisible.NO_BACKGROUND_FILL); container.add(indirizzo); campoTesto[8]=new HText("",450,290,170,20); campoTesto[8].setFont(new Font("Arial",Font.PLAIN,20)); campoTesto[8].setHorizontalAlignment( HVisible.HALIGN_RIGHT); campoTesto[8].setForeground(Color.black); campoTesto[8].setBackground(Color.white); campoTesto[8].setBackgroundMode (HVisible.BACKGROUND_FILL); campoTesto[8].setBordersEnabled(true); container.add(campoTesto[8]); System.out.println("Aggiunto indirizzo"); for (int i=0;i<9;i++) { campoTesto[i].setName(""); } invia= new HText ("INVIA",360,345,130,30); invia.setFont(new Font("Arial",Font.BOLD,20)); invia.setForeground(Color.white); invia.setBordersEnabled(false); - 108 - Sviluppo di un’applicazione MHP Capitolo 4 invia.setBackground(Color.red); invia.setBackgroundMode(HVisible.BACKGROUND_FILL); container.add(invia); System.out.println("Aggiunto invia"); indietro= new HText ("Indietro",520,345,130,30); indietro.setFont(new Font("Arial",Font.BOLD,20)); indietro.setForeground(Color.blue); indietro.setBordersEnabled(false); indietro.setBackground(Color.green); indietro.setBackgroundMode(HVisible.BACKGROUND_FILL); container.add(indietro); System.out.println("Aggiunto indietro"); border.setBounds((campoTesto[0].getX())-4, (campoTesto[0].getY())-4,(campoTesto[0].getWidth())+8, (campoTesto[0].getHeight())+8); container.add(border); container.setVisible(true); scene.add(container); System.out.println("Aggiunto container"); (InterfacciaGrafica.keypad).start(); scene.repaint(); System.out.println("Scene.repaint()"); super.setVisible(true); } //Messaggio visualizzato se l’utente non ha compilato tutti i campi public void riempi(String s) { System.out.println("entrato in riempi"); - 109 - Sviluppo di un’applicazione MHP Capitolo 4 riempi.setTextContent("Inserisci "+s, HVisible.NORMAL_STATE); container.add(riempi); scene.add(container); System.out.println("primo scene.repaint"); scene.repaint(); } public void invia () { System.out.println("entrato in invia"); if (campoTesto[0].getName()=="") this.riempi("user"); else if(campoTesto[1].getName()=="") this.riempi("password"); else if(campoTesto[2].getName()=="") this.riempi("nome"); else if(campoTesto[3].getName()=="") this.riempi("cognome"); else if(campoTesto[4].getName()=="") this.riempi("data"); else if(campoTesto[5].getName()=="") this.riempi("data"); else if(campoTesto[6].getName()=="") this.riempi("data"); else if(campoTesto[7].getName()=="") this.riempi("città"); else if(campoTesto[8].getName()=="") this.riempi("indirizzo"); - 110 - Sviluppo di un’applicazione MHP Capitolo 4 else { //Procede alla registrazione del nuovo utente, // connettendosi al server tramite la classe Comunica. ObjectRegistration obReg=new ObjectRegistration (campoTesto[0].getName(), campoTesto[1].getName(), campoTesto[2].getName(), campoTesto[3].getName(), campoTesto[6].getName()+""+campoTesto[5].getName() +"-"+campoTesto[4].getName(), campoTesto[7].getName(), campoTesto[8].getName()); Comunica comunica=new Comunica(Main.ip,8000); String str=(String)(comunica.Connetti(obReg)); reg=true; regOK=new HText(str,340,130,250,50); regOK.setBackgroundMode( HVisible.NO_BACKGROUND_FILL); regOK.setForeground(Color.white); regOK.setFont(new Font("Arial",Font.PLAIN,22)); container.removeAll(); container.add(regOK); indietro.setBounds(420,250,130,30); container.add(indietro); InterfacciaGrafica.keypad.remove(); scene.add(container); - 111 - Sviluppo di un’applicazione MHP Capitolo 4 scene.repaint(); } } //Imposta la casella attiva. public void setTestoAttivo(int numero) { border.setBounds((campoTesto[testoAttivo].getX())-4, (campoTesto[testoAttivo].getY())-4, (campoTesto[testoAttivo].getWidth())+8, (campoTesto[testoAttivo].getHeight())+8); System.out.println("impostato testo attivo"); } public void passaEvento(KeyEvent key) { int pulsantePremuto = key.getKeyCode(); switch(pulsantePremuto){ case HRcEvent.VK_UP: if(reg==false) { if(testoAttivo <= 8 && testoAttivo > 0) testoAttivo--; setTestoAttivo(testoAttivo); } break; case HRcEvent.VK_DOWN: if (reg==false) { if(testoAttivo >= 0 && testoAttivo < 8) testoAttivo++; setTestoAttivo(testoAttivo); } break; - 112 - Sviluppo di un’applicazione MHP Capitolo 4 case HRcEvent.VK_COLORED_KEY_0: if (reg==false) { this.invia(); } break; case HRcEvent.VK_0: if (testoAttivo==4 || testoAttivo==5 || testoAttivo==6) { (InterfacciaGrafica.keypad).setTextNumber (campoTesto[testoAttivo],0,testoAttivo); break; } case HRcEvent.VK_1: if (testoAttivo==4 || testoAttivo==5 || testoAttivo==6) { (InterfacciaGrafica.keypad).setTextNumber (campoTesto[testoAttivo],1,testoAttivo); break; } case HRcEvent.VK_2: if (testoAttivo==4 || testoAttivo==5 || testoAttivo==6) { (InterfacciaGrafica.keypad).setTextNumber (campoTesto[testoAttivo],2,testoAttivo); break; } - 113 - Sviluppo di un’applicazione MHP Capitolo 4 case HRcEvent.VK_3: if (testoAttivo==4 || testoAttivo==5 || testoAttivo==6) { (InterfacciaGrafica.keypad).setTextNumber (campoTesto[testoAttivo],3,testoAttivo); break; } case HRcEvent.VK_4: if (testoAttivo==4 || testoAttivo==5 || testoAttivo==6) { (InterfacciaGrafica.keypad).setTextNumber (campoTesto[testoAttivo],4,testoAttivo); break; } case HRcEvent.VK_5: if (testoAttivo==4 || testoAttivo==5 || testoAttivo==6) { (InterfacciaGrafica.keypad).setTextNumber (campoTesto[testoAttivo],5,testoAttivo); break; } case HRcEvent.VK_6: if (testoAttivo==4 || testoAttivo==5 || testoAttivo==6) { - 114 - Sviluppo di un’applicazione MHP Capitolo 4 (InterfacciaGrafica.keypad).setTextNumber (campoTesto[testoAttivo],6,testoAttivo); break; } case HRcEvent.VK_7: if (testoAttivo==4 || testoAttivo==5 || testoAttivo==6) { (InterfacciaGrafica.keypad).setTextNumber (campoTesto[testoAttivo],7,testoAttivo); break; } case HRcEvent.VK_8: if (testoAttivo==4 || testoAttivo==5 || testoAttivo==6) { (InterfacciaGrafica.keypad).setTextNumber (campoTesto[testoAttivo],8,testoAttivo); break; } case HRcEvent.VK_9: if (testoAttivo==4 || testoAttivo==5 || testoAttivo==6) { (InterfacciaGrafica.keypad).setTextNumber (campoTesto[testoAttivo],9,testoAttivo); break; } - 115 - Sviluppo di un’applicazione MHP Capitolo 4 case HRcEvent.VK_LEFT: if (reg==false) { (InterfacciaGrafica.keypad).setText (campoTesto[testoAttivo],pulsantePremuto); } break; case HRcEvent.VK_COLORED_KEY_1: case HRcEvent.VK_COLORED_KEY_2: case HRcEvent.VK_COLORED_KEY_3: case HRcEvent.VK_ENTER: default: break; } } } 4.4.10 Prenota.java import java.awt.Color; import java.awt.Font; import java.awt.Toolkit; import java.awt.event.KeyEvent; import org.havi.ui.*; import org.havi.ui.event.HRcEvent; //Questa classe si occupa della fase della prenotazione. //Più precisamente, si occupa della fase di login. public class Prenota extends Strumenti{ - 116 - Sviluppo di un’applicazione MHP Capitolo 4 private HText user; private HText password; private HText conferma; private HText indietro; private HText [] campoTesto; private HIcon border; private int testoAttivo; private boolean filmFlag; private Film disFilm; private String user2; private String pwd; private Object film; private HText riempi; public Prenota(HScene scenemain) { super(scenemain); } public void start() { //Vengono impostate le varie caselle di testo, le scritte //e i pulsanti. testoAttivo=0; filmFlag=false; disFilm=new Film(scene); disFilm.setVisible(false); campoTesto=new HText [2]; border=new HIcon(Toolkit.getDefaultToolkit().getImage("bordo.jpg")); container=new HContainer(0,0,720,576); - 117 - Sviluppo di un’applicazione MHP Capitolo 4 user= new HText ("Username:",340,100,100,20); user.setFont(new Font("Arial",Font.PLAIN,20)); user.setHorizontalAlignment(HVisible.HALIGN_RIGHT); user.setForeground(Color.white); user.setBordersEnabled(false); user.setBackgroundMode(HVisible.NO_BACKGROUND_FILL); container.add(user); riempi=new HText("",400,250,200,30); riempi.setFont(new Font("Arial",Font.PLAIN,20)); riempi.setHorizontalAlignment(HVisible.HALIGN_CENTER); riempi.setForeground(Color.orange); riempi.setBackgroundMode( HVisible.NO_BACKGROUND_FILL); riempi.setBordersEnabled(false); campoTesto[0]=new HText("",450,100,170,20); campoTesto[0].setFont(new Font("Arial",Font.PLAIN,20)); campoTesto[0].setHorizontalAlignment( HVisible.HALIGN_RIGHT); campoTesto[0].setForeground(Color.black); campoTesto[0].setBackground(Color.white); campoTesto[0].setBordersEnabled(true); campoTesto[0].setBackgroundMode( HVisible.BACKGROUND_FILL); container.add(campoTesto[0]); System.out.println("Aggiunto user"); password= new HText ("Password:",340,130,100,20); password.setFont(new Font("Arial",Font.PLAIN,20)); password.setHorizontalAlignment(HVisible.HALIGN_RIGHT); - 118 - Sviluppo di un’applicazione MHP Capitolo 4 password.setForeground(Color.white); password.setBordersEnabled(false); password.setBackgroundMode( HVisible.NO_BACKGROUND_FILL); container.add(password); campoTesto[1]=new HText("",450,130,170,20); campoTesto[1].setFont(new Font("Arial",Font.PLAIN,20)); campoTesto[1].setHorizontalAlignment( HVisible.HALIGN_RIGHT); campoTesto[1].setForeground(Color.black); campoTesto[1].setBackground(Color.white); campoTesto[1].setBordersEnabled(true); campoTesto[1].setBackgroundMode (HVisible.BACKGROUND_FILL); container.add(campoTesto[1]); System.out.println("Aggiunto password"); for (int i=0;i<2;i++) { campoTesto[i].setName(""); } conferma= new HText ("ENTRA",360,190,130,30); conferma.setFont(new Font("Arial",Font.BOLD,20)); conferma.setForeground(Color.white); conferma.setBordersEnabled(false); conferma.setBackground(Color.red); conferma.setBackgroundMode( HVisible.BACKGROUND_FILL); container.add(conferma); System.out.println("Aggiunto conferma"); - 119 - Sviluppo di un’applicazione MHP Capitolo 4 indietro= new HText ("Indietro",520,190,130,30); indietro.setFont(new Font("Arial",Font.BOLD,20)); indietro.setForeground(Color.blue); indietro.setBordersEnabled(false); indietro.setBackground(Color.green); indietro.setBackgroundMode(HVisible.BACKGROUND_FILL); container.add(indietro); System.out.println("Aggiunto indietro"); border.setBounds((campoTesto[0].getX())-4, (campoTesto[0].getY())-4,(campoTesto[0].getWidth())+8, (campoTesto[0].getHeight())+8); container.add(border); container.setVisible(true); scene.add(container); (InterfacciaGrafica.keypad).start(); scene.repaint(); super.setVisible(true); } //Visualizza un messaggio se non sono stati riempiti i campi.. public void riempi(String s) { System.out.println("entrato in riempi"); riempi.setTextContent("Inserisci "+s, HVisible.NORMAL_STATE); container.add(riempi); scene.add(container); System.out.println("primo scene.repaint"); scene.repaint(); - 120 - Sviluppo di un’applicazione MHP Capitolo 4 } //Cambia il colore del bordo della casella di testo attiva. public void setTestoAttivo(int numero) { border.setBounds((campoTesto[testoAttivo].getX())4,(campoTesto[testoAttivo].getY())4,(campoTesto[testoAttivo].getWidth())+ 8,(campoTesto[testoAttivo].getHeight())+8); } //Metodo chiamato quando viene premuto il tasto rosso //del telecomando. public void entra() { System.out.println("entrato in invia"); if (campoTesto[0].getName()=="") this.riempi("user"); else if(campoTesto[1].getName()=="") this.riempi("password"); else { //Avvia la comunicazione con il server. Comunica com=new Comunica(Main.ip,8000); user2=campoTesto[0].getName(); pwd=campoTesto[1].getName(); ObjectPrenotation pren= new ObjectPrenotation(user2,pwd); System.out.println("creato ObjectPrenotation"); film=com.Connetti(pren); - 121 - Sviluppo di un’applicazione MHP Capitolo 4 System.out.println("ritornato film"); if (film instanceof ObjectFilm) { //Viene visualizzato l’elenco dei film. container.removeAll(); InterfacciaGrafica.keypad.remove(); scene.remove(container); disFilm.disegna(user2,(ObjectFilm)film); filmFlag=true; } if(film instanceof String) { //Viene visualizzato un messaggio di errore. user2=""; pwd=""; System.out.println(film); HText pren2= new HText((String)film,340,130,300,50); pren2.setBackgroundMode( HVisible.NO_BACKGROUND_FILL); pren2.setForeground(Color.white); pren2.setFont(new Font("Arial",Font.PLAIN,22)); container.removeAll(); indietro.setBounds(440,200,100,20); container.add(indietro); InterfacciaGrafica.keypad.remove(); container.add(pren2); - 122 - Sviluppo di un’applicazione MHP Capitolo 4 scene.repaint(); } } } public void stop() { if (filmFlag) { disFilm.stop(); super.stop(); } else super.stop(); } public void passaEvento(KeyEvent key) { int pulsantePremuto = key.getKeyCode(); switch(pulsantePremuto){ case HRcEvent.VK_UP: if (filmFlag) { disFilm.passaEvento(key);} else { if(testoAttivo <= 1 && testoAttivo > 0) testoAttivo--; setTestoAttivo(testoAttivo); } break; case HRcEvent.VK_DOWN: if (filmFlag) { disFilm.passaEvento(key);} - 123 - Sviluppo di un’applicazione MHP Capitolo 4 else { if(testoAttivo >= 0 && testoAttivo < 1) testoAttivo++; setTestoAttivo(testoAttivo);} break; case HRcEvent.VK_ENTER: System.out.println("Premuto enter in prenota"); if(filmFlag) {disFilm.passaEvento(key);} else {System.out.println("bo");} break; case HRcEvent.VK_0: case HRcEvent.VK_1: case HRcEvent.VK_2: case HRcEvent.VK_3: case HRcEvent.VK_4: case HRcEvent.VK_5: case HRcEvent.VK_6: case HRcEvent.VK_7: case HRcEvent.VK_8: case HRcEvent.VK_9: case HRcEvent.VK_LEFT: case HRcEvent.VK_RIGHT: System.out.println("Premuta freccia in prenota"); if (filmFlag) {disFilm.passaEvento(key);} else { (InterfacciaGrafica.keypad).setText( campoTesto[testoAttivo], pulsantePremuto);} - 124 - Sviluppo di un’applicazione MHP Capitolo 4 break; case HRcEvent.VK_COLORED_KEY_0: if (filmFlag) {disFilm.passaEvento(key);} else { this.entra();} break; case HRcEvent.VK_COLORED_KEY_3: if(filmFlag) { disFilm.passaEvento(key); } break; case HRcEvent.VK_COLORED_KEY_2: case HRcEvent.VK_COLORED_KEY_1: default: break; } } } 4.4.11 Film.java import org.havi.ui.*; import org.havi.ui.event.HRcEvent; import java.awt.Font; import java.awt.Color; import java.awt.event.KeyEvent; //Questa classe si occupa della fase di prenotazione riguardante - 125 - Sviluppo di un’applicazione MHP Capitolo 4 //la visualizzazione dei film disponibile e la scelta tra di essi. public class Film extends Strumenti{ private HText[] titoli; private int titoloInizio; private HText conferma; private HText indietro; private String user; private ObjectFilm film; private boolean plateaFlag; private int testoAttivo; private Platea platea; private Object posti; public Film(HScene scenemain) { super(scenemain); } //Visualizza l’elenco dei film disponibili. public void disegna(String user,ObjectFilm film2) { plateaFlag=false; this.user=user; this.film=film2; platea=new Platea(scene); platea.setVisible(false); container=new HContainer(0,0,720,576); titoli=new HText[4]; int y=120; for (int i=0;i<=3;i++) { System.out.println("entrato nel for"); - 126 - Sviluppo di un’applicazione MHP Capitolo 4 titoli[i]=new HText(film2.getNome(i),270,y,300,30); System.out.println("creato HText"); titoli[i].setFont(new Font("Arial",Font.PLAIN,23)); titoli[i].setForeground(Color.white); titoli[i].setBackgroundMode( HVisible.NO_BACKGROUND_FILL); container.add(titoli[i]); System.out.println("Aggiunto film "+i); y=y+50; } titoli[0].setBackgroundMode(HVisible.BACKGROUND_FILL); titoli[0].setBackground(Color.orange); titoli[0].setForeground(Color.blue); titoloInizio=0; testoAttivo=0; conferma=new HText("Premi OK per confermare",300,350,250,30); conferma.setFont(new Font("Arial",Font.ITALIC,23)); conferma.setForeground(Color.orange); conferma.setBackgroundMode( HVisible.NO_BACKGROUND_FILL); container.add(conferma); indietro= new HText ("Logout",360,400,130,30); indietro.setFont(new Font("Arial",Font.BOLD,20)); indietro.setForeground(Color.blue); indietro.setBordersEnabled(false); indietro.setBackground(Color.green); - 127 - Sviluppo di un’applicazione MHP Capitolo 4 indietro.setBackgroundMode(HVisible.BACKGROUND_FILL); container.add(indietro); scene.add(container); scene.repaint(); } //Metodo chiamato quando viene premuto il tasto OK del //telecomando per scegliere un film. public void entra() { //Viene avviata la comunicazione con il server. Comunica com=new Comunica(Main.ip,8000); ObjectPrenFilm pren=new ObjectPrenFilm(testoAttivo+1); System.out.println("creato ObjectPrenFilm"); posti=com.Connetti(pren); System.out.println("ritornato posti"); if(posti instanceof ObjectPosti) { //Viene visualizzata la piantina dei posti del cinema //per il film scelto. container.removeAll(); InterfacciaGrafica.keypad.remove(); scene.remove(container); platea.disegna(user,(ObjectPosti)posti); plateaFlag=true; } if(posti instanceof String) { //Viene visualizzato il messaggio di errore //ritornato dal server. System.out.println(posti); - 128 - Sviluppo di un’applicazione MHP Capitolo 4 HText pren2=new HText((String)posti,340,130,300,50); pren2.setBackgroundMode( HVisible.NO_BACKGROUND_FILL); pren2.setForeground(Color.white); pren2.setFont(new Font("Arial",Font.PLAIN,22)); container.removeAll(); indietro.setBounds(440,200,100,20); container.add(indietro); InterfacciaGrafica.keypad.remove(); container.add(pren2); scene.repaint(); } } public void setTestoAttivo(int i) { titoli[titoloInizio].setBackgroundMode( HVisible.NO_BACKGROUND_FILL); titoli[titoloInizio].setForeground(Color.white); titoli[i].setBackgroundMode(HVisible.BACKGROUND_FILL); titoli[i].setBackground(Color.orange); titoli[i].setForeground(Color.blue); titoloInizio=i; scene.repaint(); } public void stop() { if (plateaFlag) { platea.stop(); - 129 - Sviluppo di un’applicazione MHP Capitolo 4 super.stop(); } else super.stop(); } public void passaEvento(KeyEvent key) { int pulsantePremuto = key.getKeyCode(); switch(pulsantePremuto){ case HRcEvent.VK_UP: if (plateaFlag) {platea.passaEvento(key);} else { System.out.println("premuto su in film"); if(testoAttivo<4 && testoAttivo>0) { testoAttivo--; this.setTestoAttivo(testoAttivo); } } break; case HRcEvent.VK_DOWN: if (plateaFlag) {platea.passaEvento(key);} else { System.out.println("premuto giù in film"); if(testoAttivo>=0 && testoAttivo<3) { testoAttivo++; this.setTestoAttivo(testoAttivo); } } break; - 130 - Sviluppo di un’applicazione MHP Capitolo 4 case HRcEvent.VK_LEFT: case HRcEvent.VK_RIGHT: if (plateaFlag) {platea.passaEvento(key);} break; case HRcEvent.VK_COLORED_KEY_3: if (plateaFlag) {platea.passaEvento(key); this.entra();} break; case HRcEvent.VK_ENTER: if (plateaFlag) {platea.passaEvento(key);} else { this.entra(); } break; } } } 4.4.12 PlateaPrenota.java public class PlateaPrenota { private PrenotaSingolo pren; public PlateaPrenota(PrenotaSingolo pren) { this.pren=pren; } public Object start() { Comunica com=new Comunica(Main.ip,8000); - 131 - Sviluppo di un’applicazione MHP Capitolo 4 Object platea=com.Connetti(pren); return platea; } } 4.4.13 Platea.java import java.awt.*; import java.awt.event.*; import org.havi.ui.*; import org.havi.ui.event.HRcEvent; //Questa classe si occupa di visualizzare la piantina dei posti //relativi al film scelto (verde=libero, rosso=occupato) e ne //gestisce la prenotazione. public class Platea extends Strumenti { private HText [][] posti1; private int x; private int y; private int width; private int length; private int[] postoAttivo; private int[] postoAttivoInizio; private Image rosso; private Image verde; private Image stampa; private HText conferma; private HText indietro; - 132 - Sviluppo di un’applicazione MHP Capitolo 4 private HText aggiorna; private String user; private ObjectPosti posto; private int cont; private HText verde_t; private HText rosso_t; private Color stamp; public Platea(HScene scenemain) { super(scenemain); cont=0; } public void disegna (String user,ObjectPosti posto2) { if (cont==1) { container.removeAll(); } verde_t=new HText("O"); verde_t.setForeground(Color.green); verde_t.setFont(new Font("Arial",Font.BOLD,20)); verde_t.setBackgroundMode( HVisible.NO_BACKGROUND_FILL); rosso_t=new HText("O"); rosso_t.setForeground(Color.red); rosso_t.setBackgroundMode( HVisible.NO_BACKGROUND_FILL); rosso_t.setFont(new Font("Arial",Font.BOLD,20)); this.user=user; - 133 - Sviluppo di un’applicazione MHP Capitolo 4 this.posto=posto2; conferma=new HText( "Premi OK per prenotare il posto.",320,430,300,50); conferma.setBackgroundMode( HVisible.NO_BACKGROUND_FILL); conferma.setFont(new Font("Arial",Font.PLAIN,20)); conferma.setForeground(Color.white); aggiorna= new HText ("Aggiorna",300,480,130,30); aggiorna.setFont(new Font("Arial",Font.BOLD,20)); aggiorna.setForeground(Color.white); aggiorna.setBordersEnabled(false); aggiorna.setBackground(Color.blue); aggiorna.setBackgroundMode(HVisible.BACKGROUND_FILL); indietro= new HText ("Logout",450,480,130,30); indietro.setFont(new Font("Arial",Font.BOLD,20)); indietro.setForeground(Color.blue); indietro.setBordersEnabled(false); indietro.setBackground(Color.green); indietro.setBackgroundMode(HVisible.BACKGROUND_FILL); postoAttivo=new int[2]; postoAttivoInizio=new int[2]; container=new HContainer(0,0,720,576); posti1=new HText [15][20]; rosso=Toolkit.getDefaultToolkit().getImage("punto_rosso.png"); verde=Toolkit.getDefaultToolkit().getImage("punto_verde.png"); System.out.println("inizializzato posti"); x=240; y=120; - 134 - Sviluppo di un’applicazione MHP Capitolo 4 width=20; length=20; stampa=verde; stamp=Color.green; //Assegna al posto il colore verde o rosso a seconda //del flag relativo impostato nel database. for (int i=0;i<15;i++) { for (int j=0;j<5;j++) { if (posto.getLibero((i*20)+j)==1) {stamp=Color.green;} else {stamp=Color.red;} posti1[i][j]=new HText("O"); posti1[i][j].setForeground(stamp); posti1[i][j].setFont( new Font("Arial",Font.BOLD,20)); posti1[i][j].setBackgroundMode( HVisible.NO_BACKGROUND_FILL); posti1[i][j].setBounds(x+j*20,y+i*20,width,length); System.out.println("creato punto "+i+" "+j); } } for (int i=0;i<15;i++) { for (int j=0;j<5;j++) { System.out.println("riga "+i+" colonna "+j); //Visualizza la piantina dei posti. container.add(posti1[i][j]); - 135 - Sviluppo di un’applicazione MHP Capitolo 4 System.out.println("aggiunto al container punto "+i+" "+j); } } x=358; for (int i=0;i<15;i++) { for (int j=5;j<15;j++) { if (posto.getLibero((i*20)+j)==1) {stamp=Color.green;} else {stamp=Color.red;} posti1[i][j]=new HText("O"); posti1[i][j].setForeground(stamp); posti1[i][j].setFont( new Font("Arial",Font.BOLD,20)); posti1[i][j].setBackgroundMode( HVisible.NO_BACKGROUND_FILL); posti1[i][j].setBounds(x+(j-5)*20,y+(i)*20, width,length); System.out.println("creato punto "+i+" "+j); } } for (int i=0;i<15;i++) { for (int j=5;j<15;j++) { System.out.println("riga "+i+" colonna "+j); container.add(posti1[i][j]); System.out.println("aggiunto al container punto - 136 - Sviluppo di un’applicazione MHP Capitolo 4 "+i+" "+j); } } x=570; for (int i=0;i<15;i++) { for (int j=15;j<20;j++) { if (posto.getLibero((i*20)+j)==1) {stamp=Color.green;} else {stamp=Color.red;} posti1[i][j]=new HText("O"); posti1[i][j].setForeground(stamp); posti1[i][j].setFont( new Font("Arial",Font.BOLD,20)); posti1[i][j].setBackgroundMode( HVisible.NO_BACKGROUND_FILL); posti1[i][j].setBounds(x+(j-15)*20,y+(i)*20, width,length); System.out.println("creato punto "+i+" "+j); } } for (int i=0;i<15;i++) { for (int j=15;j<20;j++) { System.out.println("riga "+i+" colonna "+j); container.add(posti1[i][j]); - 137 - Sviluppo di un’applicazione MHP Capitolo 4 System.out.println("aggiunto al container punto "+i+" "+j); } } container.add(indietro); container.add(conferma); container.add(aggiorna); posti1 [0][0].setBackground(Color.yellow); posti1 [0][0].setBackgroundMode( HVisible.BACKGROUND_FILL); postoAttivoInizio[0]=0; postoAttivoInizio[1]=0; postoAttivo[0]=0; postoAttivo[1]=0; container.setVisible(true); scene.add(container); scene.repaint(); cont=1; } //Imposta lo sfondo del posto selezionato di colore giallo. public void setAttivo(int i, int j) { posti1[postoAttivoInizio[0]][postoAttivoInizio[1]] .setBackgroundMode( HVisible.NO_BACKGROUND_FILL); posti1[i][j].setBackground(Color.yellow); - 138 - Sviluppo di un’applicazione MHP Capitolo 4 posti1[i][j].setBackgroundMode( HVisible.BACKGROUND_FILL); postoAttivoInizio[0]=i; postoAttivoInizio[1]=j; scene.repaint(); } public void passaEvento(KeyEvent key) { int pulsantePremuto = key.getKeyCode(); switch(pulsantePremuto){ case HRcEvent.VK_UP: if (postoAttivo[0]<=14 && postoAttivo[0]>0) postoAttivo[0]--; this.setAttivo(postoAttivo[0],postoAttivo[1]); break; case HRcEvent.VK_DOWN: if (postoAttivo[0]>=0 && postoAttivo[0]<14) postoAttivo[0]++; this.setAttivo(postoAttivo[0],postoAttivo[1]); break; case HRcEvent.VK_LEFT: System.out.println("Premuta freccia sinistra in platea"); if (postoAttivo[1]<=19 && postoAttivo[1]>0) postoAttivo[1]--; this.setAttivo(postoAttivo[0],postoAttivo[1]); break; case HRcEvent.VK_RIGHT: System.out.println("Premuta freccia destra in platea"); - 139 - Sviluppo di un’applicazione MHP Capitolo 4 if (postoAttivo[1]>=0 && postoAttivo[1]<19) postoAttivo[1]++; this.setAttivo(postoAttivo[0],postoAttivo[1]); break; case HRcEvent.VK_ENTER: System.out.println("premuto enter in platea"); int numero=(postoAttivo[0]*20)+postoAttivo[1]+1; if (posto.getLibero(numero-1)==1) { //Se viene premuto OK ed il posto è libero crea //un oggetto della classe PrenotaSingolo. PrenotaSingolo pren=new PrenotaSingolo( user,numero,posto.getCodice()); PlateaPrenota pp=new PlateaPrenota(pren); String ris=(String)pp.start(); conferma.setTextContent( ris,HVisible.NORMAL_STATE); } else { //Se viene premuto OK ed il posto è occupato, viene //visualizzato un messaggio di errore. conferma.setTextContent("Posto occupato.", HVisible.NORMAL_STATE); } conferma.setBounds(250,150,400,50); container.removeAll(); HText blu=new HText("Indietro",310,250,130,30); blu.setFont(new Font("Arial",Font.BOLD,20)); blu.setBackground(Color.blue); - 140 - Sviluppo di un’applicazione MHP Capitolo 4 blu.setForeground(Color.white); blu.setBackgroundMode( HVisible.BACKGROUND_FILL); container.add(blu); indietro.setBounds(450,250,130,30); container.add(conferma); container.add(indietro); scene.repaint(); break; case HRcEvent.VK_COLORED_KEY_3: container.removeAll(); break; default: break; } } } 4.4.14 Info.java import java.awt.Color; import java.awt.Font; import java.awt.Toolkit; import java.awt.event.KeyEvent; import org.havi.ui.*; import org.havi.ui.event.HRcEvent; - 141 - Sviluppo di un’applicazione MHP Capitolo 4 //Questa classe visualizza alcune informazioni utili //relative al cinema. public class Info extends Strumenti{ private HText testo; private HText indietro; public Info(HScene scenemain) { super(scenemain); } public void start() { container=new HContainer(0,0,720,576); testo=new HText("Il Cinema ITALIA è stato costruito nel 2000.\n"+"E' una struttura molto moderna composta\n" +"da 3 sale grandi e 2 sale piccole nelle quali \npotrete godervi il film " + "con grande comodità.\n" +"Può trovarci in Ancona in via Garibaldi n. 29." +"\n\nTelefono: 071000000\n\n" + "E-mail: [email protected]", 250,100,450,500); testo.setFont(new Font("Arial",Font.PLAIN,22)); testo.setHorizontalAlignment(HVisible.HALIGN_LEFT); testo.setVerticalAlignment(HVisible.HALIGN_LEFT); testo.setBackgroundMode( HVisible.NO_BACKGROUND_FILL); testo.setForeground(Color.white); container.add(testo); - 142 - Sviluppo di un’applicazione MHP Capitolo 4 indietro= new HText ("Indietro",380,430,130,30); indietro.setFont(new Font("Arial",Font.BOLD,20)); indietro.setForeground(Color.blue); indietro.setBordersEnabled(false); indietro.setBackground(Color.green); indietro.setBackgroundMode(HVisible.BACKGROUND_FILL); container.add(indietro); System.out.println("Aggiunto indietro"); container.setVisible(true); scene.add(container); scene.repaint(); super.setVisible(true); } public void stop() { this.setVisible(false); container.removeAll(); container.setVisible(false); scene.remove(container); System.out.println("Stop "+this.toString()); Main.gestoreSfondi.displayBackgroundInitImage(); scene.repaint(); } public void passaEvento(KeyEvent key) { int pulsantePremuto = key.getKeyCode(); switch(pulsantePremuto){ case HRcEvent.VK_UP: - 143 - Sviluppo di un’applicazione MHP Capitolo 4 case HRcEvent.VK_DOWN: case HRcEvent.VK_COLORED_KEY_0: case HRcEvent.VK_0: case HRcEvent.VK_1: case HRcEvent.VK_2: case HRcEvent.VK_3: case HRcEvent.VK_4: case HRcEvent.VK_5: case HRcEvent.VK_6: case HRcEvent.VK_7: case HRcEvent.VK_8: case HRcEvent.VK_9: case HRcEvent.VK_LEFT: case HRcEvent.VK_COLORED_KEY_1: case HRcEvent.VK_COLORED_KEY_2: case HRcEvent.VK_COLORED_KEY_3: case HRcEvent.VK_ENTER: default: break; } } } - 144 - CONCLUSIONI Il progetto sopra illustrato, è stato realizzato non senza difficoltà; la più impegnativa è stata sicuramente la scoperta che lo standard MHP non supporta il JDBC. Il problema è stato ovviato tramite l’utilizzo delle socket, necessarie per connettersi al database MySQL residente in un server. E’ stato necessario apprendere il funzionamento delle socket tramite le API Java. L’obiettivo iniziale prefisso, cioè quello di sviluppare un’applicazione MHP che utilizzasse il canale di ritorno, è stato raggiunto. Si è riusciti a creare un’applicazione perfettamente funzionante su Set-Top Box che soddisfa i criteri fissati inizialmente. Il risultato è, pertanto, soddisfacente, sebbene possa anche rappresentare una base per ulteriori sviluppi, quali utilizzare il modem PSTN come canale di ritorno, invece che la ETHERNET, oppure si può pensare di permettere al server di gestire il multithread. Il lavoro svolto, dunque, è stato molto soddisfacente ed ha permesso, oltre ad approfondire il linguaggio Java, di avere una certa formazione inerente una tecnologia, quale la TV digitale terrestre, che potrebbe avere un’importante evoluzione con conseguenti opportunità di lavoro e di ricerca. Questa tecnologia, infatti, sembra essere molto innovativa e soprattutto non presenta elevati costi di installazione (caratteristica molto importante per la diffusione della stessa), in quanto è necessario solamente comprare il Set-Top Box, senza bisogno di avere una parabola, dato che i canali possono essere ricevuti senza problemi tramite la classica antenna TV. Si pensi, poi, alle innumerevoli possibilità di utilizzo della televisione digitale terrestre: in un prossimo futuro si potrebbe navigare in Internet, controllare il proprio conto in banca, effettuare operazioni di e-commerce, scommettere, ecc., - 145 - comodamente seduti sulla poltrona di casa. Questa rappresenta un affascinante scenario che si potrebbe proporre grazie a questa tecnologia. Ultimamente, in Italia, la TV digitale terrestre si sta diffondendo soprattutto per la possibilità, concessa da alcune delle maggiori emittenti televisive nazionali, di vedere partite di calcio, film, o altri eventi a pagamento, senza necessità di attivare alcun abbonamento: l’utente paga solamente ciò che vede. Questo è possibile grazie alla caratteristica del Set-Top Box di avere un lettore di smart-card. L’utente compra una scheda (ricaricabile) di un certo importo e, tramite la stessa, è abilitato alla visioni di determinati canali e, in particolare, di alcuni eventi a pagamento. Il credito disponibile è memorizzato nella smart-card e ad ogni evento scelto, il costo dello stesso viene detratto dal credito della scheda. Il vero problema, attualmente, è che praticamente quasi nessuna emittente trasmette esclusivamente in digitale, e molto probabilmente la situazione resterà così fino a quando non si arriva alla data dello switch-off (spegnimento totale delle trasmissioni in analogico), data ancora non certa. Forse, sarebbe necessario che lo stato fornisse qualche incentivo alle emittenti TV per passare al digitale. La televisione digitale terrestre, in conclusione, rappresenta una piattaforma tecnologica che, oltre a migliorare la vita dei cittadini, potrebbe fornire diverse opportunità di lavoro e molte possibilità di sviluppo. - 146 - BIBLIOGRAFIA http://www.mhp.org http://www.dvb.org http://www.havi.org http://java.sun.com/products/javatv http://sourceforge.net/project/xletview http://www.wikipedia.it http://www.mokabyte.it http://www.interactiveweb.org http://www.wmlscript.it Steve Morris, Anthony Smith-Chaigneau, Interactive TV Standards, Focal Press Libro bianco sulla televisione digitale terrestre - 147 -