Sviluppo di applicazioni integrate con il canale di ... · Sviluppo di applicazioni integrate con...

100

Transcript of Sviluppo di applicazioni integrate con il canale di ... · Sviluppo di applicazioni integrate con...

Sviluppo di applicazioni integrate con il canale

di lettura ottica per piattaforma Android

Laureando: Marco Ciacco 578487Relatore: Ch.mo Prof. Federico FiliraCorso di laurea: Ingegneria Informatica

Data Laurea 26 Luglio 2012Anno accademico: 2011/2012

2

Indice

1 Introduzione 71.1 Obiettivi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71.2 Primo Approccio . . . . . . . . . . . . . . . . . . . . . . . . . 71.3 PhoneGap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7

1.3.1 Cos'è phonegap? . . . . . . . . . . . . . . . . . . . . . 71.3.2 I vantaggi di PhoneGap . . . . . . . . . . . . . . . . . 71.3.3 Gli svantaggi di PhoneGap . . . . . . . . . . . . . . . . 8

1.4 Codice nativo Android (Java) . . . . . . . . . . . . . . . . . . 81.4.1 I vantaggi . . . . . . . . . . . . . . . . . . . . . . . . . 91.4.2 Gli svantaggi . . . . . . . . . . . . . . . . . . . . . . . 9

2 Sviluppo di un Hello World 112.1 Installazione e con�gurazione Eclipse e ADT . . . . . . . . . . 11

2.1.1 Installare AVD . . . . . . . . . . . . . . . . . . . . . . 122.2 Sviluppo con Java . . . . . . . . . . . . . . . . . . . . . . . . . 122.3 Sviluppo con PhoneGap . . . . . . . . . . . . . . . . . . . . . 13

3 Sviluppo dell'applicazione Bollettino 153.1 Progettazione . . . . . . . . . . . . . . . . . . . . . . . . . . . 15

3.1.1 La scelta tra PhoneGap e Java . . . . . . . . . . . . . . 153.1.2 La ricerca di un OCR: Tesseract . . . . . . . . . . . . . 16

3.2 Inserimento di Tesseract nel progetto . . . . . . . . . . . . . . 163.2.1 L'NDK . . . . . . . . . . . . . . . . . . . . . . . . . . . 163.2.2 tesseract-android-tools . . . . . . . . . . . . . . . . . . 18

3.3 Scheletro di un applicazione Java . . . . . . . . . . . . . . . . 203.3.1 Gestione di base . . . . . . . . . . . . . . . . . . . . . . 203.3.2 La schermata principale . . . . . . . . . . . . . . . . . 223.3.3 Assegnare le azioni ai bottoni . . . . . . . . . . . . . . 253.3.4 Activity e Cambio di Schermata . . . . . . . . . . . . . 26

3.4 Prime Implementazioni . . . . . . . . . . . . . . . . . . . . . . 313.4.1 Acquisire un'immagine dalla Fotocamera . . . . . . . . 31

3

4 INDICE

3.4.2 E�ettuare il riconoscimento dei caratteri . . . . . . . . 333.5 Riconoscimento del bollettino postale . . . . . . . . . . . . . . 37

3.5.1 Ideazione . . . . . . . . . . . . . . . . . . . . . . . . . 373.5.2 Individuazione posizione dei simboli con l'euro . . . . . 383.5.3 Estrapolazione delle Informazioni . . . . . . . . . . . . 423.5.4 L'activity . . . . . . . . . . . . . . . . . . . . . . . . . 45

3.6 Invio delle informazioni . . . . . . . . . . . . . . . . . . . . . . 463.6.1 Layout per editare le informazioni . . . . . . . . . . . . 463.6.2 lettura del codice XML . . . . . . . . . . . . . . . . . . 513.6.3 SMS . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52

4 Sviluppo dell'Applicazione Codice a Barre 574.1 Obiettivi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 574.2 I codici a barre EAN-13 . . . . . . . . . . . . . . . . . . . . . 57

4.2.1 Generazione della cifra di controllo . . . . . . . . . . . 574.3 La libreria Zxing e L'applicazione Barcode Scanner . . . . . . 59

4.3.1 Introduzione . . . . . . . . . . . . . . . . . . . . . . . . 594.4 Integrazione di Barcode Scanner col programma . . . . . . . . 59

4.4.1 Utilizzo di barcode Scanner non incluso nell'applicazione 594.4.2 Inserimento di barcode Scanner nel progetto . . . . . . 604.4.3 Creazione di un intentFilter . . . . . . . . . . . . . . . 60

4.5 Creazione e immagazzinamento dati in un DataBase SQLite . 614.6 Creare una lista di elementi gra�ci . . . . . . . . . . . . . . . 63

4.6.1 Creazione dell'elemento . . . . . . . . . . . . . . . . . . 634.6.2 Duplicazione elementi gra�ci . . . . . . . . . . . . . . . 644.6.3 ContextMenu . . . . . . . . . . . . . . . . . . . . . . . 65

4.7 Generazione dei codici a barre EAN-13 . . . . . . . . . . . . . 664.7.1 La codi�ca EAN-13 . . . . . . . . . . . . . . . . . . . . 674.7.2 La realizzazione dell'immagine . . . . . . . . . . . . . . 68

5 Acquisto biglietti 735.1 Obiettivi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 735.2 Ideazione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73

5.2.1 La località . . . . . . . . . . . . . . . . . . . . . . . . . 735.2.2 Scelte Successive . . . . . . . . . . . . . . . . . . . . . 735.2.3 Pagamento . . . . . . . . . . . . . . . . . . . . . . . . 74

5.3 Realizzazione . . . . . . . . . . . . . . . . . . . . . . . . . . . 745.3.1 disclaimer . . . . . . . . . . . . . . . . . . . . . . . . . 745.3.2 Salvataggio Preferenze . . . . . . . . . . . . . . . . . . 755.3.3 Selezione . . . . . . . . . . . . . . . . . . . . . . . . . . 765.3.4 Reperimento dati . . . . . . . . . . . . . . . . . . . . . 84

INDICE 5

5.3.5 Layout biglietto multiplo . . . . . . . . . . . . . . . . . 855.3.6 Layout Riepilogo . . . . . . . . . . . . . . . . . . . . . 88

6 Pubblicazione dell'applicazione 936.1 Creazione �le .apk . . . . . . . . . . . . . . . . . . . . . . . . 936.2 Invio dell'applicazione . . . . . . . . . . . . . . . . . . . . . . 95

7 Conclusioni 977.1 Uno sguardo al futuro . . . . . . . . . . . . . . . . . . . . . . 97

7.1.1 Cellulare VS PC . . . . . . . . . . . . . . . . . . . . . 977.2 Potenzialità . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98

6 INDICE

Capitolo 1

Introduzione

1.1 Obiettivi

L'obiettivo dello stage é creare un'applicazione per cellulari in grado di sem-pli�care i pagamenti di: Bollette, Bollettini, Multe cercando il piú possibiledi automatizzare il processo.

L'idea é quella di scattare una foto con il cellulare al documento (bollettao multa che sia) e in automatico generare un SMS o un testo che ne consentail pagamento.

1.2 Primo Approccio

La prima decisione da a�rontare é scegliere come realizzare l'Applicazio-ne, ovvero se realizzarla con il linguaggio nativo (java) oppure con unostrumento per lo sviluppo multi piattaforma.

1.3 PhoneGap

1.3.1 Cos'è phonegap?

PhoneGap é una Piattaforma di sviluppo applicazioni in HTML5 che per-mette di creare una sola applicazione da pubblicare su 6 piattaforme diverse:iOS, Android, Blackberry, Palm, WinMobile e Symbian.

1.3.2 I vantaggi di PhoneGap

I vantaggi di questo tipo di piattaforma sono lampanti:

7

8 CAPITOLO 1. INTRODUZIONE

Write Once Come già anticipato consente di realizzare un applicazione chepuò funzionare su praticamente tutti i tipi di cellulare piú di�usi in accordocon la �loso�a write once run anywhere.

Html, css e javascript Semplicità nella creazione di un interfaccia gra�cacon la possibilità di utilizzare gli editor html per la creazione/visualizzazio-ne/test dell'applicazione prima che questa venga inviata al cellulare.

Api Nonostante sia multi piattaforma si può accedere in maniera relati-vamente facile alle periferiche del cellulare come fotocamera, connessione direte, GPS, invio SMS ecc...

1.3.3 Gli svantaggi di PhoneGap

Pesantezza e Ine�cienza Il codice sorgente inserito in html non vienericodi�cato/compilato per funzionare sulla piattaforma, ma semplicementeinterpretato al momento1, quindi l'e�cienza é notevolmente ridotta. Nel-l'applicazione, oltre ai codici sorgenti scritti in html, va inviato anche tuttoil software di phonegap che consente l'utilizzo delle API2 del cellulare.

Di�coltà di debugging Nonostante phonegap metta a disposizione undebugger molto valido, quando il numero di righe di codice inizia a diventareconsistente si fa di�cile l'individuazione di eventuali errori. Inoltre, peralcune piattaforme, il testing su macchina virtuale può dare risultati moltodiversi da quelli che si ottengono su una macchina �sica.

1.4 Codice nativo Android (Java)

Le applicazioni che girano in Android vengono scritte in Java, ovviamentecon alcuni accorgimenti per quanto riguarda la gra�ca (che viene de�nita inxml, come descritto in seguito).

1un po' come fa il browser del computer durante con il codice html o javascript2Con Application Programming Interface (API) si indica ogni insieme di procedure

disponibili al programmatore, di solito raggruppate a formare un set di strumenti speci�ciper l'espletamento di un determinato compito all'interno di un certo programma.

1.4. CODICE NATIVO ANDROID (JAVA) 9

1.4.1 I vantaggi

E�cienza Scrivere codice nel linguaggio nativo del OS Android ci garanti-sce la massima e�cienza, inoltre non é necessario inviare pacchetti aggiuntivia�nché l'applicazione funzioni.

Supporto Il supporto e le guida per la creazione di applicazioni é decisa-mente piú ampio di quello di PhoneGap. La presenza di un IDE 3 avanzatocome Eclipse 4 aiuta decisamente lo sviluppo dell'applicazione; vedremo inseguito come.

Disponibilità completa API Le API5 del cellulare sono accessibili inmaniera relativamente sempli�cata e allo stesso tempo approfondita, vedremomeglio nel resto della tesi come ciò sia possibile.

1.4.2 Gli svantaggi

Portabilità Il codice scritto per Android gira ovviamente solo su cellulariAndroid. Per poter essere eseguito su un tipo diverso di cellulare necessitauna riscrittura completa.

Linguaggio É necessario imparare come funziona nello speci�co un'appli-cazione Android e la �loso�a con cui dev'essere strutturata.

3Integrated Development Environmentin, in italiano: ambiente di sviluppo integra-to, (conosciuto anche come integrated design environment o integrated debugging en-vironment, rispettivamente ambiente integrato di progettazione e ambiente integrato didebugging) é un software che aiuta i programmatori nello sviluppo del codice.

4Eclipse é un ambiente di sviluppo integrato multi-linguaggio e multi piattaforma. Idea-to da un consorzio di grandi società quali Ericsson, HP, IBM, Intel, MontaVista Software,QNX, SAP e Serena Software, chiamato Eclipse Foundation sullo stile dell'open source.

5Application Programming Interface API (Interfaccia di Programmazione di una ap-plicazione) si indica ogni insieme di procedure disponibili al programmatore, di solitoraggruppate a formare un set di strumenti speci�ci per l'espletamento di un determinatocompito all'interno di un certo programma.

10 CAPITOLO 1. INTRODUZIONE

Capitolo 2

Sviluppo di un Hello World

Che si scelga PhoneGap oppure l'applicazione nativa, il modo piú facileper testare l'applicazione é sicuramente quello di utilizzare Eclipse, quindivediamo come con�guralo a�nché sviluppi per Android.

2.1 Installazione e con�gurazione Eclipse e ADT

Poniamo che sul computer sia installato l'ultimo JDK1. Come prima cosa

dobbiamo scaricare Eclipse IDE for Java Developers2 estrarlo ed eseguirlo.Al primo avvio ci chiede il path del nostro spazio di lavoro (workspace),scegliamo e clicchiamo su avanti. A questo punto dobbiamo scaricare l'SDK3.A seconda del sistema operativo usato si scarica e estrae il programma.

A questo punto dobbiamo installare l'ADT 4: apriamo Eclipse e andia-mo su Help > Install New Software. Si aprirá una �nestra in cui dobbiamoinserire il server da cui vogliamo scaricare il plugin. Inseriamo questo in-dirizzo https://dl-ssl.google.com/android/eclipse5 e selezioniamo tutti e 4 ipacchetti da installare.

Ora riavviamo Eclipse e selezioniamo Window ⇒ Preferences... (Mac OSX: Eclipse ⇒ Preferences), nelle schede di sinistra clicchiamo su Android.

1Java Development Kit, Il compilatore java, disponibile suhttp://www.oracle.com/technetwork/java/javase/downloads/index.html

2Il programma è disponibile sul sito http://www.eclipse.org/downloads/3Software Development Kit (piú brevemente SDK) é un termine che in italiano si

può tradurre come pacchetto di sviluppo per applicazioni, e indica un insieme di stru-menti per lo sviluppo e la documentazione di software. Scaricabile a questo indirizzohttp://developer.android.com/sdk/index.html

4Android Development Tools (ADT) é un plugin per Eclipse IDE che é stato progettatoper fornire un potente e integrato strumento con cui sviluppare applicazioni Android

5in caso il download non dovesse partire, rimuovere pure la s da https

11

12 CAPITOLO 2. SVILUPPO DI UN HELLO WORLD

Impostiamo il percorso dove abbiamo estratto l'SDK, premiamo OK e quindiSalva.

2.1.1 Installare AVD

AVD é l'acronimo di Android Virtual Device, necessario per testare agevol-mente le applicazioni. Per installarlo andiamo da eclipse, Window⇒ AndroidSdk Manager, andiamo a mettere la spunta sul pacchetto che ci interessa, nelnostro caso Android 2.2 (API 8) e clicchiamo su install package. Dopo l'in-stallazione andiamo in Window ⇒ AVD Manager e clicchiamo su New. Aquesto punto decidiamo che macchina virtuale creare -noi scegliamo Android2.2- digitiamo un nome a piacere e impostiamo la dimensione della SD (io hoimpostato 100Mb e mi sono bastati). A questo punto per avviare la macchinaclicchiamo su start e attendiamo il tempo di caricamento (circa 1-2 minuti):abbiamo un cellulare virtuale sul computer a nostra disposizione.

2.2 Sviluppo con Java

Adesso che abbiamo la macchina virtuale installata e tutto il resto collegatopossiamo iniziare a creare la prima applicazione di prova. Crearla con eclipseé immediato e veloce. Andiamo su File ⇒ New Project... e selezioniamoAndroid Project, scegliamo il nome, la versione di android per cui lo si vuolesviluppare (nel nostro caso Android 2.2). In�ne scegliamo il nome del pac-kage6 composto da almeno due nomi 7 (nel nostro caso telerete.software) e

6Un Package rappresenta una collezione di classi ed interfacce che possono essereraggruppate in base alla funzione comune da esse svolta.

7I Package sono di solito de�niti usando una struttura gerarchica, indicando i livellidi gerarchia con dei punti. Anche se i package più in basso nella gerarchia sono spessochiamati sotto-package di altri package, non c'è nessuna relazione semantica. Il documentoJava Language Speci�cation stabilisce le convenzioni da adottare nei nomi dei package, cosìda evitare di pubblicarne due con lo stesso nome. Le convenzioni descrivono come crearenomi unici, in modo da assicurare che package di uso generale e di larga distribuzione nonabbiano nomi che possano generare ambiguità.In generale, un nome comincia con il dominio di primo livello dell'organizzazione che

lo produce, seguito dal dominio e da altri eventuali sottodomini, elencati in ordine inver-so. L'organizzazione può in�ne scegliere un nome speci�co per quel particolare package.Inoltre, sempre per convenzione, i nomi dei package dovrebbero contenere solo lettereminuscole.Ad esempio, se un'organizzazione canadese chiamata MySoft crea un package che si

occupa di frazioni, chiamare il package ca.mysoft.fractions lo distingue da un altro packagesimile creato da un'altra compagnia. Infatti se una compagnia americana omonima creaun package sulle frazioni, ma lo chiama com.mysoft.fractions, le classi nei due packagesaranno tutte de�nite in namespace separati ed unici.

2.3. SVILUPPO CON PHONEGAP 13

premiamo su �ne.

Versione di Android Ho scelto la versione di Android 2.2 perché é laversione minima compatibile con ció che dovremo fare. Ci tengo a precisareche un applicazione creata per una versione é sicuramente compatibile conle versioni successive, ma non necessariamente con quelle precedenti. An-droid 2.1 e precedenti non supportano alcune API di cui avremo bisogno, mavedremo in dettaglio piú avanti.

Abbiamo creato un progetto vuoto già funzionante, adesso andiamo a pro-varlo sulla nostra brava macchina virtuale. Come prima cosa andiamo su run⇒ Run con�gurations. Nella scheda Android nel campo Project clicchiamosu Browse e scegliamo il nome della nostra applicazione, nel campo sotto-stante Launch Activity selezioniamo launch, clicchiamo sul menú a tendinae selezioniamo l'unica cosa selezionabile; nella scheda target selezioniamoAutomatic,in�ne mettiamo la spunta alla nostra macchina virtuale creatae clicchiamo su run. A questo punto dopo qualche secondo sulla macchinavirtuale apparirá la nostra prima applicazione.

2.3 Sviluppo con PhoneGap

Abbiamo visto nel paragrafo precedente come creare un Hello World in Java.Ora vediamo di crearne uno con PhoneGap. Possiamo scegliere di creare unnuovo progetto oppure utilizzare lo stesso che abbiamo usato in precedenza.Nel caso scegliessimo di crearne uno nuovo bisogna ripetere ció che abbiamofatto in 2.2, a questo punto modi�chiamo il metodo OnCreate nel modoseguente.

1 pub l i c c l a s s app extends DroidGap {/∗∗ Cal led when the a c t i v i t y i s f i r s t c r ea ted . ∗/

3 @Overridepub l i c void onCreate ( Bundle savedIns tanceState ) {

5 super . onCreate ( savedIns tanceSta te ) ;super . loadUrl ( " f i l e : /// android_asset /www/ index . html" ) ;

7 }}

Altre convenzioni per evitare le ambiguità e regole per dare nomi ai package quando indominio Internet non può essere direttamente usato nel nome sono descritte nella sezione7.7 della Java Language Speci�cation.Eclipse non accetta meno di due nomi separati da punto. Questo vuol dire che è im-

possibile scrivere codice per un package di primo livello. Questa scelta progettuale èprobabilmente stata fatta per evitare che dei package abbiano lo stesso nome e vadano incon�itto

14 CAPITOLO 2. SVILUPPO DI UN HELLO WORLD

Adesso dobbiamo scaricare PhoneGap dal sito http://phonegap.com. Se-guiamo la guida che troviamo su http://phonegap.com/start#android pergenerare l'applicazione.

Capitolo 3

Sviluppo dell'applicazione

Bollettino

3.1 Progettazione

Come spiegato in 1.1, dovremmo essere in grado di:

• Scattare la foto

• Analizzare la foto estrapolando le informazioni (OCR1)

• Produrre una stringa da inviare via SMS

3.1.1 La scelta tra PhoneGap e Java

Ora che abbiamo provato entrambe le strade abbiamo gli elementi per capirequale strada prendere.

Innanzitutto per produrre un'applicazione che utilizzi l'OCR é necessa-rio trovare una libreria che ci consenta di farlo. Dopo una breve ricerca ininternet ci si rende conto che una libreria scritta in javascript non esiste erealizzarla non é una soluzione accettabile considerato il tempo a disposizio-ne. Il fatto che non esista libreria javascript non é un ostacolo dato che conPhoneGap tramite il metodo PhoneGap.exec() é possibile eseguire metodiJava. Il problema é il seguente: per ogni libreria non javascript che si uti-lizza bisogna trovarne una compatibile per ogni tipo di telefono. Quindi siperderebbe la portabilità dell'applicazione.

1I sistemi di riconoscimento ottico dei caratteri, detti anche OCR (dall'inglese opticalcharacter recognition) sono programmi dedicati alla conversione di un'immagine conte-nente testo, solitamente acquisita tramite scanner, in testo digitale modi�cabile con unnormale editor.

15

16 CAPITOLO 3. SVILUPPO DELL'APPLICAZIONE BOLLETTINO

In secondo luogo, questa applicazione deve riconoscere delle immagini,scansionarle e dare un output in termini di posizioni. Questo richiede unagrande velocità di calcolo se si vuole che il processo termini in tempi utili, eabbiamo spiegato prima che la velocità di esecuzione di javascript é parecchioinferiore a Java.

Per queste ragioni ho scelto di utilizzare Java.

3.1.2 La ricerca di un OCR: Tesseract

Il primo obiettivo per la ricerca di un OCR era quello di trovare una libreriaOCR Java. La prima che poteva fare al caso nostro è Java Object OrientedNeural Engine: il suo problema é che usa delle librerie di Java puro nonsupportate da Android.

Dopo numerose ricerche ho trovato Tesseract, un progetto OCR opensource. Sviluppato originariamente come software proprietario dalla Hewlett-Packard tra il 1985 e il 1995, non venne piú aggiornato nel decennio suc-cessivo. Fu poi rilasciato come open source nel 2005 da Hewlett Packarde dall'Università del Nevada, Las Vegas, e rilasciato con la licenza Apache,versione 2.0. Lo sviluppo di Tesseract é attualmente sponsorizzato da Google[Tesseract - Wikipedia].

3.2 Inserimento di Tesseract nel progetto

Il problema di Tesseract é che non é scritto in java, bensì in C++. Sappiamoche in java è possibile l'inserimento di applicazioni native (già compilate)che in Java normalmente servono per eseguire operazioni non supportatedalle API native di Java (vedi ad esempio ridurre a icona una �gura). Perandroid sarà lo stesso? Sì, grazie allo strumento messo a disposizione daglisviluppatori android: L'NDK.

3.2.1 L'NDK

Cos'è l'NDK L'Android NDK2 (native development kit) é uno strumen-to che consente di inserire del codice nativo in un'applicazione Android. Lamiglior guida che ho trovato per imparare ad utilizzare questo strumento é di-sponibile nella bibliogra�a sotto la voce [Marakana - Using NDK to Call C code from Android Apps].Leggerla non é fondamentale ai �ni della creazione del progetto ma aiuta dimolto a comprendere come lavora L'NDK. La cosa piú interessante da capire

2Scaricabile all'indirizzo http://developer.android.com/sdk/ndk/index.html

3.2. INSERIMENTO DI TESSERACT NEL PROGETTO 17

é la variabile env che viene passata dalla JVM 3 . Questa permette di tra-sformare oggetti, stringhe e valori dal C/C++ al Java e vice versa. Questopassaggio è necessario perché Java gestisce gli oggetti in maniera diversa daC/C++.

La variabile env) Poniamo ad esempio di creare un metodo moltosemplice in C++ a cui venga passata una stringa.

St r ing s t r= " h e l l o " ;2 chiamataMetodoNativo ( s t r ) ;

Quando Java crea la stringa hello la salverà con la sua codi�ca in uno spazioin memoria atto a contenerla, il cui indirizzo verrà salvato nella variabile str.Questo indirizzo viene in�ne passato al metodo nativo C/C++.

//C++ code2 extern "C"

JNIEXPORT void JNICALL Java_ClassName_chiamataMetodoNativo4 (JNIEnv ∗env , j o b j e c t obj , j s t r i n g j avaSt r i ng )

{6 //Get the nat ive s t r i n g from javaSt r ing

const char ∗ na t i v eS t r i ng =env−>GetStringUTFChars ( javaStr ing , 0) ;

8

//Do something with the na t i v eS t r i ng10

//DON'T FORGET THIS LINE ! ! !12 env−>ReleaseStringUTFChars ( javaStr ing , na t i v eS t r i ng ) ;

}14 /∗−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−∗/

/∗C code ∗/16 JNIEXPORT void JNICALL Java_ClassName_chiamataMetodoNativo

(JNIEnv ∗env , j o b j e c t obj , j s t r i n g j avaSt r i ng )18 {

/∗Get the nat ive s t r i n g from javaSt r ing ∗/20 const char ∗ na t i v eS t r i ng = (∗ env )−>GetStringUTFChars ( env ,

javaStr ing , 0) ;

22 /∗Do something with the na t i v eS t r i ng ∗/

24 /∗DON'T FORGET THIS LINE ! ! ! ∗/(∗ env )−>ReleaseStringUTFChars ( env , javaStr ing ,

na t i v eS t r i ng ) ;26 }

3Java Virtual Machine

18 CAPITOLO 3. SVILUPPO DELL'APPLICAZIONE BOLLETTINO

N.B.: Il codice appena visto sarà l'implementazione di un'interfaccia C/C++generata dalla compilazione con javah.

La variabile env è in grado di convertire la stringa in modo che C/C++sia in grado di elaborarla. (tratto da [Java Native Interface - Wikipedia])

Con queste informazioni si é in grado di portare qualsiasi applicazioneC/C++ in java, anche tesseract. Fare un lavoro del genere di interfaccia-mento é un lavoro lungo e impegnativo, per fortuna qualcuno ci ha messo adisposizione tesseract-android-tools, un progetto java/c++ praticamente giàpronto per essere compilato

3.2.2 tesseract-android-tools

Tesseract android tools, come ho già, detto sempli�ca l'importazione di tesse-ract nel progetto. La guida che ho utilizzato per realizzarlo è disponibile nellabibliogra�a [Using Tesseract Tools for Android to Create a Basic OCR App].

Nonostante sia ben fatta io ho trovato numerosi problemi nella compila-zione del progetto. Ecco gli accorgimenti che ho dovuto fare per rendere ilprogetto compilabile.

Innanzitutto per avviare ndk build é necessario impostare il percorso nelpath di sistema. Quando si scarica il progetto è preferibile posizionarlo nelworkspace di Eclipse per evitare complicazioni.

Durante la compilazione mi dava errori, ecco come ho fatto per ovviareal problema in caso dovesse presentarsi:

Il �le mk Il �le con estensione .mk é un �le, di solito, generato automati-camente che indica al compilatore come eseguire la compilazione. //

Vediamo come modi�carlo per non fargli lanciare errori:La revision 6 di tesseract-android-tools ha una piccola modi�ca che lo

rende incompatibile con il �le readme al suo interno. Per ovviare al pro-blema navighiamo �no ad <percorsoWorkspaceEclipse>/android-tesseract-tools/jni/Android.mk e commentiamo (o se preferite cancelliamo) tutte lerighe prima di

i f e q "$ (TESSERACT_PATH)" ""2 $ ( e r r o r You must s e t the TESSERACT_PATH va r i ab l e to the

Tesse rac t source \d i r e c t o r y . See README and j n i /Android .mk f o r d e t a i l s )

4 end i f

3.2. INSERIMENTO DI TESSERACT NEL PROGETTO 19

salviamo e chiudiamo.Poi dobbiamo correggere i �le Android.mk nelle due sottocartelle

1. com_googlecode_leptonica_android

2. com_googlecode_tesseract_android

e per ogni �le Android.mk nelle 2 sottocartelle, modi�care la riga REAL_LOCAL_PATH:= $(call my-dir) in REAL_LOCAL_PATH := <percorsoWorkspaceEclipse>/tesseract-android-tools/$(call my-dir)

Una volta compilato tesseract con NDK, se non riusciamo a compilare il�le con ant (come scritto nella guida), lo si può fare con Eclipse nel seguen-te modo: si apre Eclipse e si importando i �le come indicato nella guida;sempre da Eclipse si clicca col tasto destro sulla cartella tesseract-android-tools ⇒ Properties, sulla barra di sinistra si clicca Android e si toglie laspunta da isLibrary e si salva. Si va su Run Con�guration, click col destrosu Android application e si preme new. A questo punto impostiamo comeproject tesseract-android-tools e impostiamo do nothing. Andiamo su starte controlliamo che nella consolle scriva tesseract-android-tools.apk installedon device. Ora rimettiamo la spunta a isLibrary e giunti a questo puntosiamo riusciti a generare l'apk di tesseract-android-tools. Nel nostro proget-to originale dobbiamo includere le librerie di tesseract: per fare ciò bastacliccare col destro sul nome della nostra applicazione a sinistra, andare inProperties, nella sezione Build Path e sulla scheda Project cliccare su add emettere la spunta su tesseract-android-tools. Clicchiamo in�ne su ok e sal-va. A questo punto siamo quasi pronti per far partire il riconoscimento maprima dobbiamo inviare il �le eng.traineddata (o ita.traineddata) nella car-tella /mnt/sdcard/tesseract/tessdata del telefono virtuale. Per fare questoabbiamo bisogno prima di tutto di aggiungere al path di linux o windows lacartella <vostro percorso>/android-sdks/platform-tools/. A questo puntopotete lanciare i comandi:

adb shell mkdir /mnt/sdcard/tesseractadb shell mkdir /mnt/sdcard/tesseract/tessdataadb push eng.traineddata(ita.traineddata) /mnt/sdcard/tesseract/tessda-

taI primi due comandi generano le cartelle tesseract e la sottocartella tessdata,il secondo invia il �le eng.traineddata (ita.traindeddata) nell'ultima cartellacreata.

Inseriamo nell'applicazione questo codice:

F i l e myDir = ge tEx t e rna lF i l e sD i r ( Environment .MEDIA_MOUNTED) ;2

TessBaseAPI baseApi = new TessBaseAPI ( ) ;

20 CAPITOLO 3. SVILUPPO DELL'APPLICAZIONE BOLLETTINO

4 baseApi . i n i t (myDir . t oS t r i ng ( ) , "eng" ) ; // oppure i t a a l posto d ieng

baseApi . setImage (myImage) ; //puo e s s e r e un perco r so o un Bitmapc o d i f i c a t o con ARGB_8888 a l t r imen t i dar \ ' a un e r r o r e

6

St r ing recognizedText = baseApi . getUTF8Text ( ) ; // Log orotherwi se d i sp l ay t h i s

8 Log . i ( "Testo Riconosc iuto " , recognizedText ) ;baseApi . end ( ) ;

Eseguendo questo codice e passandogli un'immagine nel LogCat4 si vedrà iltesto riconosciuto. Adesso possiamo inviare un �le di immagine jpg o png evedere il suo riconoscimento sulla consolle LogCat

3.3 Scheletro di un applicazione Java

3.3.1 Gestione di base

Nell' Hello World che abbiamo ottenuto in 2.2 notiamo subito che al postodel Main c'è una classe che estende la classe Activity e ne viene sovrascrittoil metodo onCreate(Bundle savedInstanceState). Questa é la prima cosa cheviene eseguita quando l'applicazione viene lanciata. Ora esaminiamo il codicegià inserito.

1 super . onCreate ( savedIns tanceState ) ;

questa riga chiama il metodo onCreate della classe Activity. Una riga benpiú interessante é

1 setContentView (R. layout . main ) ;

4Il termine Log indica la registrazione cronologica delle operazioni man mano che ven-gono eseguite [Log - Wikipedia]. Il termine Cat indica un comando dei sistemi operativiUnix e Unix-like che legge i �le che gli sono speci�cati come parametri (o lo standard input)e produce sullo standard output la concatenazione del loro contenuto [Cat - Wikipedia].I due termini uniti formano Logcat, che indica quindi il tracciamento cronologico delle

operazioni con la relativa visualizzazione sullo standard output del sistema. Lo si visualizzain una scheda di Eclipse accanto alla consolle. Vengono visualizzati anche gli errori nelcaso in cui ce ne siano e la relativa posizione nel codice.

3.3. SCHELETRO DI UN APPLICAZIONE JAVA 21

Questo comando mostra a video la schermata main estratta dalla classeR.layout.main che viene generata automaticamente nella fase di build. Que-sta schermata grazie ai tool di Eclipse viene visualizzata in maniera gra�cae si trova nella cartella res/layout/main.xml.

I Layout Nella cartella Layout vengono de�niti tutti i layout delle scher-mate che si vogliono inserire nel programma. Vediamo nello speci�co lastruttura del �le main.xml generato automaticamente in fase di creazionedel progetto:

1 <?xml ve r s i on=" 1 .0 " encoding="utf−8"?><LinearLayout

xmlns : android="http :// schemas . android . com/apk/ r e s / android "3 android : layout_width=" f i l l_pa r e n t "

android : layout_height=" f i l l_pa r e n t "5 android : o r i e n t a t i o n=" v e r t i c a l " >

7 <TextViewandroid : layout_width=" f i l l_pa r e n t "

9 android : layout_height="wrap_content"android : t ex t="@str ing / h e l l o " />

11

</LinearLayout>

Ogni comando di de�nizione é preceduto dalla stringa }android:~. Lasciandoperdere il resto che verrà approfondito piú avanti notiamo cosa mostra a videola scritta Hello World <nome applicazione> che é la TextView. I primi duecomandi de�niscono la dimensione del campo di testo, la terza de�nisce cosac'è scritto nel campo. Notiamo che c'è scritto @string/hello che fa riferimentoalla stringa hello la quale é de�nita nel �le string che si trova in res/strings

Le Stringhe Tutti i �le che si trovano nella cartella res/strings contengonola de�nizione di tutte le stringhe che vengono usate nel programma. Ovvia-mente non siamo costretti a de�nire ogni stringa all'interno di questi �le,possiamo inserirle direttamente nei �le che ci interessano. Nel �le precedenteavremmo potuto scrivere:

android : t ex t="CIAO MONDO"

e il compilatore ce lo avrebbe accettato senza problemi. É buona norma peròinserire qui ogni stringa per due motivi:

1. É più facile modi�carle per eventuali correzioni

22 CAPITOLO 3. SVILUPPO DELL'APPLICAZIONE BOLLETTINO

2. Si possono fare dei supporti multilingua in modo da non dover crearediverse versioni del programma solo per la lingua

3.3.2 La schermata principale

Iniziamo subito con la creazione della schermata principale, che consiste nel-l'inserire i bottoni con funzioni che poi dovremo implementare. Con l'editorgra�co risulta facile crearla. Ecco Il listato

1 <?xml ve r s i on=" 1 .0 " encoding="utf−8"?><Scro l lV iew

xmlns :andro id=" ht tp : // schemas . android . com/apk/ r e s / android "3 android : layout_width=" f i l l_pa r e n t "

andro id : l ayout_he ight=" f i l l_pa r e n t " >5 <LinearLayout

android : layout_width=" f i l l_pa r e n t "7 andro id : l ayout_he ight="wrap_content"

and ro id : ba s e l i n eA l i gned=" true "9 and r o i d : o r i e n t a t i o n=" v e r t i c a l " >

11 <TextViewandro i d : i d="@+id / textView1"

13 android : layout_width=" f i l l_pa r e n t "andro id : l ayout_he ight="wrap_content"

15 andro id : l ayout_grav i ty=" r i gh t "and ro i d : t ex t="@str ing /useCamera"

17 andro id : textAppearance="? and r o i d : a t t r / textAppearanceLarge "/>

19 <Buttonand ro i d : i d="@+id /videocamera "

21 android : layout_width="match_parent"andro id : l ayout_he ight="wrap_content"

23 and ro i d : t ex t="@str ing /useCamera" />

25 <Buttonand ro i d : i d="@+id / ocr "

27 android : layout_width="match_parent"andro id : l ayout_he ight="wrap_content"

29 and ro i d : t ex t="@str ing / ocr " />

31 <Buttonand ro i d : i d="@+id / b o l l e t t i n o "

33 android : layout_width="match_parent"andro id : l ayout_he ight="wrap_content"

35 and ro i d : t ex t="@str ing / b o l l e t t i n o " />

37 <Button

3.3. SCHELETRO DI UN APPLICAZIONE JAVA 23

and ro i d : i d="@+id / v i s u a l i z z a "39 android : layout_width="match_parent"

andro id : l ayout_he ight="wrap_content"41 and ro i d : t ex t="@str ing /show" />

43 <Buttonand ro i d : i d="@+id / scanButton"

45 android : layout_width="match_parent"andro id : l ayout_he ight="wrap_content"

47 andro id :onCl i ck="onScan"and ro i d : t ex t="@str ing / barre " />

49

<TextView51 and ro i d : i d="@+id / scanResu l t "

android : layout_width="wrap_content"53 andro id : l ayout_he ight="wrap_content"

and ro i d : t ex t="@str ing /noBarCode"55 andro id : textAppearance="? and r o i d : a t t r /textAppearanceMedium"

/>

57 <Buttonand ro i d : i d="@+id /download"

59 android : layout_width="match_parent"andro id : l ayout_he ight="wrap_content"

61 andro id :onCl i ck="onDownload"and ro i d : t ex t="@str ing /download" />

63

<Button65 and ro i d : i d="@+id / testCamera"

android : layout_width="match_parent"67 andro id : l ayout_he ight="wrap_content"

andro id :onCl i ck="onCamera"69 and ro i d : t ex t="@str ing / testCamera" />

71 <Buttonand ro i d : i d="@+id / r i c o n o s c i t a r g a "

73 android : layout_width="match_parent"andro id : l ayout_he ight="wrap_content"

75 andro id :onCl i ck="onTarga"and ro i d : t ex t="@str ing / r i c o n o s c i t a r g a " />

77

<Button79 and ro i d : i d="@+id /button1"

android : layout_width="match_parent"81 andro id : l ayout_he ight="wrap_content"

andro id :onCl i ck="onTestHtml"83 and ro i d : t ex t="@str ing / r i ch i e s taHtml " />

85 <Button

24 CAPITOLO 3. SVILUPPO DELL'APPLICAZIONE BOLLETTINO

and ro i d : i d="@+id / t e s t "87 android : layout_width="match_parent"

andro id : l ayout_he ight="wrap_content"89 andro id :onCl i ck=" onTestAct iv i ty "

and ro i d : t ex t="@str ing / t e s t " />91

</LinearLayout>93 </Scro l lV iew>

../software/res/layout/main.xml

Come possiamo notare ci sono due tipi di elementi, le TextView e i Button.Le TextView sono delle comuni aree testuali mentre i Button sono appunto ibottoni. Come potete notare ogni elemento é identi�cato dall' ID che vienede�nito così android:id=@+id/textView1 5. Gli ID ci risulteranno utili piúavanti per assegnare azioni ai vari bottoni.

Dimensionamento I Campi android:layout_width e android:layout_heightde�niscono le dimensioni dei pulsanti. Il valore match_parent attribuito allalunghezza indica al bottone di allargarsi �nché non raggiunge la lunghezza delcontenitore padre (in questo caso lo schermo stesso), il valore wrap_contentinvece indica al componente che può allungarsi quanto vuole (in altezza se éassociato a layout_height) per far spazio al testo che ha al suo interno.

Assegnare Stringhe ai componenti Grazie all'editor gra�co é moltosemplice assegnare una stringa, basta cliccare col destro sul componente ecliccare su edit text, a questo punto vediamo tutte le stringhe già inserite-quella che vogliamo noi ovviamente non é presente- , quindi clicchiamo suNew String. Nel campo String: scriviamo il valore della stringa che vogliamoaggiungere (ad esempio Utilizza la Telecamera), nel campo New R.stringscriviamo un id che in qualche modo evochi la scritta, ad esempio useCamera.Tutte le stringhe vanno nella cartella res/values. Eclipse automaticamenteassegna il nome strings.xml anche se, come vedremo anche in altre occasioni,il nome non é discriminante. Ecco il �le con tutte le stringhe.

1 <?xml ve r s i on=" 1 .0 " encoding="utf−8"?><re s ou r c e s>

3

<s t r i n g name=" h e l l o ">Hel lo World , So f twareAct iv i ty !</ s t r i n g>5 <s t r i n g name="app_name">Software</ s t r i n g>

<s t r i n g name="wait ">Per favo r e a t t end i</ s t r i n g>7 <s t r i n g name="useCamera">Usa l a videocamera</ s t r i n g>

5Si possono editare anche via gra�ca i vari id semplicemente facendo click destro sulcomponente e cliccando su Edit ID

3.3. SCHELETRO DI UN APPLICAZIONE JAVA 25

<s t r i n g name="back">Ritorna</ s t r i n g>9 <s t r i n g name="expectedText ">Quì dovrebbe comparire i l t e s t o

r i c o n o s c i u t o</ s t r i n g><s t r i n g name=" ocr ">Riconoscimento Cara t t e r i</ s t r i n g>

11 <s t r i n g name=" b o l l e t t i n o ">Riconosc i Bo l l e t t i n o</ s t r i n g><s t r i n g name="show">Vi sua l i z z a Immagine</ s t r i n g>

13 <s t r i n g name="cc ">Conto Corrente</ s t r i n g><s t r i n g name=" importo">Importo</ s t r i n g>

15 <s t r i n g name=" i n t e s t a t a r i o ">I n t e s t a t a r i o</ s t r i n g><s t r i n g name=" causa l e ">Causale</ s t r i n g>

17 <s t r i n g name="send">Inv ia Sms</ s t r i n g><s t r i n g name="barre ">Scan Codice a Barre</ s t r i n g>

19 <s t r i n g name="download">Sca r i ca Cl iccando Qui</ s t r i n g><s t r i n g name="noBarCode">Non hai i n s t a l l a t o nessuna

app l i c a z i on e per e f f e t t u a r e l o scan ! ! !</ s t r i n g>21 <s t r i n g name="add">Aggiungi</ s t r i n g>

<s t r i n g name=" v e r i f y ">Ve r i f i c a</ s t r i n g>23 <s t r i n g name="showCode">Vi sua l i z z a Codic i</ s t r i n g>

<s t r i n g name=" a r ch i v i o ">Archiv io</ s t r i n g>25 <s t r i n g name=" de l e t e ">Cance l la</ s t r i n g>

<s t r i n g name="testCamera">Testa l a Telecamera</ s t r i n g>27 <s t r i n g name=" c l i c k ">Scatta</ s t r i n g>

<s t r i n g name=" r i c o n o s c i t a r g a ">Riconosc i La Targa</ s t r i n g>29 <s t r i n g name=" r i ch i e s taHtml ">Test Html</ s t r i n g>

<s t r i n g name=" t e s t ">Test</ s t r i n g>31

</ r e s ou r c e s>

../software/res/values/strings.xml

3.3.3 Assegnare le azioni ai bottoni

I vari bottoni in main così come sono non fanno nulla perché non é statoassegnato loro alcuna azione. Ora vediamo come assegnarne loro una. Nelmetodo onCreate andiamo a inserire questo codice per richiamare l'oggettobottone dal codice Java

OnCl ickListener c l i c k= new OnCl ickListener ( ) {2 pub l i c void onCl ick (View arg0 ) {

//Codice da e s e gu i r e a l c l i c k4 }

6 } ;Button but=(Button ) findViewById (R. id . videocamera ) ;

8 but . s e tOnCl i ckL i s t ene r ( c l i c k ) ;

26 CAPITOLO 3. SVILUPPO DELL'APPLICAZIONE BOLLETTINO

Spiego brevemente il codice. OnClickListener é un interfaccia java conun unico metodo da implementare: }onClick()~. Questo metodo viene in-vocato appunto quando il tasto viene premuto. Ora che abbiamo l'oggettoclick con un implementazione di OnClickListener dobbiamo dire in qualebottone impostare questa azione. Per farlo prima dobbiamo prelevare dal-la gra�ca l'oggetto. Per fare questo dobbiamo usare il metodo �ndView-ById(R.id.<idComponente>). Questo ci restituisce un oggetto View che ésuperclasse rispetto a Button. Quindi con un cast esplicito lo trasformiamoin un Button. A questo punto con l'oggetto gli impostiamo l'azione che devefare al click.

3.3.4 Activity e Cambio di Schermata

Ora che sappiamo assegnare azioni ai bottoni vogliamo creare altre schermatein modo tale che quando clicchiamo su un bottone si acceda ad altre. Vediamocome creare la piú semplice, la visualizzazione di un'immagine. Il metodomigliore per visualizzare schermate é senza dubbio quello di farle visualizzarea una nuova Activity

Cos'è un Activity? Un Activity rappresenta una possibile interazionedell'utente con l'applicazione e può essere associata al concetto di schermata.Essa potrà contenere componenti di sola visualizzazione, insieme ad altri cheinvece permettono l'interazione con l'utente.6

L'immagine che scegliamo di visualizzare é quella che poi useremo perfarci l'Ocr, riconoscere il bollettino oppure la bolletta. Come prima cosadobbiamo creare il Layout per questa nuova schermata, ecco il codice xmlrelativo:

<?xml ve r s i on=" 1 .0 " encoding="utf−8"?>2 <Scro l lV iew

xmlns :andro id=" ht tp : // schemas . android . com/apk/ r e s / android "android : layout_width=" f i l l_pa r e n t "

4 andro id : l ayout_he ight=" f i l l_pa r e n t " ><LinearLayout

6 android : layout_width=" f i l l_pa r e n t "andro id : l ayout_he ight="wrap_content"

8 and ro id : ba s e l i n eA l i gned=" true "and r o i d : o r i e n t a t i o n=" v e r t i c a l " >

10 <LinearLayout android : layout_width=" f i l l_pa r e n t "andro id : l ayout_he ight="300dp"

12 android:background="@color /white "

6Tratto dal libro: Android Guida per lo sviluppatore di Massimo Carli disponibile nellabibliogra�a [Android, Guida per lo sviluppatore]

3.3. SCHELETRO DI UN APPLICAZIONE JAVA 27

and ro i d : g r av i t y=" cente r ">14 <ImageView

andro i d : i d="@+id / image"16 android : layout_width="wrap_content"

andro id : l ayout_he ight="wrap_content"18 andro id : c on t en tDe s c r i p t i on="@str ing / expectedText "

and r o i d : s r c="@drawable/ load ing " />20 </LinearLayout>

<Button22 and ro i d : i d="@+id /back"

android : layout_width="wrap_content"24 andro id : l ayout_he ight="wrap_content"

and ro i d : t ex t="@str ing /back" />26

<TextView28 and ro i d : i d="@+id / t e s t oR i cono s c i u t o "

android : layout_width=" f i l l_pa r e n t "30 andro id : l ayout_he ight="wrap_content"

a nd r o i d : s c r o l l b a r s = " v e r t i c a l "32 andro id : layout_weight=" 0 .51 "

and ro i d : t ex t="@str ing / expectedText "34 andro id : textAppearance="? and r o i d : a t t r /textAppearanceMedium"

/>

36

38 </LinearLayout></Scro l lV iew>

../software/res/layout/visualizza.xml

Ci sono tre componenti: l'area dell'immagine (ImageView), l'area di testo(TextView) e il Bottone (Button). L'unico non visto �n ora é l'ImageViewla cui unica particolarità é il campo android:src che corrisponde alla sorgentedell'immagine. Sorgente che inseriamo ma che, in realtà, non ci interessain quanto l'indirizzo verrà cambiato sempre prima della visualizzazione del-la schermata. Ora che abbiamo la schermata procediamo con la creazionedell'Activity relativa alla visualizzazione della nuova schermata. Una nuovaActivity ha la stessa struttura della precedente, quindi ricopiamo il codicedell'Hello World con l'unica di�erenza che al posto di visualizzare il layoutMain, visualizziamo il layout visualizza. Vediamone il codice:

1 package android . t e l e r e t e ;

3 import android . app . Ac t i v i ty ;import android . g raph i c s . Bitmap ;

5 import android . os . Bundle ;import android . t ex t . method . ScrollingMovementMethod ;

28 CAPITOLO 3. SVILUPPO DELL'APPLICAZIONE BOLLETTINO

7 import android . view . View ;import android . view . View . OnCl ickLis tener ;

9 import android . widget . Button ;import android . widget . ImageView ;

11 import android . widget . TextView ;

13 pub l i c c l a s s v i s u a l i z z a extends Act i v i t y {pub l i c void onCreate ( Bundle savedIns tanceState ) {

15 super . onCreate ( savedIns tanceState ) ;Bitmap immagine=So f twareAct iv i ty . img ;

17 i f ( immagine==nu l l ) {f i n i s h ( ) ;

19 }setContentView (R. layout . v i s u a l i z z a ) ;

21 ImageView img=(ImageView ) findViewById (R. id . image ) ;

23 img . setImageBitmap ( immagine ) ;// img . setImageURI ( f i l e U r i ) ;

25 St r ing recognizedText=So f twareAct iv i ty . t e s t oR i cono s c i u t o ;i f ( recognizedText != nu l l ) {

27 TextView t= (TextView ) findViewById (R. id . t e s t oR i cono s c i u t o ) ;t . setMovementMethod (new ScrollingMovementMethod ( ) ) ;

29 t . setText ( recognizedText ) ;}

31 Button but= (Button ) findViewById (R. id . back ) ;but . s e tOnCl i ckL i s t ene r (new OnCl ickListener ( ) {

33 pub l i c void onCl ick (View v) {f i n i s h ( ) ;

35

}37 }) ;

39 }}

../software/src/android/telerete/visualizza.java

Nel codice si nota come é avvenuto il passaggio dell'immagine dall'Activityprincipale all'Activity visualizza. In sostanza c'è una variabile statica nel-l'Activity principale che si chiama immagine di tipo Bitmap7 e a cui bisognanecessariamente assegnare un valore prima di invocare l'Activity, altrimentiquesta, appena creata, termina immediatamente (invocando il metodo �-nish). Lo stesso metodo viene usato per passare il testo da visualizzare sottol'immagine8 Notiamo inoltre che l'azione che fa il tasto Back non é esplicita-

7Tipo di oggetto che contiene la mappa dei pixel di un immagine8Il passaggio dell'immagine e del testo é avvenuto tramite riferimento. Questo tipo di

passaggio é possibile soltanto quando si vuole avviare un altra Activity interna all'applica-

3.3. SCHELETRO DI UN APPLICAZIONE JAVA 29

mente di tornare alla schermata principale, ma semplicemente di terminarel'Activity. Questo perché all'attivazione di una nuova Activity quella vec-chia viene messa in pausa9 �nché la nuova Activity non termina, lasciandoil foreground a quella che l'ha generata. Ora non ci resta che far avviarel'Activity visualizza all'Activity principale. Per farlo eseguiamo il metodoschermataVisualizza

pub l i c void schermataVisua l i z za (Bitmap immagine , S t r ingrecognizedText ) {

2 So f twareAct iv i ty . immagine=immagine ;So f twareAct iv i ty . t e s t oR i cono s c i u t o=recognizedText ;

4 In tent i n t en t= new Intent ( getAppl i cat ionContext ( ) ,v i s u a l i z z a . c l a s s ) ;

s t a r tA c t i v i t y ( i n t en t ) ;6 }

Come già detto in precedenza impostiamo le variabili statiche immagine etestoRiconosciuto, poi facciamo partire l'Activity selezionata. Per avviarla ciserviamo di un Intent.

Cos'è un Intent? Nel dizionario di Android, un intent é }la descrizione diun'operazione che dev'essere eseguita~. (tratto da [Android, Guida per lo sviluppatore])Più semplicemente, gli Intent sono dei messaggi che il sistema manda a un'ap-plicazione quando si aspetta che questa faccia qualcosa. Ci sono due tipi diIntent:

• Gli Intent Espliciti

• Gli Intent non Espliciti

Gli Intent Espliciti, quelli usati per avviare l'Activity visualizza, vengonousati nel caso in cui si intenda far comunicare attività della stessa appli-cazione e già note in fase di sviluppo. Gli Intent non Espliciti, invece,si riferiscono ad Activity non necessariamente presenti nell'applicazione, maall'interno del telefono, come ad esempio la rubrica e la fotocamera (che trapoco vedremo).

Se proviamo ora a far partire l'applicazione notiamo che, appena clicchia-mo sul tasto Visualizza Immagine, si blocca. Dando uno sguardo al LogCatvediamo la comparsa di questo errore.

zione, vedremo successivamente che nel caso volessimo passare dei parametri a un activityesterna é necessario utilizzare dei metodi piú rigorosi.

9In alcuni casi, come ad esempio il bisogno urgente di memoria ram da parte deltelefono, viene anche rimossa e successivamente ricaricata.

30 CAPITOLO 3. SVILUPPO DELL'APPLICAZIONE BOLLETTINO

02−10 1 3 : 4 1 : 3 7 . 8 9 7 : E/AndroidRuntime (308) :android . content . ActivityNotFoundException : Unable to f i nde x p l i c i t a c t i v i t y c l a s s{ android . t e l e r e t e / android . t e l e r e t e . v i s u a l i z z a } ; have youdec l a r ed t h i s a c t i v i t y in your AndroidManifest . xml?

L'errore ci sta dicendo che non é stata dichiarata alcuna attività esplicitacol nome visualizza. Quindi per far partire l'Activity dobbiamo editare il �leAndroidManifest.xml e aggiungere questa riga prima della chiusura del tag</application>

1 <a c t i v i t y android : name=" v i s u a l i z z a "></ac t i v i t y >

Abbiamo così dichiarato l'Activity, quindi ora é autorizzata ad essere esegui-ta.

Sulla macchina virtuale vediamo che ci mostra correttamente la foto. Pertornare alla schermata principale possiamo usare il tasto Ritorna sullo scher-mo o semplicemente il tasto ritorna del cellulare.

Invio dati ad un'Activity Sia che si stia chiamando una nuova Activityin modo esplicito o la si stia chiamando in modo implicito, si possono pas-sare dei parametri. Il metodo che ci consente di farlo è il putExtra(Stringchiave,Object Valore). Questo è un metodo sovraccarico 10 che accettacome ingresso tutti i tipi di dato standard di java (byte, int, boolean, Stringecc..) e anche array e liste concatenate contenenti dati di questo tipo. Accet-ta inoltre oggetti di tipo Parcelable e Serializable (e liste concatenate oArrayList contenenti questi oggetti). Se si vuole inviare oggetti non standardin un array bisogna necessariamente che siano di tipo Parcelable, i dati ditipo Serializable in array non potranno essere inviati.

Vediamo ora come usare un'Activity non esplicita

10si dice sovraccarica(overloaded in inglese) una famiglia di funzioni/subroutine aventilo stesso nome, ma con la possibilità di accettare un diverso set di argomenti (signature),ed eventualmente restituire un diverso valore di ritorno

3.4. PRIME IMPLEMENTAZIONI 31

3.4 Prime Implementazioni

3.4.1 Acquisire un'immagine dalla Fotocamera

Per fare un'azione del genere dobbiamo creare un Intent diverso da quellogenerato prima per tre ragioni.

• Il software per scattare la fotogra�a sappiamo che non é integrato nelnostro, quindi per fare ciò dobbiamo chiedere a un'Activity di un altraapplicazione se può scattare la foto.

• Abbiamo bisogno che questa Activity ci comunichi se l'operazione éandata a buon �ne e dove ha salvato l'immagine.

• Dobbiamo dire all'Activity dove salvare l'immagine scattata.

Ecco il listato del codice che fa partire la fotocamera.

1 pub l i c void onCl ick (View v) {Intent i n t en t = new Intent (MediaStore .ACTION_IMAGE_CAPTURE) ;

3 F i l e d i r = Environment . ge tExte rna lS to rageDi r e c to ry ( ) ;yourF i l e = new F i l e ( d ir , "DCIM" ) ;

5 yourF i l e = new F i l e ( yourFi le , "Camera" ) ;yourF i l e = new F i l e ( yourFi le , "bau . jpg " ) ;

7 f i l e U r i = Uri . f romFi l e ( yourF i l e ) ; // c r e a t e a f i l e to save theimage

Log . i ( " F i l e Des t inat i on Foder" , f i l e U r i . getEncodedPath ( ) ) ;9 i n t en t . putExtra (MediaStore .EXTRA_OUTPUT, f i l e U r i ) ; // s e t the

image f i l e name// s t a r t the image capture Intent

11 s t a r tAc t i v i t yFo rRe su l t ( intent ,CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE) ;

}

Come possiamo notare l'Intent creato non é esplicito in quanto non gli éstato detto esplicitamente che classe eseguire. Le righe dalla 3 alla 7 ser-vono a creare un oggetto File che punta a un oggetto di nome bau.jpg nellacartella /DCIM/Camera/ all'interno della sdcard; la riga 8 serve soltantoa mostrare nel LogCat l'indirizzo completo del �le bau.jpg; la riga 9 servea dire all'Intent che viene prodotto dalla fotocamera che deve essere salva-to come bau.jpg nella cartella indicata prima; la riga 10 serve a far partirel'Intent. Notiamo peró che questa volta il metodo scrive startActivityForRe-sult : questo sta a indicare che si fa partire l'Activity per avere un risultato.Una volta che termina una qualsiasi Activity, lanciata con questo metodo,

32 CAPITOLO 3. SVILUPPO DELL'APPLICAZIONE BOLLETTINO

viene eseguito il metodo protected void onActivityResult(int requestCode, intresultCode, Intent data). Vediamone i parametri:

• requestCode: é un intero contenente il codice identi�cativo di quel-la particolare Activity che noi abbiamo precedentemente assegnatoquando abbiamo invocato il metodo startActivityForResult, in que-sto speci�co caso il codice che restituirá la fotocamera é CAPTU-RE_IMAGE_ACTIVITY_REQUEST_CODE11

• resultCode: Il result code é un intero che ci informa se l'Activity éterminata correttamente, é stata annullata o se si sono veri�cati errori.Può assumere i valori RESULT_OK, RESULT_CANCELED o altrivalori che corrispondono ai vari errori.

• intentData: Questo é un oggetto di tipo Intent che contiene, tra lealtre cose, l'insieme dei dati che l'Activity lanciata ha prodotto.

Vediamo ora il metodo onActivityResult12 della nostra applicazione

protec ted void onAct iv i tyResu l t ( i n t requestCode , i n t resultCode ,In tent data ) {

2 i f ( requestCode == CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE) {//\ ' e l a r i s p o s t a r e l a t i v a a l l a fotocamera ?

i f ( re su l tCode == RESULT_OK) {// l ' a c qu i s i z i o n e \ ' e andataa buon f i n e

4 // Image captued and saved to f i l e U r i s p e c i f i e d in theIntent

St r ing au=" de s t i n a z i on e non d i s p o n i b i l e " ;6 i f ( data != nu l l )

au=data . getData ( ) . getPath ( ) ;8 e l s e {

Toast . makeText ( th i s , "Errore durantel ' a c qu i s i z i o n e " , Toast .LENGTH_LONG) . show ( ) ;

10 re turn ;}

12 t h i s . yourF i l e=new F i l e ( data . getData ( ) . getPath ( ) ) ;Toast . makeText ( th i s , "Image saved "+au ,

Toast .LENGTH_LONG) . show ( ) ;14 BitmapFactory . Options opt ions = new

BitmapFactory . Options ( ) ;opt i ons . inSampleSize = 2 ;

11Questo codice viene de�nito come costante staticaprivate static �nal int CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE = 100;

12Questo metodo è già presente nella superclasse Activity che estendiamo, e verrà dunquesovrascritto

3.4. PRIME IMPLEMENTAZIONI 33

16 Bitmapimmagine=BitmapFactory . decodeF i l e ( data . getData ( ) . getPath ( ) , opt i ons ) ;

s chermataVisua l i z za ( immagine , nu l l ) ;18

} e l s e i f ( re su l tCode == RESULT_CANCELED){// l ' a c qu i s i z i o n e \ ' e s t a t a c an c e l l a t a

20 Toast . makeText ( th i s , "L ' a c qu i s i z i o n e d e l l ' immagine \ ' es t a t a c an c e l l a t a " , Toast .LENGTH_LONG) . show ( ) ;

22 } e l s e {//E ' avvenuto un e r r o r eToast . makeText ( th i s , " Errore durante

l ' a c qu i s i z i o n e " , Toast .LENGTH_LONG) . show ( ) ;24

}26

}28 }

In questo frammento di codice, vediamo innanzitutto che controlla che ilrequestCode sia relativo a quello della chiamata alla videocamera. In secondoluogo controlla di ricevere il RESULT_OK, in�ne se il parametro data édiverso da null. Se tutte queste condizioni sono soddisfatte, esegue le seguentioperazioni:

• Noti�ca all'utente con un breve messaggio dove l'immagine é statasalvata.

• Salva il percorso dell'immagine nella variabile di istanza yourFile.

• Legge l'immagine e la salva in un Oggetto di tipo Bitmap.

• In�ne visualizza l'immagine all'utente richiamando il metodo costriuitoin precedenza.

3.4.2 E�ettuare il riconoscimento dei caratteri

Ora che sappiamo come acquisire un'immagine e sappiamo come visualizzar-la, possiamo benissimo e�ettuare l'OCR e visualizzare Immagine e Ricono-scimento a video. Il codice per e�ettuare l'OCR é questo

TessBaseAPI baseApi = new TessBaseAPI ( ) ;2 i f ( immagine . getConf ig ( ) . compareTo ( Config .ARGB_8888) !=0){

immagine=co r r e t t o r eG r a f i c o . dammiImmagine ( immagine ,newDimension (0 , 0 , immagine . getWidth ( ) , immagine . getHeight ( ) ) ) ;

34 CAPITOLO 3. SVILUPPO DELL'APPLICAZIONE BOLLETTINO

4 }baseApi . i n i t (myDir . t oS t r i ng ( ) , " i t a " ) ; // myDir +

"/ t e s sda ta /eng . t ra ineddata " must be pre sent6 baseApi . setImage ( immagine ) ;

8 St r ing recognizedText = baseApi . getUTF8Text ( ) ; //Log or otherwi se d i sp l ay t h i s s t r i n g . . .

Log . i ( "Testo Riconosc iuto " , recognizedText ) ;10

baseApi . end ( ) ;12 schermataVi sua l i z za ( immagine , recognizedText ) ;

Se assegnamo questo codice all'evento click notiamo che, al momento dellapressione del bottone, il programma sembra bloccarsi. In realtà sta e�et-tuando l'OCR che richiede un po' di tempo a seconda delle dimensioni e delnumero di caratteri nella �gura. Ora é abbastanza naturale pensare che unutente medio pensi appunto che il programma abbia smesso di funzionare ecercherà di terminarlo se non si dice almeno di aspettare. Per farlo intantocreiamo la schermata che invita l'utente ad attendere.

Schermata di Attesa<?xml ve r s i on=" 1 .0 " encoding="utf−8"?>

2 <LinearLayoutxmlns :andro id=" ht tp : // schemas . android . com/apk/ r e s / android "android : layout_width=" f i l l_pa r e n t "

4 andro id : l ayout_he ight=" f i l l_pa r e n t "and ro id : ba s e l i n eA l i gned=" f a l s e "

6 and r o i d : o r i e n t a t i o n=" v e r t i c a l " >

8 <ImageViewandro i d : i d="@+id / immagineDaRiconoscere"

10 android : layout_width="match_parent"andro id : l ayout_he ight="400dp"

12 and r o i d : s r c="@android:drawable /alert_dark_frame" />

14 <Relat iveLayoutand ro i d : i d="@+id / re l a t i v eLayout1 "

16 android : layout_width="match_parent"andro id : l ayout_he ight="wrap_content" >

18

<ProgressBar20 and ro i d : i d="@+id / progressBar1 "

s t y l e="? and r o i d : a t t r / progre s sBarSty l eLarge "22 android : layout_width="wrap_content"

andro id : l ayout_he ight="wrap_content"24 andro id : l ayout_a l i gnParentLe f t=" true "

3.4. PRIME IMPLEMENTAZIONI 35

andro id : l ayou t_cen t e rVe r t i c a l=" true " />26

<TextView28 and ro i d : i d="@+id / textView2"

android : layout_width="wrap_content"30 andro id : l ayout_he ight="wrap_content"

andro id : l ayou t_cen t e rVe r t i c a l=" true "32 android : layout_toRightOf="@+id / progressBar1 "

and ro i d : t ex t="@str ing /wait "34 andro id : textAppearance="? and r o i d : a t t r / textAppearanceLarge "

/>

36 </Relat iveLayout>

38 </LinearLayout>

../software/res/layout/caricamento.xml

Vediamo l'inserimento di un nuovo elemento che si chiama RelativeLayoutin cui sono inclusi gli oggetti progressBarStyleLarge13 e un TextView a cui éassociata una stringa con scritto Per favore attendi. Il RelativeLayout servesolo per mettere di �anco il progressBarStyle e il TextView. Il tool gra�co diEclipse aiuta molto nella sistemazione degli elementi e genera lui automatica-mente il codice. Per completezza spiego brevemente i comandi che per la pri-ma volta vediamo nel codice: android:layout_alignParentLeft= true:Allineamento a sinistra rispetto al contenitore14. android:layout_centerVertical=true: allineamento centrato rispetto al contenitore. android:layout_toRightOf=@+id/progressBar1: posizionato alla destra di progressBar1.

L'Activity Caricamento Il codice dell'activity caricamento é abbastanzasemplice

package android . t e l e r e t e ;2

import android . app . Ac t i v i ty ;4 import android . g raph i c s . Bitmap ;import android . os . Bundle ;

6 import android . widget . ImageView ;

8 pub l i c c l a s s car icamento extends Act i v i ty {pub l i c void onCreate ( Bundle savedIns tanceState ) {

10 super . onCreate ( savedIns tanceState ) ;Bitmap immagine=So f twareAct iv i ty . img ;

13Un oggetto di forma quadrata che mostra l'animazione di una rotella che gira. Servea far capire all'utente che il telefono sta lavorando

14Che in questo caso é appunto il RelativeLayout

36 CAPITOLO 3. SVILUPPO DELL'APPLICAZIONE BOLLETTINO

12 setContentView (R. layout . car icamento ) ;ImageView

img=(ImageView ) findViewById (R. id . immagineDaRiconoscere ) ;14 i f ( immagine==nu l l ) {

f i n i s h ( ) ;16 }

img . setImageBitmap ( immagine ) ;18 }

}

../software/src/android/telerete/caricamento.java

Come possiamo notare é semplicemente la visualizzazione dell'immagine pas-sata come riferimento nella variabile statica img della classe SoftwareActivity(Che é quella principale). La cosa interessante di questa Activity é come vienelanciata. Vediamo il codice che la lancia

1 pub l i c void schermataCaricamento (Bitmap immagine ) {So f twareAct iv i ty . img=immagine ;

3 In tent i= new Intent ( getAppl i cat ionContext ( ) ,car icamento . c l a s s ) ;

i . addFlags ( In tent .FLAG_ACTIVITY_NO_HISTORY) ;5 s t a r tA c t i v i t y ( i ) ;}

La riga interessante é la quarta, in cui si aggiunge il �ag:FLAG_ACTIVITY_NO_HISTORY.

Spieghiamo innanzitutto cos'è un �ag: si tratta di una variabile che pos-siamo impostare al lancio di un'Activity per modi�carne il modo in cui vieneeseguita. Il �ag che abbiamo usato noi serve a non far entrare l'Activitynello stack delle attività.

A livello pratico cosa succede? Siamo nel caso in cui l'Activity Soft-wareActivity sta mostrando un caricamento lanciando l'Activity caricamento.Dopo qualche istante SoftwareActivity ha terminato il riconoscimento e vuolemostrare i risultati quindi lancerà l'Activity visualizza. Se non avessimo usatoil �ag indicato in precedenza alla pressione del tasto ritorna (nella schermatavisualizza) si tornerebbe a visualizzare il caricamento e non é decisamenteció che si vuole ottenere. Con il �ag impostato, l'Activity caricamento vienesostituita dalla nuova(visualizza) lanciata dal padre(SoftwareActivity). Co-sì facendo, alla pressione del tasto ritorna, si fa ritorno direttamente allaschermata principale.

3.5. RICONOSCIMENTO DEL BOLLETTINO POSTALE 37

Figura 3.1: Bollettino

3.5 Riconoscimento del bollettino postale

3.5.1 Ideazione

Abbiamo ora tutti gli strumenti per preoccuparci di riconoscere il bollettino.La prima soluzione che mi é venuta in mente per risolvere il problema é difare un riconoscimento OCR generalizzato a tutta l'immagine e dalla stringaottenuta estrapolare i dati. Questo metodo peró é molto ine�ciente oltreche impreciso. Provando l'OCR su una quantità di caratteri anche limitataa mezza pagina di un libro per bambini, il cellulare può impiegare anche unminuto per il riconoscimento completo.

Per queste ragioni ho ritenuto la mia prima idea da scartare, optandoinvece per trovare le parti interessanti del bollettino ed e�ettuare un ricono-scimento mirato su queste.Ovviamente anche una scansione dell'immagine richiede un certo tempo, masigni�cativamente inferiore a quello di un OCR generalizzato. In manieratale da escludere alcune zone in cui e�ettuare la scansione per velocizzareil processo. L'idea é di usare dei punti facilmente individuabili e da quelliprelevare le informazioni scritte sul bollettino. I punti in questione, per viadel loro colore e della loro posizione, sono sicuramente i simboli neri su cui édisegnato l'Euro.

É facile individuarli perché basta cercare nella foto due quadrati neri conle dimensioni simili a quelle indicate. Dopo aver individuato le posizioni ele dimensioni più o meno precise dei due cpossiamo estrarre dal bollettinole parti interessanti, ovvero il C/C, gli Euro da pagare, l'intestatario e lacausale. Per quanto riguarda le informazioni di chi esegue il bollettino nonci preoccupiamo di prelevarle per due ragioni:

• Perché nella maggior parte dei casi saranno i dati dell'utente proprie-tario del telefono, quindi daremo la possibilità di inserirli a mano al

38 CAPITOLO 3. SVILUPPO DELL'APPLICAZIONE BOLLETTINO

primo utilizzo.

• Se le informazioni sono presenti saranno scritte inevitabilmente a manoe Tesseract funziona male con il riconoscimento di questa scrittura.

Vediamo ora di realizzare a livello di codice le nostre idee.

3.5.2 Individuazione posizione dei simboli con l'euro

Il problema si riduce a trovare due quadrati nero di dimensioni compatibiliall'interno della foto. Come prima cosa cerchiamo di capire quali sono ledimensioni che stiamo cercando. Guardando la �gura 3.1 possiamo intuireche oscillano tra il 2% e il 4% della �gura. Quindi iniziamo a de�nire lecostanti

pr i va t e f i n a l s t a t i c i n t expectedDimEMin=2;2 pr i va t e f i n a l s t a t i c i n t expectedDimEMax=4;

Prima di iniziare la ricerca ci serve un metodo per capire se il pixel preso inesame é da considerarsi un pixel nero(o almeno grigio) oppure di un colorediverso. Creiamo allora il metodo isGrigio(int argb) per discriminare i pixel

pr i va t e boolean i sG r i g i o ( i n t argb ) {2 i n t r = ( argb >> 16) & 0xFF ;

i n t g = ( argb >> 8) & 0xFF ;4 i n t b = ( argb >> 0) & 0xFF ;

long mediaGrigia=(r+g+b) /3 ;6 i n t c o l o r e=r ;

i f ( mediaGrigia>co lo r e−range&&mediaGrigia<co l o r e+range ) {8 c o l o r e=g ;

i f ( mediaGrigia>co lo r e−range&&mediaGrigia<co l o r e+range ) {10 c o l o r e=b ;

i f ( mediaGrigia>co lo r e−range&&mediaGrigia<co l o r e+range ) {12 i n t va lue=ing r e s s o . getWidth ( ) ∗2 ;

mediaColore=(( mediaColore∗ value ) +mediaGrigia ) /( va lue+1) ;

14 i f ( mediaGrigia<mediaColore )mediaBassa=Math . min ( ( ( mediaBassa∗ value )

+mediaGrigia ) / ( va lue+1) , 110) ;16 e l s e

mediaAlta=Math .max ( ( ( mediaAlta∗ value )+ mediaGrigia ) /( va lue+1) , 150) ;

18 re turn mediaGrigia <(mediaBassa+mediaAlta ) /2 ;}

20 }}

3.5. RICONOSCIMENTO DEL BOLLETTINO POSTALE 39

Figura 3.2: Ra�gurazione ARGB

22

re turn f a l s e ;24 }

Il primo pezzo di codice trasforma l'intero ARGB in un colore RGB.

Cos'è l'ARGB L'ARGB é l'acronimo di Alpha Red Green Blue che sonole quattro componenti necessarie a de�nire un colore. Alpha indica la tra-sparenza del colore (0=trasparente, 255 opaco), Red é l'intensità del rosso,Green quella del verde e Blue quella del blu. Ogni componente può assu-mere un valore compreso tra 0 e 255. Il valore in ARGB si ottiene cosìARGB=(((((Alpha*256)+Red)*256)+Green)*256)+BlueOppure apponendo i valori in esadecimale uno dietro l'altroAd esempio se volessimo rappresentare il rosso opaco si utilizzano i seguentivalori A=255 R=255 G=0 B=0Che in esadecimale diventano A=FF R=FF G=00 B=00Quindi ARGB= FFFF0000;

Per convertire un numero da ARGB nelle sue 3 componenti (Perché ilvalore di Alpha non ci interessa) facciamo uno shift dei bit verso destra, adesempio per il rosso argb � 16. Questo corrisponde a fare una divisione per216 ovvero per 256*256. Così facendo otteniamo il valore che ci interessa neiprimi 8 bit. Estraiamo il valore forzando gli altri bit al di fuori di quegli ottoad essere zero con un operazione di AND 0xFF.

Tornando ad esaminare il codice vediamo che viene calcolata la media-Grigia che altro non é che la media dei tre colori, ovvero la rappresentazionein bianco e nero del colore proposto. I 3 if controllano semplicemente che ilcolore sia e�ettivamente una gradazione di grigio e non un rosso ad esem-pio15. All'interno dei tre if c'è un sistema che cerca di capire se un pixel ée�ettivamente da considerarsi nero oppure no cercando di adattarsi a tuttele condizioni di illuminazione in cui può essere scattata la foto.

15La variabile range indica con che tolleranza accettare le gradazioni di grigio; il valoreche dà i migliori risultati é 10 (ottenuto sperimentalmente)

40 CAPITOLO 3. SVILUPPO DELL'APPLICAZIONE BOLLETTINO

Figura 3.3: Estrazione del valore rosso da ARGB

Vediamo che é presente la variabile mediaColore che é la media dei valoridegli ultimi16 pixel. Le altre medie vengono calcolate col medesimo meccani-smo badando che la mediaAlta é la media dei valori superiori alla mediaColo-re, mentre la mediaBassa é la media di quelli inferiori. Da notare inoltre chele medie hanno un massimo e un minimo imposto per evitare che assumanovalori troppo sballati in determinate condizioni, ad esempio che la foto vengafatta su un tavolo nero e che sia eccessivamente scura.

Si decide in�ne se il pixel in esame é grigio eseguendo questo test.

re turn mediaGrigia <(mediaBassa+mediaAlta ) /2 ;

Individuazione e�ettiva delle posizioni Ora che abbiamo il metodo perdistinguere i pixel da considerare neri passiamo al metodo che restituisce lecoordinate delle posizioni dei simboli dei 2 euro neri. Come prima cosa no-tiamo che é inutile fare la scansione dell'intera immagine per trovarli. Nellamaggior parte dei casi (se non in tutti) gli euro si troveranno nella metà su-periore della foto, quindi la scansione si limita a metà documento. In secondoluogo, dato che usiamo lo stesso metodo per individuare i due bollettini, do-vremmo prevedere che ammetta un intero che de�nisca da quale posizioneiniziare la scansione. La scansione, quindi, inizierà dal pixel più in alto dellacolonna indicata dal parametro in ingresso e procederà verticalmente versoil basso. Inizierà così a cercare delle strisce di pixel di lunghezza compatibilea quella aspettata. Non appena le trova, conta i pixel neri del quadrato chesi origina utilizzando come lato sinistro la striscia appena trovata. Se questisono almeno il doppio dei pixel non neri trovati nel medesimo quadrato, allo-ra, con ogni probabilità, sarà il quadrato del bollettino che stiamo cercando.Restituiamo quindi le dimensioni e posizioni del simbolo tramite un oggetto

16Guardando il codice si nota che si dà peso soltanto agli ultimi ingresso.getWidth()*2valori, quindi in un immagine di dimensioni 300*200 pixel saranno gli ultimi 600 valori

3.5. RICONOSCIMENTO DEL BOLLETTINO POSTALE 41

Dimension che contiene quattro interi, la posizione x e y e la dimensione x ey. Ecco il listato del metodo

1 pub l i c Dimension po s i z i on eEuroBo l l e t t i n o ( i n t da ) {i n t dimMin=( i n t ) ( ( i n g r e s s o . getWidth ( ) ∗expectedDimEMin ) /100) ;

3 i n t dimMax=( in t ) ( ( i n g r e s s o . getWidth ( ) ∗expectedDimEMax ) /100) ;Log . d( "Min e MaxNeri" ,dimMin+" "+dimMax) ;

5 i n t maxDist=dimMin /5 ;Log . i ( "Max Dist " , ""+maxDist ) ;

7 f o r ( i n t x=da ; x<ing r e s s o . getWidth ( ) −1;x++){i n t pos iz ioneY=−1;

9 i n t distanzaDaUltimo=0;i n t numeriNeri=0;

11 f o r ( i n t y=1;y<ing r e s s o . getHeight ( ) /2 ; y++){// ce rca s o l o n e l l ametà in a l t o

i n t argb=ing r e s s o . g e tP ix e l (x , y ) ;13 i f ( i sG r i g i o ( argb ) ) {

i f ( numeriNeri==0)15 pos iz ioneY=y ;

numeriNeri++;17 distanzaDaUltimo=0;

}19 e l s e {

distanzaDaUltimo++;21 i f ( distanzaDaUltimo>maxDist ) {//Non è i n t e r r o t t o da un so l o

p i x e l biancoi f ( numeriNeri<dimMax&&numeriNeri>dimMin) {//La

dimensione è compat ib i l e23 Log . i ( "NumeriNeriY a c c e t t a b i l i t r o v a t i " , "Al la

po s i z i on e "+x+" "+y) ;i n t numeriBianchi=0;

25 i n t quest iNumeriNer i =0;f o r ( i n t

questoY=pos iz ioneY ; questoY<y ; questoY++){// conta ipunt i n e r i a de s t ra d e l l a l i n e a t rovata

27 f o r ( i n t i=x ; i<x+(( numeriNeri ∗2) /3) ; i++){i n t c=ing r e s s o . g e tP ix e l ( i , questoY ) ;

29 i f ( ! i sG r i g i o ( c ) ) {numeriBianchi++;

31 }e l s e {

33 quest iNumeriNer i++;}

35 }}

37 i f ( quest iNumeriNeri>numeriBianchi ∗2) {return new

Dimension (x , pos iz ioneY , numeriNeri , numeriNeri ) ;39 }

42 CAPITOLO 3. SVILUPPO DELL'APPLICAZIONE BOLLETTINO

}41 numeriNeri=0;

}43 }

}45 }

return nu l l ;47 }

3.5.3 Estrapolazione delle Informazioni

Servendosi del metodo precedente, si individuano le posizioni degli euro. Tra-mite proporzioni si generano i Dimension relativi alle varie posizioni, si ri-tagliano le �gure e si fa l'OCR su quelle estratte. In�ne si restituisce ilbollettino.

1 pr i va t e b o l l e t t i n o b o l l e t t i n o (Bitmap thumb) {c o r r e t t o r eG r a f i c o cor= new co r r e t t o r eG r a f i c o ( thumb) ;

3 Dimension d= cor . po s i z i on eEuroBo l l e t t i n o (0 ) ;Bitmap immagine=co r r e t t o r eG r a f i c o . i n c o r n i c i a ( thumb , d) ;

5 schermataCaricamento ( immagine ) ;Dimension d2 ;

7 cor= new co r r e t t o r eG r a f i c o ( thumb) ;i n t dax=thumb . getWidth ( ) /3 ;

9 do{d2= cor . po s i z i on eEuroBo l l e t t i n o ( dax ) ;

11 dax=d2 . posx+5;immagine=co r r e t t o r eG r a f i c o . i n c o r n i c i a ( thumb , d2 ) ;

13 schermataCaricamento ( immagine ) ;}

15 whi le ( d2 . posy>d . posy+(thumb . getHeight ( ) ∗0 .02 ) | | d2 . posy<d . posy−(thumb . getHeight ( ) ∗0 .02 ) ) ;c o r r e t t o r eG r a f i c o aa= new

co r r e t t o r eG r a f i c o ( c o r r e t t o r eG r a f i c o . dammiImmagine (thumb ,d2 ) ) ;

17 immagine=aa . ev id enz i a ( ) ;schermataCaricamento ( immagine ) ;

19 t ry {Thread . s l e e p (2000) ;

21 } catch ( Inter ruptedExcept ion e ) {

23 }d . dimy=d . dimx=(d2 . posx−d . posx ) /19 ;

25 Log . i ( "Dimensione x" , ""+d . dimx ) ;i n t posx=d . posx+d . dimx ;

27 i n t dimx=d2 . posx−(2∗d2 . dimx )−posx ;

3.5. RICONOSCIMENTO DEL BOLLETTINO POSTALE 43

Dimension s t r i s c i aCC=newDimension ( posx , ( d . posy+d2 . posy ) /2 ,dimx−((dimx+d . dimx ) /2) , d2 . dimy ) ;

29 posx=posx+(dimx−((dimx+d . dimx ) /2) ) ;Dimension s t r i s c i a Impo r t o=new

Dimension ( posx+3∗d . dimx , ( d . posy+d2 . posy ) /2 , ( ( dimx−d . dimx∗3) /2) , d2 . dimy ) ;31 Bitmap contoCorrente=co r r e t t o r eG r a f i c o . dammiImmagine (thumb ,

s t r i s c i aCC ) ;Bitmap importo=co r r e t t o r eG r a f i c o . dammiImmagine (thumb ,

s t r i s c i a Impo r t o ) ;33 posx=d . posx+(d . dimx/4) ;

i n t posy=d . posy+( i n t ) (d . dimy ∗2 . 3 ) ;35 i n t dimy=( i n t ) ( ( d . dimy∗2) ∗0 . 8 ) ;

Bitmap inte s ta toA=co r r e t t o r eG r a f i c o . dammiImmagine (thumb , newDimension (d . posx , posy , d2 . posx−(2∗d2 . dimx )−posx , dimy ) ) ;

37 posx=d . posx+(d . dimx/4) ;posy=d . posy+( i n t ) (d . dimy ∗4 . 3 ) ;

39 dimy=( in t ) ( ( d . dimy∗2) ∗0 . 9 ) ;Bitmap causa l e=co r r e t t o r eG r a f i c o . dammiImmagine (thumb , new

Dimension (d . posx , posy , d2 . posx−(2∗d2 . dimx )−posx , dimy ) ) ;41 St r ing [ ] u s c i t a=riconosciOCR (new

Bitmap [ ] { contoCorrente , importo , intestatoA , causa l e }) ;S t r ing cau=us c i t a [ 3 ] ;

43 i n t da=−1;u s c i t a [1 ]= u s c i t a [ 1 ] . toLowerCase ( ) . r ep l a c e ( " " , "" ) ;

45 f o r ( i n t i =0; i<u s c i t a [ 1 ] . l ength ( ) ; i++){i f ( u s c i t a [ 1 ] . charAt ( i )>=' 0 '&&us c i t a [ 1 ] . charAt ( i )<=' 9 ' ) {

47 da=i ;break ;

49 }}

51 f l o a t impo ;i f ( da==−1){

53 impo=0;}

55 e l s e {u s c i t a [1 ]= u s c i t a [ 1 ] . s ub s t r i ng (da , u s c i t a [ 1 ] . l ength ( ) ) ;

57 u s c i t a [1 ]= u s c i t a [ 1 ] . r ep l a c e ( "o" , "0" ) ;

59 i f ( u s c i t a [ 1 ] . charAt ( u s c i t a [ 1 ] . l ength ( )−3)> ' 9 ' | | u s c i t a [ 1 ] . charAt ( u s c i t a [ 1 ] . l ength ( )−3)< ' 0 ' )u s c i t a [1 ]= u s c i t a [ 1 ] . r ep l a c e ( u s c i t a [ 1 ] . charAt ( u s c i t a [ 1 ] . l ength ( )−3) ,

' . ' ) ;61 Log . i ( "Valore Float " , u s c i t a [ 1 ] ) ;

impo=Float . par seF loat ( u s c i t a [ 1 ] ) ;63 }

da=−1;65 u s c i t a [0 ]= u s c i t a [ 0 ] . toLowerCase ( ) . r ep l a c e ( " " , "" ) ;

f o r ( i n t i =0; i<u s c i t a [ 0 ] . l ength ( ) ; i++){67 i f ( u s c i t a [ 0 ] . charAt ( i )>=' 0 '&&us c i t a [ 0 ] . charAt ( i )<=' 9 ' ) {

da=i ;

44 CAPITOLO 3. SVILUPPO DELL'APPLICAZIONE BOLLETTINO

69 break ;}

71 }i n t CC=0;

73 i f ( da==−1){CC=0;

75 }e l s e {

77 Log . v ( "Usc i ta [ 0 ] " , u s c i t a [ 0 ] ) ;whi l e ( u s c i t a [ 0 ] . charAt (da−1)=='b ' | | u s c i t a [ 0 ] . charAt (da−1)==' o ' )

79 da−−;u s c i t a [0 ]= u s c i t a [ 0 ] . s ub s t r i ng (da , u s c i t a [ 0 ] . l ength ( ) ) ;

81 u s c i t a [0 ]= u s c i t a [ 0 ] . r ep l a c e ( "o" , "0" ) ;u s c i t a [0 ]= u s c i t a [ 0 ] . r ep l a c e ( "b" , "8" ) ;

83 St r i ngBu i l d e r r e t= new St r ingBu i l d e r ( ) ;f o r ( i n t i =0; i<u s c i t a [ 0 ] . l ength ( ) ; i++){

85 i f ( u s c i t a [ 0 ] . charAt ( i )>=' 0 '&&us c i t a [ 0 ] . charAt ( i )<=' 9 ' )r e t . append ( u s c i t a [ 0 ] . charAt ( i ) ) ;

87 }u s c i t a [0 ]= r e t . t oS t r i ng ( ) ;

89 CC=Int eg e r . pa r s e In t ( u s c i t a [ 0 ] ) ;}

91 St r ing i n t e s t a t a r i o=u s c i t a [ 2 ] ;r e turn new b o l l e t t i n o ( cau , impo , CC, i n t e s t a t a r i o ) ;

93 }

Vediamo che nel codice postato ci sono dei metodi che non abbiamo vi-sto in passato. Spiego il loro output senza mostrare il metodo poiché pocointeressante:

• correttoreGra�co.incornicia(Bitmap immagine,Dimension d):Questo metodo genera un immagine(Bitmap) a bassa risoluzione il cuiquadrato de�nito da d viene colorato di rosso. Ciò facilita di molto leoperazioni di debug in quanto rende palese dove ha trovato il bollettino.

• dammiImmagine(Immagine thumb,Dimension d): Questo me-todo restituisce un'immagine Bitmap ritagliata da thumb nelle dimen-sioni indicate da d.

• evidenzia(): Questo metodo restituisce un immagine bitmap ugualealla precedente con i pixel neri colorati di rosso, che mi è utile per capirese il metodo isGrigio() funziona correttamente.

• riconosciOCR(Bitmap[] img): E�ettua il riconoscimento OCR dipiú immagini, così facendo si risparmia tempo di caricamento di Tes-

3.5. RICONOSCIMENTO DEL BOLLETTINO POSTALE 45

seract per ogni immagine. Durante il processo mostra a video le scher-mate caricamento.

Dopo il metodo riconosciOCR vediamo che ci sono dei metodi che cercanodi correggere eventuali errori di riconoscimento dell' OCR (ad esempio loscambio dello zero per la O) e �ltraggio delle informazioni utili (rimozionead esempio del testo sul C/C del bollettino). In�ne le informazioni vengonosalvate nell'oggetto Bollettino e restituite.

3.5.4 L'activity

Anche questo metodo viene racchiuso all'interno di un'Activity. Questadi�erisce dalle precedenti create per il fatto che deve restituire un valore.Vediamone il codice.

1 pub l i c void onCreate ( Bundle savedIns tanceSta te ) {super . onCreate ( savedIns tanceState ) ;

3

questa=So f twareAct iv i ty . img ;5

schermataCaricamento ( questa ) ;7 Thread t= new Thread (new Runnable ( ) {

pub l i c void run ( ) {9 b o l l e t t i n o b= b o l l e t t i n o ( questa ) ;

In tent i n t en t=ge t In t en t ( ) ;11 i n t en t . putExtra ( " b o l l e t t i n o " , b) ;

s e tRe su l t (RESULT_OK, in t en t ) ;13 f i n i s h ( ) ;

}15

}) ;17 t . s t a r t ( ) ;

}

Intanto notiamo che l'esecuzione dell'Activity viene a�data ad un thread.Questo per un motivo ben preciso: il metodo onCreate é quello responsabiledella creazione della nuova Activity, e �nché questo non termina, l'Activitynon viene creata. Conseguentemente a ció noi a video non la vediamo appa-rire �no al termine del metodo onCreate(). Quindi, se non fosse in un thread,alla pressione del tasto Riconosci bollettino, la gra�ca rimarrebbe bloccata�no all'uscita dei risultati.

Altra novità in questa Activity é il metodo putExtra applicato a un intentgenerico. Questo metodo serve ad aggiungere dei dati da allegare all'Intent dirisposta che invieremo all'Activity padre. Con il metodo putExtra possiamo

46 CAPITOLO 3. SVILUPPO DELL'APPLICAZIONE BOLLETTINO

inviare qualsiasi tipo di variabile standard (tipo int, char, double ecc..) inpiú possiamo inviare qualsiasi Oggetto che implementi la classe Serializable.

Il metodo setResult(RESULT_OK,intent) impone che al termine dell'Ac-tivity venga noti�cato che l'esecuzione é andata a buon �ne producendo irisultati attesi, e imposta l'intent da restituire.

Per ricevere le informazioni nella SoftwareActivity ovvero quella principaledobbiamo aggiungere questo pezzo di codice al metodo onActivityResult

i f ( requestCode== BOLLETTINO_REQUEST_CODE){2 i f ( re su l tCode == RESULT_OK) {

b o l l e t t i n ob=( b o l l e t t i n o ) data . g e t S e r i a l i z a b l eEx t r a ( " b o l l e t t i n o " ) ;

4 schermataVi sua l i z za ( img , b . t oS t r i ng ( ) ) ;} e l s e i f ( resu l tCode == RESULT_CANCELED) {

6 Toast . makeText ( th i s , " I l r i conosc imento de l b o l l e t t i n o non \ ' eandato a buon f i n e " , Toast .LENGTH_LONG) . show ( ) ;

}8 e l s e {

Toast . makeText ( th i s , "Errore , ma non dovrebbe mai a r r i v a r equi " , Toast .LENGTH_LONG) . show ( ) ;

10 }

3.6 Invio delle informazioni

Adesso che abbiamo le informazioni incapsulate nell'Oggetto bollettino pos-siamo inviare le informazioni acquisite tramite SMS alla banca per e�ettuareil pagamento. Come prima cosa diamo la possibilità all'utente di correggeregli eventuali errori di riconoscimento di tesseract.

3.6.1 Layout per editare le informazioni

Creiamo quindi un editor visuale che ci consenta di editare le informazio-ni. Per farlo dobbiamo creare una nuova View e una nuova Activity. Lanuova Activity verrà chiamata visBollettino. Quindi creiamo la classe cheestende Activity e la dichiariamo nell'AndroidManifest come spiegato prima.Creiamo un nuovo layout che consenta la modi�ca delle informazioni comemostrato in �gura 3.4 Vediamo ora il codice che lo genera.

<?xml ve r s i on=" 1 .0 " encoding="utf−8"?>2

4 <Scro l lV iew

3.6. INVIO DELLE INFORMAZIONI 47

h

Figura 3.4: Bollettino

xmlns :andro id=" ht tp : // schemas . android . com/apk/ r e s / android "6 android : layout_width=" f i l l_pa r e n t "

andro id : l ayout_he ight=" f i l l_pa r e n t ">8

<LinearLayout10 and ro i d : i d="@+id / l inearLayout2 "

android : layout_width="match_parent"12 andro id : l ayout_he ight="wrap_content"

and r o i d : o r i e n t a t i o n=" v e r t i c a l " >14

<TextView16 and ro i d : i d="@+id /Tcc"

android : layout_width="wrap_content"18 andro id : l ayout_he ight="wrap_content"

and ro i d : t e x t="@str ing / cc "20 andro id : textAppearance="? and r o i d : a t t r /textAppearanceMedium"

/>

22 <EditTextand ro i d : i d="@+id /contCont"

24 android : layout_width="match_parent"andro id : l ayout_he ight="wrap_content"

26 android: inputType="textPersonName" >

48 CAPITOLO 3. SVILUPPO DELL'APPLICAZIONE BOLLETTINO

28 <requestFocus /></EditText>

30

<TextView32 and ro i d : i d="@+id /Timporto"

android : layout_width="wrap_content"34 andro id : l ayout_he ight="wrap_content"

and ro i d : t ex t="@str ing / importo"36 andro id : textAppearance="? and r o i d : a t t r /textAppearanceMedium"

/>

38 <EditTextand ro i d : i d="@+id / importo"

40 android : layout_width="match_parent"andro id : l ayout_he ight="wrap_content"

42 android : inputType="textPersonName" />

44 <TextViewandro i d : i d="@+id / T in t e s t a t a r i o "

46 android : layout_width="wrap_content"andro id : l ayout_he ight="wrap_content"

48 and ro i d : t ex t="@str ing / i n t e s t a t a r i o "andro id : textAppearance="? and r o i d : a t t r /textAppearanceMedium"

/>50

<EditText52 and ro i d : i d="@+id / i n t e s t a t a r i o "

android : layout_width="match_parent"54 andro id : l ayout_he ight="wrap_content"

android : inputType="textPersonName" />56

<TextView58 and ro i d : i d="@+id /Tcausale "

android : layout_width="wrap_content"60 andro id : l ayout_he ight="wrap_content"

and ro i d : t ex t="@str ing / causa l e "62 andro id : textAppearance="? and r o i d : a t t r /textAppearanceMedium"

/>

64 <EditTextand ro i d : i d="@+id / causa l e "

66 android : layout_width="match_parent"andro id : l ayout_he ight="wrap_content"

68 android : inputType="textPersonName" /><Relat iveLayout

70 and ro i d : i d="@+id / l inearLayout1 "android : layout_width="match_parent"

72 andro id : l ayout_he ight="wrap_content">

3.6. INVIO DELLE INFORMAZIONI 49

74 <Buttonand ro i d : i d="@+id /back"

76 android : layout_width="wrap_content"andro id : l ayout_he ight="wrap_content"

78 and ro i d : t ex t="@str ing /back" />

80 <Buttonand ro i d : i d="@+id / i nv i a "

82 android : layout_width="wrap_content"andro id : l ayout_he ight="wrap_content"

84 andro id : layout_al ignParentRight=" true "android : layout_al ignParentTop=" true "

86 and ro i d : t ex t="@str ing / send" /></Relat iveLayout>

88 </LinearLayout>

90 </Scro l lV iew>

../software/res/layout/bollettino.xml

Nel listato del codice emergono due nuovi elementi: EditText e Scrool-View. EditText é semplicemente un campo che consente all'utente l'in-serimento di dati. ScroolView invece é un tipo di View che consente loscorrimento di tutti gli elementi che contiene. Quindi se lo schermo é troppopiccolo per visualizzare contemporaneamente tutti gli elementi, tramite scor-rimento é possibile visualizzarli tutti comunque. Se non e�ettuiamo ulteriorimodi�che notiamo che il simulatore e tutti i cellulari con tastiera �sica nonhanno problemi a far funzionare correttamente l'app. Se invece utilizziamoun cellulare con tastiera virtuale17 notiamo che questa può posizionarsi soprai campi e rende di�cile l'inserimento del testo. Per risolvere questo problemainseriamo nel manifest questa riga di codice

1 android : windowSoftInputMode=" ad ju s tRes i z e ">

tra i tag activity dell'activity principale. Grazie a questo inserimento quandocompare la tastiera il programma sposta automaticamente il campo di testosopra la tastiera in modo da vedere ció che si sta scrivendo. Ecco il metodoonCreate dell'Activity

1 pub l i c void onCreate ( Bundle savedIns tanceSta te ) {super . onCreate ( savedIns tanceState ) ;

3 ques taAct iv i ty=th i s ;In tent i= ge t In t en t ( ) ;

17Ovvero una tastiera non �sica, che compare sul touch screen al momentodell'immissione di un testo

50 CAPITOLO 3. SVILUPPO DELL'APPLICAZIONE BOLLETTINO

5 b o l l e t t i n ob=( b o l l e t t i n o ) i . g e t S e r i a l i z a b l eEx t r a ( " b o l l e t t i n o " ) ;

f l o a t a=( f l o a t ) −1;7 i f (b==nu l l ) {

b=new b o l l e t t i n o ( "Errore " , a , 0 , "Errore " ) ;9 }

setContentView (R. layout . b o l l e t t i n o ) ;11

impos taBo l l e t t i no (b) ;13 a z i on iPu l s an t i ( ) ;

15 }

Possiamo notare il passaggio dell'informazione tramite il metodo getSeriali-zableExtra e il caricamento del Layout. Se non viene passato alcun bollettinoviene considerato il bollettino Errore. Questo metodo é stato utilizzato pertestare la visualizzazione senza dover e�ettuare il riconoscimento ogni volta,ovviamente si potrebbe sostituire la riga con il seguente codice.

1 i f (b==nu l l ) {throw new I l l ega lArgumentExcept ion ( " Bo l l e t t i n o mancante" ) ;

3 }

Il metodo impostaBollettino() semplicemente compila i campi vuotidegli EditText. Non viene riportato perché lo giudico poco interessante.Il metodo azioniPulsanti() attribuisce al tasto back l'azione di terminarel'Activity. Anche questo non viene riportato per le medesime motivazioni.

A questo punto non rimane che inviare i dati alla banca rispettando unformato. Siccome il protocollo di invio non é stato ancora de�nito, deve po-ter essere modi�cato agevolmente anche dopo la creazione dell'applicazione,faccio in modo che lo si possa editare in un �le xml. I �le xml esterni alprogramma vanno inseriti nella cartella xml (sottocartella di res)18.

1 <?xml ve r s i on=" 1 .0 " encoding="utf−8"?><v a r i a b i l i>

3 <!−− $CC$ campo conto co r r en t e −−><!−− $ importo$ campo importo senza s imbo l i d i euro agg iunt i ,

s o l o l a c i f r a −−>5 <!−− $ causa l e $ campo causa l e −−>

18Per creare un nuovo �le xml con Eclipse nella sezione Package Explorer clicchiamo coldestro sul nostro progetto → new → Other... in�ne andiamo su Android Xml File, nelmenù a tendina scegliamo values, scegliamo il nome e clicchiamo su OK. Dopo aver creatoil �le dobbiamo spostarlo nella cartella xml, se non esiste la creiamo

3.6. INVIO DELLE INFORMAZIONI 51

<!−− $ i n t e s t a t a r i o $ campo i n t e s t a t a r i o −−>7 <sms type=" b o l l e t t i n o ">Conto Corrente $CC$ , importo :

$ importo$ c a u s a l e : $ cau sa l e $ i n t e s t a t a r i o :$ i n t e s t a t a r i o $ e s e g u i t o : $nome$ $ i n d i r i z z o $ $CAP$$ l o c a l i t a $</sms>

<numero type=" b o l l e t t i n o ">3481064869</numero>9 </ v a r i a b i l i>

../software/res/xml/variabili.xml

Ho scelto di de�nire tra dollari le parole chiave che poi nel programmaverranno sostituite con quelle reali estrapolate dal bollettino.

3.6.2 lettura del codice XML

Per leggere un documento XML ho dovuto creare una classe che mi consen-tisse agevolmente di prelevare valori.

1 pub l i c c l a s s XMLReader {pub l i c s t a t i c S t r ing getTagKey ( i n t conten i to r e , S t r ing

chiave , S t r ing t ipo , Ac t i v i ty a ) {3 t ry {

XmlResourceParser p= a . getResources ( ) . getXml ( c on t en i t o r e ) ;5 i n t eventType= p . getEventType ( ) ;

boolean g iu s t o=f a l s e ;7 whi le ( eventType !=XmlResourceParser .END_DOCUMENT){

9 i f ( eventType==XmlResourceParser .START_TAG){St r ing tagName=p . getName ( ) ;

11 i f ( ch iave . equa l s ( tagName) ) {St r ing Tipo=p . getAttr ibuteValue ( nu l l , " type" ) ;

13 Log . v ( "Codice type " , Tipo ) ;i f ( Tipo . equa l s ( t i po ) ) {

15 g iu s t o=true ;Log . v ( "Codice Type" , "Era que l l o che cercavamo" ) ;

17 }e l s e {

19 g iu s t o=f a l s e ;}

21 }e l s e {

23 g iu s t o=f a l s e ;}

25 }e l s e i f ( eventType==XmlResourceParser .TEXT){

27 i f ( g i u s t o ) {St r ing r e t=p . getText ( ) ;

29 re turn r e t ;}

52 CAPITOLO 3. SVILUPPO DELL'APPLICAZIONE BOLLETTINO

31 }eventType= p . next ( ) ;

33 }} catch ( Exception e ) {

35 e . pr intStackTrace ( ) ;r e turn nu l l ;

37 }

39 re turn nu l l ;}

41 }

Questo metodo accetta in ingresso:

• contenitore: un int che contiene il codice del �le xml da cui bisognacercare le chiavi

• chiave: uno String che contiene il nome del tag che si vuole cercare

• tipo: uno String che contiene il nome del dell'attributo type associatoal tag.

• a: Una variabile di tipo Activity.

3.6.3 Spedizione SMS

Adesso che sappiamo come prendere una stringa dall'xml possiamo inviaree�ettivamente l'sms.

Come prima cosa, onde evitare che l'sms venga inviato piú volte perpressioni multiple accidentali del tasto, con e�etti non troppo piacevoli sulconto corrente, lo disabilitiamo dopo la pressione e lo manteniamo disabilitato�nché non abbiamo la conferma che il messaggio non é stato inviato.

Come seconda cosa dobbiamo sostituire le parole chiave che otteniamodall'XML con quelle che l'utente ha inserito nei campi di testo.

In�ne dobbiamo inviare l'sms vero e proprio e dare una conferma all'u-tente che il messaggio é stato e�ettivamente inviato. Ecco il codice associatoalla pressione del tasto invia SMS.

1 pub l i c void onCl ick (View v ) {( ( Button ) findViewById (R. id . i nv i a ) ) . setEnabled ( f a l s e ) ;

3 St r ing sms=XMLReader . getTagKey (R. xml . v a r i a b i l i , "sms" ," b o l l e t t i n o " , que s taAct iv i ty ) ;

Log . v ( " Lettura XML SMS" , sms ) ;5 St r ing

CC=((EditText ) findViewById (R. id . contCont ) ) . getText ( ) . t oS t r i ng ( ) ;

3.6. INVIO DELLE INFORMAZIONI 53

St r ingimporto=((EditText ) findViewById (R. id . importo ) ) . getText ( ) . t oS t r i ng ( ) ;

7 St r ingi n t e s t a t a r i o =((EditText ) findViewById (R. id . i n t e s t a t a r i o ) ) . getText ( ) . t oS t r i ng ( ) ;

S t r ingcausa l e =((EditText ) findViewById (R. id . cau sa l e ) ) . getText ( ) . t oS t r i ng ( ) ;

9 sms=sms . r ep l a c e ( "$CC$" , CC) ;sms=sms . r ep l a c e ( " $importo$" , importo ) ;

11 sms=sms . r ep l a c e ( " $ i n t e s t a t a r i o $ " , i n t e s t a t a r i o ) ;sms=sms . r ep l a c e ( " $causa l e$ " , cau sa l e ) ;

13 sms=sms . r ep l a c e ( "$nome$" , "nome" ) ;sms=sms . r ep l a c e ( " $ i n d i r i z z o $ " , " i n d i r i z z o " ) ;

15 sms=sms . r ep l a c e ( "$CAP$" , "CAP" ) ;sms=sms . r ep l a c e ( " $ l o c a l i t a $ " , " l o c a l i t \ ' a" ) ;

17 Log . v ( " Lettura XML SMS" , sms ) ;Log . v ( "Lunghezza SMS" , sms . l ength ( )+"" ) ;

19 St r ing numeroTel=XMLReader . getTagKey (R. xml . v a r i a b i l i ,"numero" , " b o l l e t t i n o " , que s taAct iv i ty ) ;

sendSMS( numeroTel , sms ) ;21 }

Il metodo sendSMS deve prendere il messaggio e controllare se questo è mag-giore o minore di 160 caratteri: se é inferiore manda un messaggio singolo,altrimenti manda un messaggio composto (multipartSms). Inoltre deve veri-�care che l'invio sia e�ettivamente avvenuto oppure si sia veri�cato qualcheerrore (dovuto ad esempio alla mancanza di rete).

1 pr i va t e void sendSMS( St r ing phoneNumber , S t r ing message ) {St r ing SENT = "SMS_SENT" ;

3 St r ing DELIVERED = "SMS_DELIVERED" ;SmsManager sms = SmsManager . ge tDe fau l t ( ) ;

5 f i n a l i n t numeroParti ;ArrayList<Str ing> part s=nu l l ;

7 i f ( message . l ength ( )<=160){numeroParti=1;

9 }e l s e {

11 par t s = sms . div ideMessage ( message ) ;numeroParti=par t s . s i z e ( ) ;

13 }PendingIntent sentPI = PendingIntent . getBroadcast ( th i s , 0 , new

Intent (SENT) , 0) ;15 BroadcastRece iver bro=new BroadcastRece iver ( ) {

i n t i n v i a t i =0;17 pub l i c void onReceive ( Context arg0 , In tent arg1 ) {

i f ( i n v i a t i <0){19 re turn ;

54 CAPITOLO 3. SVILUPPO DELL'APPLICAZIONE BOLLETTINO

}21 switch ( getResultCode ( ) ) {

case Act i v i t y .RESULT_OK:23 i n v i a t i++;

i f ( i n v i a t i >=numeroParti ) {25 Toast . makeText ( getBaseContext ( ) , "SMS inv i a t o con

succ e s so " ,Toast .LENGTH_SHORT) . show ( ) ;

27 un r e g i s t e rRe c e i v e r ( t h i s ) ;f i n i s h ( ) ;

29 }e l s e {

31 Toast . makeText ( getBaseContext ( ) , " Inv io in Corso . . ."+i n v i a t i+"/"+numeroParti , Toast .LENGTH_SHORT) . show ( ) ;

}33 break ;

case SmsManager .RESULT_ERROR_GENERIC_FAILURE:35 Toast . makeText ( getBaseContext ( ) ,

"Errore i nv i o " ,Toast .LENGTH_SHORT) . show ( ) ;

37 i n v i a t i =−1;( ( Button ) findViewById (R. id . i nv i a ) ) . setEnabled ( t rue ) ;

39 break ;case SmsManager .RESULT_ERROR_NO_SERVICE:

41 Toast . makeText ( getBaseContext ( ) , "SMS non i nv i a t o perassenza d i s e r v i z i o " ,

Toast .LENGTH_SHORT) . show ( ) ; i n v i a t i =−1;43 ( ( Button ) findViewById (R. id . i nv i a ) ) . setEnabled ( t rue ) ;

break ;45 case SmsManager .RESULT_ERROR_NULL_PDU:

Toast . makeText ( getBaseContext ( ) , "Nul l PDU" ,Toast .LENGTH_SHORT) . show ( ) ;

47 i n v i a t i =−1;( ( Button ) findViewById (R. id . i nv i a ) ) . setEnabled ( t rue ) ;

49 break ;case SmsManager .RESULT_ERROR_RADIO_OFF:

51 Toast . makeText ( getBaseContext ( ) , "SMS non i nv i a t operch \ ' e i l c e l l u l a r e \ ' e in modal it \ ' a OFFLINE oAEREO" ,

Toast .LENGTH_LONG) . show ( ) ;53 i n v i a t i =−1;

( ( Button ) findViewById (R. id . i nv i a ) ) . setEnabled ( t rue ) ;55 break ;

}57 }

59 } ;i f ( message . l ength ( ) <=160){

3.6. INVIO DELLE INFORMAZIONI 55

61 r e g i s t e rR e c e i v e r ( bro , new I n t e n tF i l t e r (SENT) ) ;sms . sendTextMessage (phoneNumber , nu l l , message , sentPI ,

nu l l ) ;63 }

e l s e {65 ArrayList<PendingIntent> PI = new ArrayList<PendingIntent >() ;

r e g i s t e rR e c e i v e r ( bro , new I n t e n tF i l t e r (SENT) ) ;67 f o r ( i n t i =0; i<numeroParti ; i++){

PI . add ( PendingIntent . getBroadcast ( th i s , 0 , newIntent (SENT) , 0) ) ;

69 }sms . sendMultipartTextMessage (phoneNumber , nu l l , parts , PI ,

nu l l ) ;71 }

}

In questo caso non usiamo un Intent per mandare un messaggio, perchénon andremo ad utilizzare un'Activity, ma direttamente le API native delcellulare.

L'SmsManager é un oggetto che ci consente di inviare un messaggio.Il BroadcastReceiver é un'interfaccia che ha il metodo onReceive() cheviene richiamato al termine di un'operazione, in questo caso al terminedell'operazione di invio dell'SMS.

Come funziona il BroadcastReceiver? Il BroadcastReceiver viene crea-to implementando il metodo de�nito nell'interfaccia. In seguito viene asso-ciato a un IntentFilter (in questo caso associato alla stringa SMS_SENTcontenuto nella variabile SENT). L'associazione viene fatta quando si usail metodo registerReceiver. Così facendo si é impostato un intent �lter,cioè quando viene chiamato un intent (non esplicito) associato alla stringa}SMS_SENT~, risponderà il nostro broadcast receiver. Ovviamente questoé valido �nché l'Activity non termina. Se si vuole creare un intent �lter validoper tutte le applicazioni lo si deve de�nire all'interno del �le AndroidMani-fest.xml come vedremo in seguito. Ora dobbiamo solo comunicare al metodoche invia l'SMS quale Intent chiamare al termine della sua esecuzione, che sa-rà new Intent(SENT). Lo comunichiamo tramite l'oggetto PendingIntent.Per crearlo usiamo il metodo statico getBroadcast a cui chiediamo di dar-ci il pending intent associato a questa Activity che risponde alla chiamataSENT. In�ne inviamo il messaggio tramite il metodo sendTextMessage seil messaggio ha un numero massimo di 160 caratteri, altrimenti usiamo ilmetodo sendMultipartTextMessage.

56 CAPITOLO 3. SVILUPPO DELL'APPLICAZIONE BOLLETTINO

Come si può notare, a di�erenza del primo, che accetta uno singolo, il se-condo metodo accetta una lista concatenata di pendingIntent. Questo perchéquando si invia un messaggio composto da piú parti, il telefono invia tuttele parti come messaggi e�ettivamente separati e, al termine di ogni invio,viene chiamato il pendingIntent associato. Nel nostro caso il pendingIntenté lo stesso ma in una situazione generale possiamo richiamare pending intentdiversi per ogni parte del messaggio.

Diamo uno sguardo ora all'implementazione dell'interfaccia Broadca-stReceiver. Tramite il metodo getResultCode() riusciamo a capire se ilmessaggio associato è stato inviato oppure no. L'intero che viene restituitopuò assumere i valori che vediamo visualizzati nel metodo stesso, i cui signi�-cati sono spiegati nei Toast. Al termine di ogni tipo di errore viene riabilitatoil tasto invia, per consentire il rinvio del messaggio. Se tutti i messaggi so-no stati inviati con successo l'Activity termina e noti�ca all'utente l'esitopositivo.

Capitolo 4

Sviluppo dell'Applicazione Codice

a Barre

Passiamo adesso alla creazione di un tool per il riconoscimento dei codici abarre nell'applicazione.

4.1 Obiettivi

L'obiettivo di questo tool è di riconoscere dei codici a barre di tipo EAN-131.E associare ad ogni codice una descrizione in modo che, ogni volta che si ripre-senti lo stesso codice, il programma mostri la descrizione de�nita al momentodell'inserimento. Questo tool è la base per poi (in futuro) usare un databaseonline condiviso da più cellulari/pc che utilizzino quest'applicazione.

4.2 I codici a barre EAN-13

I codici a barre EAN-13 sono un tipo particolare di codice a barre. Sonocomposti da 13 cifre di cui 12 signi�cative e la tredicesima di controllo.

4.2.1 Generazione della cifra di controllo

La cifra di controllo viene generata in funzione delle altre 12 e serve a limitaregli errori di lettura del codice stesso. In pratica il lettore legge il codice abarre (tutte e 13 le cifre) e poi partendo dalle prime 12, genera la tredicesima.Se questa combacia con quella letta allora possiamo accettare il codice. Così

1Spiegati nel capitolo seguente

57

58CAPITOLO 4. SVILUPPODELL'APPLICAZIONE CODICE A BARRE

Figura 4.1: Esempio di codice a barre EAN-13

facendo diminuisce sensibilmente la probabilità di avere un errore durante lalettura.La tredicesima cifra si ottiene nel seguente modo:

• De�niamoD come la somma delle cifre nelle posizioni dispari del codiceletto. (cioè la prima cifra + la terza + la quinta ecc...)

• De�niamo P come la somma delle cifre nelle posizioni pari del codiceletto.

• Eseguiamo questa somma N=D+(P*3)

• De�niamo S come la decina superiore di N. (Cioè se N=11 allora S=20,se N=24 allora S=30, se N=40 allora S=40)

• La cifra di controllo C si ottiene facendo C=S-N.

Esempio Se il codice é 123456789012 per calcolare la tredicesima cifrafacciamo:D=1+3+5+7+9+1=26P=(2+4+6+8+0+2)=22N=26+(22*3)=26+66=92S=100

4.3. LA LIBRERIA ZXING E L'APPLICAZIONE BARCODE SCANNER59

C=100-92=8

La codi�ca del codice a barre vera e propria viene spiegata in manieraesauriente nella sezione 4.7.

4.3 La libreria Zxing e L'applicazione Barcode

Scanner

4.3.1 Introduzione

Durante la ricerca di un'applicazione open source da poter inserire nel proget-to mi sono imbattuto subito in Barcode Scanner che utilizza la libreria Zxingper riconoscere i codici a barre. Quest'applicazione, la cui documentazioneé disponibile nella bibliogra�a [ZXing (Zebra Crossing)], non è solo in gradodi riconoscere i codici a barre EAN-13, ma anche la maggior parte dei codicia barre presenti sul mercato e codici QR. Dobbiamo quindi tenere conto chepotrebbe restituirci un codice non in formato EAN-13 e gestire l'evento.

4.4 Integrazione di Barcode Scanner col pro-

gramma

Grazie all'utilizzo di Intent non espliciti si può utilizzare Activity non inclu-sa nell'applicazione. Quindi per integrare il software possiamo seguire duestrade: includere i sorgenti nel software oppure chiedere all'utente di scari-care l'applicazione barcode scanner e utilizzarla tramite intent. Vediamoleentrambe.

4.4.1 Utilizzo di barcode Scanner non incluso nell'ap-

plicazione

Per utilizzare il barcode scanner non incluso nell'applicazione dobbiamo ne-cessariamente veri�care che questo sia stato installato dall'utente e, in casocontrario farglielo installare. Per sapere se il Barcode Scanner é stato instal-lato sul cellulare usiamo il PackageManager. Ecco le righe di codice di cuiabbiamo bisogno.

PackageManager packageManager = th i s . getPackageManager ( ) ;2 Reso lve In fo r e s o l v e I n f o = packageManager . r e s o l v eAc t i v i t y (

SCAN_INTENT, PackageManager .GET_RESOLVED_FILTER) ;

60CAPITOLO 4. SVILUPPODELL'APPLICAZIONE CODICE A BARRE

dove SCAN_INTENT é un intent cosí de�nito

pr i va t e s t a t i c f i n a l In tent SCAN_INTENT = newIntent ( "com . goog l e . zx ing . c l i e n t . android .SCAN" ) ;

cioè associato alla particolare Activity scansione di Android Scanner. Se lavariabile resolverInfo é null allora non c'è nessun Intent Filter che rispon-de alla chiamata di quell'Intent, ovvero Barcode Scanner non è installato.In questo caso noti�chiamo all'utente e lo indirizziamo verso il downloaddell'applicazione. In caso contrario lo facciamo accedere al sottomenù checonsentirá poi di aggiungere, visualizzare e veri�care i codici a barre.

4.4.2 Inserimento di barcode Scanner nel progetto

Se invece vogliamo inglobare barcode Scanner nel progetto dobbiamo operarecome segue. Intanto procurarci i sorgenti (scaricandoli dal sito indicato a4.3.1): lo zip di Zxing contiene tutti i progetti per tutte le piattaforme, a noiinteressa soltanto android e core, per cui tutto il resto può essere eliminato.Creiamo un nuovo progetto con eclipse e importiamo tutti i �le contenutinella cartella Android. Dopo l'importazione ci saranno sicuramente deglierrori perché dobbiamo anche inserire la libreria Zxing.

Per importarla clicchiamo col destro sul progetto appena creato �> buildPath �> Con�gure build path. In librarys aggiungiamo la libreria core.jarche si trova nella cartella Core del �le appena scaricato, aggiungiamo anchecore.jar al nostro progetto originale e, in�ne, mettiamo la spunta su isLibrary,come abbiamo visto in 3.2.2. Andiamo nelle impostazioni del nostro progettoprincipale e nelle proprietà�>(Scheda)Android oltre a tesseract aggiungiamoanche il progetto appena creato. Ora abbiamo aggiunto il Barcode Scanneral progetto .

4.4.3 Creazione di un intentFilter

Il problema ora è che il progetto non sa che può soddisfare gli intent ditipo SCAN_INTENT. Quindi dobbiamo andare dentro al �le AndroidMani-fest.xml e dichiarare il nuovo intent Filter. Apriamo quindi il �le Android-Manifest e inseriamo questo codice prima della �ne del tag application

1 <a c t i v i t yandroid : name="com . goog l e . zx ing . c l i e n t . android . CaptureAct iv i ty "

4.5. CREAZIONE E IMMAGAZZINAMENTODATI IN UN DATABASE SQLITE61

android : s c r e enOr i en ta t i on=" landscape "3 android : conf igChanges=" o r i e n t a t i o n | keyboardHidden"

android : theme="@android : s t y l e /Theme . NoTitleBar . Fu l l s c r e en "5 android : windowSoftInputMode=" stateAlwaysHidden">

<intent− f i l t e r >7 <act i on android : name="com . goog l e . zx ing . c l i e n t . android .SCAN"/>

<category android : name="android . i n t en t . category .DEFAULT"/>9 </intent− f i l t e r >

</ac t i v i t y >

Abbiamo così inserito una nuova Activity: CaptureActivity. Tutti i campiprima di <intent-�lter> servono a de�nire l'orientamento, la visualizzazionedella tastiera, e impostano il full screen. all'interno dei tag <intent-�lter>vediamo la de�nizione dell'Intent.

Abbiamo quindi detto che, ad ogni chiamata di Intent non esplicito conla stringa indicata in action, risponde l'Activity CaptureActivity.

4.5 Creazione e immagazzinamento dati in un

DataBase SQLite

Creiamo un metodo statico che ci restituisca un dataBase SQLite funzionan-te. Il metodo crea un �le chiamato database.db nella cartella tesseract (giàcreata nell'activity precendente) dove al suo interno viene scritto un databa-se contenente una tabella (se non esiste) denominata codiciabarre la qualecontiene due campi:

ID Un long contenente il codice a barre che é anche chiave primaria

DESCRIZIONE un varchar (una Stringa) contenente una descrizione di massimo 300caratteri.

c l a s s dataBaseCreator {2 pub l i c s t a t i c SQLiteDatabase getDatabase ( Act i v i ty a ) {

4 F i l e Dir = Environment . ge tExte rna lS to rageDi r e c to ry ( ) ;Dir= new F i l e ( Dir , " t e s s e r a c t " ) ;

6 Dir= new F i l e ( Dir , " database . db" ) ;SQLiteDatabase data=SQLiteDatabase . openOrCreateDatabase ( Dir ,

nu l l ) ;8 data . execSQL ( "CREATE TABLE IF NOT EXISTS

\" cod i c i a ba r r e \"(\" id \" LONG PRIMARY KEY,\" d e s c r i z i o n e \" VARCHAR(300) ) " ) ;

r e turn data ;

62CAPITOLO 4. SVILUPPODELL'APPLICAZIONE CODICE A BARRE

10 }

12 }

Come si vede dal codice, il metodo ci restituisce il database sottoforma di unoggetto di tipo SQLiteDatabase; se non esiste perché è la prima volta che ilmetodo viene eseguito su una determinata macchina, lo crea. Vediamo comeinserire un codice a barre all'interno del DataBase SQL

data . execSQL( "INSERT INTO cod i c i aba r r e ( id , d e s c r i z i o n e )VALUES ( "+codiceABarre+" , ' "+d e s c r i z i o n e+" ' ) " ) ;

2 data . c l o s e ( ) ;f i n i s h ( ) ;

Vediamo così che stiamo inserendo una coppia id,descrizione nel DataBasemediante SQL nativo. Al termine dell'operazione chiudiamo il database el'Activity. É necessario invocare il metodo data.close() per evitare che vadain errore alla chiusura dell'Activity o alla riapertura del database all'internodella stessa. Diamo un'occhiata al codice per veri�care se un entry è giàpresente nel DataBase:

1 SQLiteDatabase data=dataBaseCreator . getDatabase ( ) ;Cursor c=data . query ( " c od i c i aba r r e " , new

St r ing [ ] { " d e s c r i z i o n e " } , " id="+codiceABarre , nu l l , nu l l ,nu l l , nu l l ) ;

3 St r ing d e s c r i z i o n e ;i f ( c . moveToNext ( ) )

5 d e s c r i z i o n e=c . g e tS t r i ng (0 ) ;e l s e {

7 d e s c r i z i o n e="Codice non r e g i s t r a t o " ;}

9 c . c l o s e ( ) ;data . c l o s e ( ) ;

Creiamo quindi una variabile di tipo Cursor dove vengono passati i parametri:

• la tabella (codiciabarre) su cui vogliamo e�ettuare la Query

• i campi che ci interessano (descrizione)

• il WHERE della Query

4.6. CREARE UNA LISTA DI ELEMENTI GRAFICI 63

Gli altri parametri, che nel nostro caso sono impostati a null, sono le altrecomponenti dell'SQL, come il groupBy, l'Having e l'OrderBy che, nel nostroparticolare caso, non ci interessano. Il metodo moveToNext() chiede allavariabile Cursor se c'è almeno una riga che rispetta la query. Se non c'ènella variabile descrizione verrà impostata la stringa Codice non registrato.Per visualizzare tutti i codici a barre con relative descrizioni registrati neldatabase dobbiamo e�ettuare una Query piú semplice che peró risponde piúdi un valore. Vediamo quindi come estrapolare tutte le righe della query.

SQLiteDatabase data= dataBaseCreator . getDatabase ( ) ;2 Cursor c=data . query ( " c od i c i a ba r r e " , new St r ing [ ] " id "

, " d e s c r i z i o n e " } , nu l l , nu l l , nu l l , nu l l , nu l l ) ;whi l e ( c . moveToNext ( ) ) {

4 s fondo . addView ( c r eaVi s ta ( sfondo , c . getLong (0 ) ,c . g e tS t r i ng (1 ) ) ) ;

}6 c . c l o s e ( ) ;

data . c l o s e ( ) ;

In questo caso abbiamo lasciato a null il campo SELECT che equivale adavere un SELECT *. Finché ci sono elementi, viene creata una vista conla visualizzazione del codice a barre e della descrizione, in�ne si chiude ildatabase. Il metodo creaVista lo vediamo nella prossima sezione.

4.6 Creare una lista di elementi gra�ci

Per creare una lista di elementi gra�ci dello stesso tipo abbiamo bisogno delLayoutIn�ater. Il LayoutIn�ater ci consente di duplicare i layout contenutinei �le xml. Per creare una lista di elementi uguali dobbiamo però primacreare l'elemento da copiare.

4.6.1 Creazione dell'elemento

Quindi creiamo un nuovo �le Layout xml. Questo �le conterrà l'immaginedel codice a barre (che vedremo come generare dal codice nella sezione 4.7)a sinistra, e subito a destra la descrizione associata al codice, come mostratoin �gura 4.2. Il codice xml per generare questo layout é il seguente:

1 <?xml ve r s i on=" 1 .0 " encoding="utf−8"?>

3

<Relat iveLayout

64CAPITOLO 4. SVILUPPODELL'APPLICAZIONE CODICE A BARRE

Figura 4.2: Creazione dell'elemento

5 and ro i d : i d="@+id / v i s t a "xmlns :andro id=" ht tp : // schemas . android . com/apk/ r e s / android "

android : layout_width="match_parent"7 andro id : l ayout_he ight="wrap_content"

android:paddingTop="1dp"9 android:paddingBottom="1dp">

11 <ImageViewandro i d : i d="@+id / immagineCodiceABarre"

13 android : layout_width="wrap_content"andro id : l ayout_he ight="wrap_content"

15 andro id : l ayout_a l i gnParentLe f t=" true "andro id : l ayou t_cen t e rVe r t i c a l=" true "

17 andro id : c on t en tDe s c r i p t i on="@str ing / barre "and r o i d : s r c="@drawable/ load ing " />

19

<TextView21 and ro i d : i d="@+id / descr i z ioneCodiceABarre "

android : layout_width="wrap_content"23 andro id : l ayout_he ight="wrap_content"

andro id : l ayou t_cen t e rVe r t i c a l=" true "25 andro id :padd ingLe f t="3dp"

android : layout_toRightOf="@+id / immagineCodiceABarre"27 and ro i d : t ex t="@str ing / barre "

/>29

</Relat iveLayout>

../software/res/layout/elemento.xml

Il campo android:padding serve a de�nire quanto margine lasciare, in modotale che gli elementi non siano spiacevolmente attaccati gli uni agli altri. Peril resto, i comandi sono tutti già conosciuti.

4.6.2 Duplicazione elementi gra�ci

Ora che abbiamo il nostro elemento.xml da usare per visualizzare ogni sin-golo codice a barre, dobbiamo duplicarlo tante volte quanti sono i codici neldatabase e assegnargli l'immagine relativa al codice e la sua descrizione.

4.6. CREARE UNA LISTA DI ELEMENTI GRAFICI 65

Prima di tutto però dobbiamo creare un nuovo �le xml con un layout ascorrimento dove inserire tutti questi elementi. Abbiamo già visto come sicrea un layout a scorrimento tramite ScroolView in 3.6.1. Creiamo quindiuno ScroolView e, al suo interno, mettiamo un LinearLayout.

Vediamo ora il metodo creaVista:

pr i va t e View creaVi s ta (ViewGroup v i s ta , f i n a l long codice , f i n a lS t r ing d e s c r i z i o n e ) {

2 Layout In f l a t e r i n f l a t e r = ( Layou t In f l a t e r ) getSystemServ ice (Context .LAYOUT_INFLATER_SERVICE) ;

View view = i n f l a t e r . i n f l a t e ( R. layout . elemento , v i s ta ,f a l s e ) ;

4 ( ( ImageView ) view . findViewById (R. id . immagineCodiceABarre ) ) . setImageBitmap (c o r r e t t o r eG r a f i c o . creaCodiceEan13 ( codice , 1) ) ;

( ( ImageView ) view . findViewById ( R. id . immagineCodiceABarre )) . s e tContentDesc r ip t i on ( cod i c e+"" ) ;

6 ( ( TextView ) view . findViewById(R. id . descr i z ioneCodiceABarre ) ) . setText ( d e s c r i z i o n e ) ;

view . s e tC l i c k ab l e ( t rue ) ;8 view . s e tOnCl i ckL i s t ene r (new OnCl ickListener ( ) {

pub l i c void onCl ick (View v ) {10 s chermataVisua l i z za ( c o r r e t t o r eG r a f i c o . creaCodiceEan13 (

codice , 2) , d e s c r i z i o n e ) ;}

12 }) ;view . se tLongCl i ckab l e ( t rue ) ;

14 registerForContextMenu ( view ) ;re turn view ;

16 }

Il LayoutIn�ater crea appunto dei duplicati del layout elemento, ai qua-li poi sostituiamo l'immagine e la descrizione con quelle passate dai duevalori in ingresso (codice e descrizione). Modi�chiamo inoltre anche ilContentDescription (vedremo in seguito a cosa ci serve modi�carlo).

Ad ogni elemento associamo un particolare OnClickListener che gli favisualizzare il codice in grande con la relativa descrizione.

Il metodo regiterForContextMenu serve a far comparire, alla pressionelunga dell'elemento, un menù, che vedremo dopo come creare.

4.6.3 ContextMenu

Il ContextMenu é un menù che compare sopra l'Activity e permette lascelta di uno o piú parametri.

66CAPITOLO 4. SVILUPPODELL'APPLICAZIONE CODICE A BARRE

Le varie opzioni del menú vengono de�nite in un �le xml. Per crearloandiamo su New->Other.. e selezioniamo Android XML �le. Poi dal menù atendina accanto a resource Type selezioniamo Menù. Ora possiamo inseriretutte le voci del menù che vogliamo. In questo caso solo una, ovvero delete.É su�ciente compilare i campi id e title per avere la voce del menú completae funzionante.

Adesso dobbiamo comunicare all'Activity quale menù lanciare e lo de�-niamo sovrascrivendo il metodo onCreateContextMenu

pub l i c void onCreateContextMenu (ContextMenu menu , Viewv , ContextMenuInfo menuInfo ) {

2 super . onCreateContextMenu (menu , v , menuInfo ) ;l a n c i a t a=v ;

4 MenuInf later i n f l a t e r = getMenuIn f la te r ( ) ;

6 i n f l a t e r . i n f l a t e (R.menu . showmenu , menu) ;}

Alla pressione di ogni tasto viene invocato il metodo onContextItem-Selected che dobbiamo sovrascrivere.

1 pub l i c boolean onContextItemSelected (MenuItem item ) {switch ( item . getItemId ( ) ) {

3 case R. id . d e l e t e :rimuoviElemento ( l a n c i a t a ) ;

5 re turn true ;d e f au l t :

7 re turn super . onContextItemSelected ( item ) ;}

9 }

Il metodo getItemId ci restituisce l'ID del menù che abbiamo appena pre-muto (siccome è l'unico disponibile ci restituirà l'ID di delete, ma, in uncaso generale, potrebbero essercene di più). Alla pressione del tasto Deletelanciamo il metodo rimuoviElemento che rimuove il codice a barre dal videoe dal database.

4.7 Generazione dei codici a barre EAN-13

In questa sezione parleremo di come generare l'immagine Bitmap a partiredal codice a barre in ingresso.

4.7. GENERAZIONE DEI CODICI A BARRE EAN-13 67

4.7.1 La codi�ca EAN-13

Questa codi�ca trasforma i 13 numeri passati in ingresso in un codice binario,il quale viene poi trasformato in barrette secondo la logica: 1 corrisponde allabarra nera, 0 corrisponde a quella bianca. Le barre vanno ra�gurate tutteattaccate le une alle altre senza spaziature, quindi la ra�gurazione di 11apparirà come un unica sbarra del doppio dello spessore.

Ogni cifra ha tre possibili codi�che, la codi�ca A,B e C secondo questatabella:

cifra A B C0 0001101 0100111 11100101 0011001 0110011 11001102 0010011 0011011 11011003 0111101 0100001 10000104 0100011 0011101 10111005 0110001 0111001 10011106 0101111 0000101 10100007 0111011 0010001 10001008 0110111 0001001 10010009 0001011 0010111 1110100

Detto questo, possiamo iniziare a dare un'occhiata alla �gura 4.1. Intantonotiamo che le prime due barrette, le due centrali e le due �nali, sono piúlunghe rispetto alle altre. Questo perché sono dei caratteri standard chefanno capire al lettore di codice quanto larga é una barretta. Il codice quindié composto da 5 parti

• Parte Iniziale: composta sempre da 101

• Prima Parte: formata dalla conversione dei caratteri dal 2 al 7,secondo la codi�ca A o B (vedremo poi come)

• Stacco Centrale: composto sempre da 01010

• Seconda Parte: formata dalla conversione dei caratteri dall'8 al 13,secondo la codi�ca C

• Parte Finale: composta sempre dai caratteri 101

Notiamo subito che la prima cifra non é presente nell'elenco precedente. Que-sto perché non viene direttamente codi�cato in forma di barre ma dedottodal tipo di codi�ca delle prime 6 cifre. A decidere quali cifre codi�care secon-do la codi�ca A o la codi�ca B è appunto la prima cifra secondo la seguentetabella.

68CAPITOLO 4. SVILUPPODELL'APPLICAZIONE CODICE A BARRE

cifra codi�ca0 AAAAAA1 AABABB2 AABBAA3 AABBBA4 ABAABB5 ABBAAB6 ABBBAA7 ABABAB8 ABABBA9 ABBABA

Ad esempio se la prima cifra é 2 vorrà dire che la seconda, la terza, la sestae la settima cifra dovranno essere codi�cate secondo la codi�ca A, mentre laquarta e la quinta con la B.

4.7.2 La realizzazione dell'immagine

Per realizzare l'immagine ci serve innanzitutto un metodo che trasformi uningresso di tipo long in un insieme di bit codi�cati, come spiegato in 4.7.1.Una volta trasformata in bit, un altro metodo deve creare un'immagine apartire dalla sequenza di bit generata. Ecco il codice piú interessante, ovveroquello che genera la prima e la seconda parte della sequenza:

1 pr i va t e s t a t i c f i n a l S t r ing [ ] sequenza={"AAAAAA" ,"AABABB" , "AABBAB" , "AABBBA" , "ABAABB", "ABBAAB" , "ABBBAA" , "ABABAB" , "ABABBA" , "ABBABA" } ;

p r i va t e s t a t i c byte [ ] [ ] dammiCodifica ( S t r ing cod i c e ) {3 i f ( cod i c e . l ength ( ) !=13) {

throw new I l l ega lArgumentExcept ion ( " I l cod i c e i nv i a t o nonha i l numero d i c a r a t t e r i g i u s t o . Quel lo i n v i a t o ne ha"+cod i c e . l ength ( ) ) ;

5 }byte [ ] primaParte= new byte [ 4 2 ] ;

7 St r ing sequenzaAttuale=sequenza[ In t eg e r . pa r s e In t ( cod i c e . charAt (0 )+"" ) ] ;

f o r ( i n t i =1; i <7; i++){9 i n t ora=( i −1) ∗7 ;

byte [ ]qu e s t i=c o d i f i c a ( In t eg e r . pa r s e In t ( cod i c e . charAt ( i )+"" ) ,sequenzaAttuale . charAt ( i −1) ) ;

11 f o r ( i n t j =0; j <7; j++){primaParte [ ora+j ]= que s t i [ j ] ;

13 }}

15 byte [ ] secondaParte= new byte [ 4 2 ] ;

4.7. GENERAZIONE DEI CODICI A BARRE EAN-13 69

f o r ( i n t i =7; i <13; i++){17 i n t ora=( i −7) ∗7 ;

byte [ ]qu e s t i=c o d i f i c a ( In t eg e r . pa r s e In t ( cod i c e . charAt ( i )+"" ) ,'C ' ) ;

19 f o r ( i n t j =0; j <7; j++){secondaParte [ ora+j ]= que s t i [ j ] ;

21 }}

23 re turn new byte [ ] [ ] { primaParte , secondaParte } ;}

Innanzitutto si vede che non si accettano in ingresso sequenze con una lun-ghezza superiore o inferiore a 13. In base alla prima cifra, poi viene sceltala codi�ca da usare per i primi 6 caratteri. Il metodo codi�ca non vieneriportato per la sua banalità, questo semplicemente restituisce una sequenzadi 1 e di 0 in un array di lunghezza 7 secondo la codi�ca impostata.

Questo metodo in�ne ritorna la prima e la seconda parte del codice sottoforma di 2 array di byte.

Vediamo ora come viene generata l'immagine Bitmap del codice a barrea partire dal codice completo, ovvero comprensivo della parte iniziale, quellacentrale e quella �nale. L'iniziale e la �nale, dovendo avere una lunghezzasuperiore rispetto alla prima e alla seconda parte, avranno valore 2 anziché1.

i n t lunghezza=(codiceCompleto . l ength+7)∗dimBarra ;2 Bitmap barre=

Bitmap . createBitmap ( lunghezza , ( lunghezza ∗2) /3 , Conf ig .ARGB_8888) ;i n t [ ] barraNera=new in t [ barre . getHeight ( ) ∗dimBarra ] ;

4 f o r ( i n t i =0; i<barraNera . l ength ; i++){barraNera [ i ]=Color .BLACK;

6 }i n t [ ] barraBianca=new in t [ barre . getHeight ( ) ∗dimBarra ] ;

8 f o r ( i n t i =0; i<barraBianca . l ength ; i++){barraBianca [ i ]=Color .WHITE;

10 }f o r ( i n t i =0; i <7; i++){\\ co l o r a d i bianco l ' immagine per i pr imi 7

po s t i12

barre . s e tP i x e l s ( barraBianca , 0 , dimBarra , ( i ) ∗dimBarra , 0 ,dimBarra , barre . getHeight ( ) ) ;

14

16 }f o r ( i n t i =0; i<codiceCompleto . l ength ; i++){\\ crea l e barre

18 i f ( codiceCompleto [ i ]==1){

70CAPITOLO 4. SVILUPPODELL'APPLICAZIONE CODICE A BARRE

barre . s e tP i x e l s ( barraNera , 0 , dimBarra , (7+ i ) ∗dimBarra , 0 ,dimBarra , barre . getHeight ( ) ) ;

20 i n t lung=(barre . getHeight ( ) ∗5) /6 ;barre . s e tP i x e l s ( barraBianca , 0 , dimBarra , (7+ i ) ∗dimBarra ,

lung , dimBarra , barre . getHeight ( )−lung ) ;22 }

e l s e i f ( codiceCompleto [ i ]==0){24 barre . s e tP i x e l s ( barraBianca , 0 , dimBarra , (7+ i ) ∗dimBarra ,

0 , dimBarra , barre . getHeight ( ) ) ;}

26 e l s e i f ( codiceCompleto [ i ]==2){barre . s e tP i x e l s ( barraNera , 0 , dimBarra , (7+ i ) ∗dimBarra , 0 ,

dimBarra , barre . getHeight ( ) ) ;28

30 }}

32 Canvas c = new Canvas ( barre ) ;c . s e tDens i ty (Bitmap .DENSITY_NONE) ;

34 Paint p = new Paint ( ) ;p . s e tTextS i z e ( dimBarra ∗13) ;

36 p . s e tAnt iA l i a s ( t rue ) ;p . setFakeBoldText ( t rue ) ;

38 p . s e tCo lo r ( Color .BLACK) ;s . i n s e r t (1 , ' ' ) ;

40 s . i n s e r t (8 , ' ' ) ;c . drawText ( s . t oS t r i ng ( ) , 0 , ( barre . getHeight ( ) ) , p ) ;

42 re turn barre ;

La variabile dimBarra è la larghezza in pixel della sbarra del codice a barre.N.B. Da ora in poi userò come unità di misura la barra, che misurerà tantipixel quanti impostati nella variabile dimBarra. Andiamo a creare un'imma-gine Bitmap di larghezza pari al numero di barrette da inserire + 7. Il +7serve a lasciare lo spazio per la prima cifra del codice che deve essere inseritoin basso a sinistra2. BarraNera e barraBianca sono la rappresentazionesu array di interi delle le barrette, di colore rispettivamente nero e bianco,che poi andremo a inserire nell'immagine.

Il metodo createBitmap ci restituisce un'immagine vuota, completamentenera. Siccome vogliamo avere lo spazio riservato al primo carattere bianco,e�ettuiamo un ciclo for che colora di bianco le prime 7 barre.

Il secondo ciclo for, partendo dall'ottava barra, ne crea una bianca senell'array il valore é 0, di nero se é 1 lasciando peró 1

5di spazio libero a

2sono 7 barrette perché ogni cifra viene rappresentata da 7 bit, quindi 7 barrette

4.7. GENERAZIONE DEI CODICI A BARRE EAN-13 71

fondo immagine riservato alle cifre. Se il valore è 2, invece, crea una sbarracontinua �no in fondo senza lasciare alcuno spazio.

L'ultima parte di codice inserisce in fondo all'immagine le cifre del codicea barre.

72CAPITOLO 4. SVILUPPODELL'APPLICAZIONE CODICE A BARRE

Capitolo 5

Sviluppo dell'applicazione per

l'acquisto di biglietti

5.1 Obiettivi

Realizzare e sviluppare un applicazione che consenta agevolmente di acqui-stare,tramite cellulare, biglietti e abbonamenti per autobus, treni o traghetti.

5.2 Ideazione

Per realizzare un progetto del genere abbiamo bisogno di un'applicazione chefaccia scegliere all'utente che tipo di biglietto acquistare, da quale compagnia,per quale tratta e in quale quantità. In�ne produrre una richiesta di acquistoe noti�care l'utente dell'esito dell'operazione.

5.2.1 La località

Per l'acquisto del biglietto è necessario conoscere la località in cui si troval'utente, in maniera tale da fargli scegliere soltanto tra le compagnie e le trat-te che operano localmente sempli�cando e velocizzando di molto l'acquisto.Inoltre, è possibile che anche stesse compagnie in città diverse o�rano deiservizi di�erenti e con prezzi diversi.

5.2.2 Scelte Successive

Una volta decisa la località si farà scegliere all'utente la compagnia pressola quale vuole acquistare il biglietto. In seguito, verrà chiesto di selezionarela modalità di acquisto.

73

74 CAPITOLO 5. ACQUISTO BIGLIETTI

Modalità di acquisto Le modalità di acquisto sono 3: Biglietto Singo-lo, Biglietto Multiplo e Abbonamento.

Biglietto Singolo É il modo più veloce per acquistare un biglietto,utile se si è alla fermata e si ha necessità di comprare in fretta il titolo diviaggio.

Biglietto Multiplo Utile se si intende comprare più biglietti dellostesso tipo (comitive o carnet), oppure anche biglietti diversi della stessacompagnia in un unico passaggio (ad esempio tram e autobus).

Abbonamento Per acquistare un abbonamento ad una determinatacompagnia di trasporti. Verrà chiesta la data di quando si ha intenzione diattivare l'abbonamento e il numero di tessera.

5.2.3 Pagamento

Il pagamento avverrà attraverso SMS, come spiegato nel paragrafo 3.6. Sel'importo è su�cientemente elevato, come appunto nel caso di stipulazionedi abbonamenti, sarà richiesto il codice di controllo (vedi 5.3.6).

In breve, vorremmo ottenere una sequenza di schermate mostrata nella�gura 5.1

5.3 Realizzazione

Creiamo una nuova applicazione come descritto in 2, e la chiamiamo Bigliet-tiAutobus.

5.3.1 Disclaimer

Prima di tutto, bisogna far accettare al cliente alcune condizioni di utilizzodel programma e, possibilmente, dargli la possibilità di non visualizzarlo laprossima volta che lo si avvia. A�diamo la visualizzazione del disclaimer adun'Activity e creiamo un nuovo Layout da visualizzare.

CheckBox L'unico aspetto interessante in questo layout rispetto a quelligià visti precedentemente è la presenza di un CheckBox (Figura 5.2 ). Adi�erenza dei bottoni, non gli attribuiremo un listener, poiché questo tipo dielemento gra�co conserva lo stato, che andremo a leggere dopo che l'utente

5.3. REALIZZAZIONE 75

avrà premuto il bottone Accetto le condizioni. Riporto qui il pezzo di codiceche viene eseguito alla pressione del tasto:

pub l i c void OnContinua (View view ) {2 CheckBox c= (CheckBox ) findViewById (R. id . nonMostrare ) ;

i f ( c . isChecked ( ) ) {4 p r e f e r en z e . setNonMostareDisc la imer ( th i s , t rue ) ;

}6 s e tRe su l t (RESULT_OK) ;

f i n i s h ( ) ;8 }

Come si può notare, se il CheckBox è stato spuntato, chiama il metodo staticopreferenze.setNonMostareDisclaimer(this, true);. Questo metodo fain modo che si salvi la preferenza anche quando termina l'applicazione. Mavediamone ora il listato:

5.3.2 Salvataggio Preferenze

pub l i c s t a t i c void setNonMostareDisc la imer ( Act i v i ty a , booleanbol ) {

2 SharedPre f e rences p r e f s=a . ge tSharedPre f e r ence s ( Const .MY_PREFERENCES,Context .MODE_PRIVATE) ;

Editor e=p r e f s . e d i t ( ) ;4 e . putBoolean ( Const .NON_MOSTRARE, bol ) ;

e . commit ( ) ;6 }pub l i c s t a t i c boolean getNonMostareDisclaimer ( Act i v i ty a ) {

8 SharedPre f e rences p r e f s=a . ge tSharedPre f e r ence s ( Const .MY_PREFERENCES,Context .MODE_PRIVATE) ;

10 re turn p r e f s . getBoolean ( Const .NON_MOSTRARE, f a l s e ) ;}

Le informazioni vengono salvate in un data-base interno all'applicazione incui è possibile salvare dati come in un dizionario, ovvero si può attribuire auna determinata chiave uno speci�co valore.

Come si vede dal listato, utilizziamo il metodo getSharedPreferencesper ottenere l'accesso alle impostazioni salvate, associate alla stringa conte-nuta in MY_REFERENCES. La variabile MODE_PRIVATE indica che le

76 CAPITOLO 5. ACQUISTO BIGLIETTI

informazioni a cui accedo non sono condivise con altre applicazioni. Il meto-do edit mi restituisce un oggetto di tipo Editor che mi consente di inseriree modi�care le impostazioni salvate. Con il metodo putBoolean scrivo ilvalore della variabile booleana bol all'interno del DataBase associandolo allastringa NON_MOSTRARE. I tipi di variabile che si possono salvare sono ditipo boolean, �oat, int, long e String. Nel nostro esempio abbiamo salvatosolo una preferenza ma nel caso in cui volessimo salvarne diverse, basterà at-tribuire loro una chiave diversa. Prima dell'esecuzione del metodo commit()i nuovi dati o modi�che rimangono nell'oggetto }e~, e quindi in RAM; dopol'esecuzione del comando verrano �sicamente scritti nella memoria del tele-fono. La mancata esecuzione del metodo comporta un mancato salvataggiodelle impostazioni.

Per reperire le informazioni salvate non si dovrà fare altro che richiamareil metodo get<variabile> (nel nostro caso getBoolean dato che è una variabilebooleana) sull'oggetto prefs, come si evince analizzando il secondo metododel listato.

5.3.3 Selezione

Una volta accettate le condizioni, il programma dovrà far scegliere all'utentela città, la compagnia, la modalità ecc... In breve dovrà far compiere all'u-tente tante scelte multiple. Creiamo quindi un layout standard per la sceltamultipla.

Layout scelta multipla Voglio creare un oggetto che mi mostri una listadi elementi selezionabili, come ad esempio un elenco di città. Appena toccouno degli oggetti della lista questo si deve evidenziare, e alla pressione deltasto conferma devo essere in grado di capire quale o quali elementi sonostati selezionati.

Come prima cosa creo un nuovo tipo di oggetto, l'oggetto vista<E>che implementa l'interfaccia Serializable 1. La <E> indica che è un oggettogenerico, ovvero uno o più dei suoi campi sarà un oggetto non de�nito apriori. Vediamone il listato.

1 c l a s s v i s ta<E> implements S e r i a l i z a b l e {/∗∗

3 ∗∗/

5 pr i va t e s t a t i c f i n a l long ser ia lVers ionUID = 1L ;pub l i c S t r ing te s to , d e s c r i z i o n e ;

1Il motivo per cui l'oggetto estende l'interfaccia Serializable è che in tal modo èpossibile inviarlo attraverso le varie Activity

5.3. REALIZZAZIONE 77

7 pub l i c E ogg ;pub l i c Bitmap img ;

9 pub l i c boolean f r e c c i a ;pub l i c i n t quant i ta ;

11

pub l i c v i s t a ( S t r ing te s to , S t r ing d e s c r i z i on e , E ogg , Bitmapimage , boolean f r e c c i a , i n t quant i ta ) {

13 t h i s . t e s t o=t e s t o ;t h i s . ogg=ogg ;

15 img=image ;t h i s . f r e c c i a=f r e c c i a ;

17 t h i s . d e s c r i z i o n e=d e s c r i z i o n e ;t h i s . quant i ta=quant i ta ;

19 }pub l i c v i s t a ( S t r ing te s to , E ogg , Bitmap image , boolean

f r e c c i a ) {21 t h i s ( t e s to , nu l l , ogg , image , f r e c c i a ,−1) ;

23 }

25 }

Nelle variabili di istanza ne abbiamo due di tipo String testo e descrizioneche conterranno rispettivamente il testo che verrà visualizzato nella lista chepoi creeremo e un eventuale descrizione che comparirà in basso sotto al testo.La variabile ogg è l'oggetto generico. Vedremo in seguito perché ci sarà utile.img è l'eventuale immagine che si accompagna al testo per avere l'interfacciapiù accattivante. freccia è un �ag che ci dirà se aggiungere o no l'immaginedi una freccia nella visualizzazione dell'elemento. quantità è un intero incui verrà salvata un'eventuale quantità. Ad esempio, se volessimo prendere2 biglietti dello stesso tipo la variabile sarà impostata su 2.

Ora che abbiamo gli oggetti che de�niscono la lista non ci resta che crearegli oggetti gra�ci a partire dall'oggetto vista. Ad assolvere questo compitosarà l'oggetto oggettoLista<E>.

1 abs t r a c t c l a s s ogget toL i s ta<E>{pub l i c s t a t i c S t r ing VISTA_RETURN=" strBack " ;

3 pub l i c f i n a l v i s ta<E> v i s t a ;p ro tec ted Act i v i ty act ;

5 protec ted Relat iveLayout questo ;p r i va t e boolean s e l e c t e d ;

7 pub l i c f i n a l OnCl ickLis tener c l i c k ;pub l i c ogge t t oL i s t a ( v i s ta<E> v i s t a ) {

9 t h i s . v i s t a=v i s t a ;

11 c l i c k=getOnCl i ckL i s tener ( ) ;

78 CAPITOLO 5. ACQUISTO BIGLIETTI

13 }protec ted Relat iveLayout getRe lat iveLayout ( ViewGroup

overview , Act i v i t y a ) {15

Layout In f l a t e r i n f l a t e r =( Layou t In f l a t e r ) a . getSystemServ ice (Context .LAYOUT_INFLATER_SERVICE) ;

17 Relat iveLayout view = ( Relat iveLayout ) i n f l a t e r . i n f l a t e (R. layout . bo t t one f r e c c i a , overview , f a l s e ) ;

19 re turn view ;

21 }

23 pub l i c OnCl ickListener getOnCl i ckL i s tener ( ) {OnCl ickListener r= new OnCl ickListener ( ) {

25 pub l i c void onCl ick (View v) {OnCl ickLis tener ( v ) ;

27 }} ;

29 re turn r ;}

31 pub l i c ab s t r a c t void OnCl ickListener (View v ) ;pub l i c void f i n i s h ( ) {

33 act . f i n i s h ( ) ;}

35 pub l i c void r i t o rnaV i s t a ( v i s ta<E> s ) {Intent i n t en t=act . g e t In t en t ( ) ;

37 i n t en t . putExtra (VISTA_RETURN, s ) ;act . s e tRe su l t ( Ac t i v i ty .RESULT_OK, in t en t ) ;

39 f i n i s h ( ) ;}

41 protec ted Intent g e t In t en t ( ) {re turn act . g e t In t en t ( ) ;

43 }pub l i c View creaVi s ta ( Act i v i ty a , ViewGroup overview ) {

45

act=a ;47

Relat iveLayout view = getRe lat iveLayout ( overview , a ) ;49

questo=view ;51 agg io rnaVis ta ( ) ;

r e turn view ;53 }

pub l i c void agg io rnaVis ta ( ) {55 Relat iveLayout view=questo ;

i f ( v i s t a . img==nu l l ) {

5.3. REALIZZAZIONE 79

57 view . removeView ( view . findViewById (R. id . immagineElemento ) ) ;

}59 e l s e {

( ( ImageView ) view . findViewById (R. id . immagineElemento) ) . setImageBitmap ( v i s t a . img ) ;

61 }i f ( ! v i s t a . f r e c c i a ) {

63 view . removeView ( view . findViewById (R. id . immagineFreccia ) ) ;}

65 ( ( TextView ) view . findViewById (R. id . t e s t o ) ) . setText ( v i s t a . t e s t o ) ;view . s e tC l i c k ab l e ( t rue ) ;

67 view . s e tOnCl i ckL i s t ene r (new OnCl ickListener ( ) {pub l i c void onCl ick (View v ) {

69

OnCl ickListener ( v ) ;71

}73 }) ;

}75 pr i va t e Drawable prev ;

pub l i c void s e l e c t ( boolean s e l e c t ) {77 t h i s . s e l e c t e d=s e l e c t ;

i f ( s e l e c t ) {79 prev=questo . getBackground ( ) ;

questo . setBackgroundColor ( Color .YELLOW) ;81 }

e l s e {83 questo . setBackgroundDrawable ( prev ) ;

85 }}

87 pub l i c boolean i s S e l e c t e d ( ) {re turn s e l e c t e d ;

89 }pub l i c LinkedList<v i s ta<E>> ge tL i s t a ( ) {

91 I t e r a t o r <ogget toL i s ta<E>> i= s e l e c t e d . i t e r a t o r ( ) ;LinkedList<v i s ta<E>> re t= new LinkedList<v i s ta<E>>() ;

93 whi le ( i . hasNext ( ) ) {ogget toL i s ta<E> r=i . next ( ) ;

95 r e t . add ( r . v i s t a ) ;}

97 re turn r e t ;

99 }}

80 CAPITOLO 5. ACQUISTO BIGLIETTI

Come vediamo nel listato ho creato una classe astratta 2, lasciando comeunico metodo da implementare onClickListener.

Lo scopo di questo oggetto è di creare una view che rappresenti gra�ca-mente l'oggetto Vista passato nel costruttore(metodo creaVista(Activity,ViewGroup)), che sia possibile selezionarlo (metodo select()) e che siapossibile capire se è stato selezionato (metodo isSelected()).

Il metodo aggiornaVista() consente di aggiornare la gra�ca della vistastessa in base all'oggetto passato.

RitornaVista() invece termina l'Activity restituendo l'oggetto vista pas-sato al costruttore.

Ora che abbiamo gli oggetti per creare un elemento della lista possiamocreare la lista vera e propria.

Ogni lista selezionabile avrà le sue caratteristiche. Procedendo con ordine,vediamo come realizzare la prima, ovvero quella di scelta della città. Laschermata prevede che l'utente possa scegliere, tramite tocco, una e una solacittà dall'elenco.

Prima di realizzare la lista selezionabile creiamo una classe che mostrisemplicemente un elenco di oggettoLista che verrà poi esteso per realizzarele liste di cui abbiamo bisogno.

Il codice, davvero molto semplice, è il seguente

pub l i c c l a s s l i s taView<E> extends Scro l lV iew {2 protec ted ogget toL i s ta<E>[ ] l i s t a ;

p ro tec ted l i s t aV i ew ( Act i v i ty a c t i v i t y ) {4 super ( a c t i v i t y . getAppl i cat ionContext ( ) ) ;

}6 pub l i c l i s t aV i ew ( ogget toL i s ta<E>[] l i s t a s , Ac t i v i t y a c t i v i t y )

{super ( a c t i v i t y . getAppl i cat ionContext ( ) ) ;

8 i f ( l i s t a s==nu l l ) {throw new I l l ega lArgumentExcept ion ( " La v a r i a b i l e l i s t a s

non può e s s e r e nu l l " ) ;10 }

c r e aL i s t a ( l i s t a s , a c t i v i t y ) ;12 }

protec ted void c r e aL i s t a ( ogget toL i s ta<E>[ ] l i s t a s , Ac t i v i tya c t i v i t y ) {

14 l i s t a=l i s t a s ;LinearLayout l= new

LinearLayout ( a c t i v i t y . getAppl i cat ionContext ( ) ) ;

2Si dice classe astratta una classe che de�nisce un'interfaccia senza implementarla com-pletamente. Ciò serve come base di partenza per generare una o più classi specializzateaventi tutte la stessa interfaccia di base. Queste potranno poi essere utilizzate indi�eren-temente da applicazioni che conoscono l'interfaccia base della classe astratta, senza sapereniente delle specializzate.(Wikipedia)

5.3. REALIZZAZIONE 81

16 l . s e tOr i en t a t i on ( LinearLayout .VERTICAL) ;f o r ( ogget toL i s ta<E> l i s : l i s t a ) {

18 l . addView ( l i s . c r eaVi s ta ( a c t i v i t y , t h i s ) ) ;}

20 addView ( l ) ;l . getLayoutParams ( ) . he ight=LayoutParams .WRAP_CONTENT;

22 l . getLayoutParams ( ) . width=LayoutParams .FILL_PARENT;}

24 }

Cominciamo notando che questa classe estende ScroolView (già visto in3.6.1), dunque ci aspettiamo un layout a scorrimento.

Come si evince dal codice, il costruttore non si limita a controllare che lavariabile listas sia diversa da null e chiama il metodo creaLista. Quest'ulti-mo inserisce all'interno del layout le viste generate dal comando creaVista del-la classe oggettoLista. Le ultime due righe del metodo servono a dimensionarela lista view e hanno la stessa funzionalità dei comandi android:layout_widthe android:layout_width nel �le xml spiegati in 3.3.2.

Realizziamo ora la lista selezionabile di cui avevamo bisogno. Ovviamentela classe estenderà quella appena vista. Eccone il listato:

c l a s s s e l e c t ab l eL i s t aV i ew<E> extends l i s taView<E>{2 pr i va t e i n t maxSelectable ;

p r i va t e LinkedList<ogget toL i s ta<E>> s e l e c t e d ;4 protec ted s e l e c t ab l eL i s t aV i ew ( Act i v i t y a c t i v i t y ) {

super ( a c t i v i t y ) ;6 }

pub l i c s e l e c t ab l eL i s t aV i ew ( v i s ta<E>[ ] v i s t e , Ac t i v i tya c t i v i t y , i n t maxSelecteabl ) {

8 super ( a c t i v i t y ) ;s e l e c t e d= new LinkedList<ogget toL i s ta<E>>() ;

10 t h i s . maxSelectable=maxSelecteabl ;l i s t a = ext rac ted2 ( v i s t e . l enght ) ;

12 f o r ( i n t i =0; i<l i s t a . l ength ; i++){l i s t a [ i ]= new ogget toL i s ta<E>( v i s t e [ i ] ) {

14 pub l i c void OnCl ickLis tener (View v ) {i f ( ! i s S e l e c t e d ( ) ) {

16 i f ( s e l e c t e d . s i z e ( )>=maxSelectable ) {s e l e c t e d . remove ( ) . s e l e c t ( f a l s e ) ;

18 }s e l e c t ( t rue ) ;

20 s e l e c t e d . add ( t h i s ) ;}

22 e l s e {s e l e c t ( f a l s e ) ;

24 s e l e c t e d . remove ( t h i s ) ;

82 CAPITOLO 5. ACQUISTO BIGLIETTI

}26 }

} ;28 }

c r e aL i s t a ( l i s t a , a c t i v i t y ) ;30

32 }pub l i c LinkedList<v i s ta<E>> ge tL i s t a ( ) {

34 I t e r a t o r <ogget toL i s ta<E>> i= s e l e c t e d . i t e r a t o r ( ) ;LinkedList<v i s ta<E>> re t= new LinkedList<v i s ta<E>>() ;

36 whi le ( i . hasNext ( ) ) {ogget toL i s ta<E> r=i . next ( ) ;

38 r e t . add ( r . v i s t a ) ;}

40 re turn r e t ;

42 }pub l i c void s e t S e l e c t e dL i s t a ( LinkedList<v i s ta<E>> v) {

44 I t e r a t o r <v i s ta<E>> i= v . i t e r a t o r ( ) ;whi l e ( i . hasNext ( ) ) {

46 v i s ta<E> n=i . next ( ) ;ogge t toL i s ta<E> o= trovamiOggettoLista (n) ;

48 o . s e l e c t ( t rue ) ;s e l e c t e d . add ( o ) ;

50 }}

52 pr i va t e ogget toL i s ta<E> trovamiOggettoLista ( v i s ta<E> n) {f o r ( i n t i =0; i<l i s t a . l ength ; i++){

54 i f ( l i s t a [ i ] . v i s t a . equa l s (n) ) {re turn l i s t a [ i ] ;

56

}58 }

return nu l l ;60 }

pub l i c s e l e c t ab l eL i s t aV i ew ( v i s ta<E>[] v i s t e , Ac t i v i tya c t i v i t y , i n t maxSelecteabl , boolean r i e p i l o g o ) {

62 super ( a c t i v i t y ) ;s e l e c t e d= new LinkedList<ogget toL i s ta<E>>() ;

64 t h i s . maxSelectable=maxSelecteabl ;ogge t toRiep i l ogo<E>[ ] l i s t a = ext rac t ed ( v i s t e ) ;

66

f o r ( i n t i =0; i<l i s t a . l ength ; i++){68 l i s t a [ i ]= new ogget toRiep i l ogo<E>( v i s t e [ i ] ) {

pub l i c void OnCl ickListener (View v) {70 i f ( ! i s S e l e c t e d ( ) ) {

i f ( s e l e c t e d . s i z e ( )>=maxSelectable ) {72 s e l e c t e d . remove ( ) . s e l e c t ( f a l s e ) ;

5.3. REALIZZAZIONE 83

}74 s e l e c t ( t rue ) ;

s e l e c t e d . add ( t h i s ) ;76 }

e l s e {78 s e l e c t ( f a l s e ) ;

s e l e c t e d . remove ( t h i s ) ;80 }

82 }

84 } ;}

86 c r e aL i s t a ( l i s t a , a c t i v i t y ) ;}

88 @SuppressWarnings ( "unchecked" )p r i va t e ogge t toRiep i l ogo<E>[ ] ex t rac t ed ( v i s ta<E>[ ] v i s t e ) {

90 re turn ( ogget toRiep i l ogo<E>[ ] ) newogge t toR i ep i l ogo [ v i s t e . l ength ] ;

}92 @SuppressWarnings ( "unchecked" )

p r i va t e ogget toL i s ta<E>[] ext rac ted2 ( v i s ta<E>[] v i s t e ) {94 re turn ( ogget toL i s ta<E>[ ] ) new ogge t t oL i s t a [ v i s t e . l ength ] ;

}96 pub l i c LinkedList<ogget toL i s ta<E>> getSe l e c t edOgge t toL i s ta ( ) {

LinkedList<ogget toL i s ta<E>> re t=newLinkedList<ogget toL i s ta<E>>() ;

98 I t e r a t o r <ogget toL i s ta<E>> i=s e l e c t e d . i t e r a t o r ( ) ;whi l e ( i . hasNext ( ) ) {

100 r e t . add ( i . next ( ) ) ;}

102 re turn r e t ;}

104 }

Questa classe ha due costruttori pubblici, ma cominciamo analizzando soloil primo. Per ogni oggetto di tipo vista passato nel costruttore vediamoche viene creato un oggetto di tipo oggettoLista. Siccome oggettoLista èun oggetto di tipo astratto, devo implementare i metodi non implementati,in questo caso: OnClickListener. In questa maniera se l'oggetto non è giàselezionato, ad ogni click, si aggiunge alla LinkedList selected. Se la lista hagià raggiunto il numero massimo di elementi viene rimosso il primo per farespazio al nuovo arrivato. Se invece viene cliccato un elemento già selezionato,questo viene deselezionato e rimosso da selected.

I metodi setSelectedLista e getLista servono fare in modo che si possa

84 CAPITOLO 5. ACQUISTO BIGLIETTI

salvare lo {stato| della lista (ovvero fare in modo di ricordare cosa è statoselezionato e ripristinarlo). Questi metodi sono importanti per non perderela selezione mentre si ruota lo schermo3. Il metodo getLista inoltre è utileper ottenere la lista degli elementi selezionati.

Il metodo getSelectedOggettoLista() ci restituisce una copia della listaconcatenata contenente gli oggettoLista selezionati.

Il secondo costruttore è praticamente identico al primo, con l'unica di�e-renza avere oggettoLista al posto di oggettoRiepilogo. Vedremo in seguito acosa servirà.

Adesso che abbiamo i vari metodi per generare il layout a scelta multiplanon ci resta che utilizzarlo: partendo da un array di String -che vedremoin seguito come ottenere- creiamo un array di vista<String> per ogni cittàimpostando le variabili nome e ogg4 con il nome della città.

A questo punto invochiamo il costruttore di selectableListaView passan-dogli come parametro l'array di viste e aggiungendo la vista al layout, siottiene una schermata come quella della �gura 5.3.

Alla pressione del tasto continua viene invocato il metodo getLista perottenere l'oggetto vista in cui è scritto quale città è stata selezionata; que-st'ultimo in�ne viene inviato all'Activity successiva (in questo caso scegli-CompagniaActivity) come illustrato in 3.3.4

5.3.4 Reperimento dati

Il reperimento dati è a�dato alla classe htmlReader. Attualmente ho svi-luppato il metodo che consente di aprire pagine html leggendone il codi-ce, contando di estrapolare informazioni servendomi dei metodi della clas-se String. Tuttavia, non avendo ancora un server a cui allacciarci, sia-mo costretti a simulare l'arrivo di dati semplicemente ritornando dei dativerosimili.

Benché la connessione a internet non venga e�ettivamente utilizzata, èimportante inserire nell'android manifest i permessi di accesso.

<uses−permis s ion android : name="android . permis s ion .INTERNET"/>

3N.B.: Riusciamo a salvare la LinkedList ottenuta da metodo getSelected perchévista<E> è di tipo Serializable

4Che, in questo caso, è una stringa

5.3. REALIZZAZIONE 85

5.3.5 Layout biglietto multiplo

Come si vede nella �gura 5.1, nel caso in cui l'utente scelga il biglietto mul-tiplo, gli verrà data la possibilità di comprare biglietti di tipo e quantità di-verse. Siccome abbiamo bisogno di passare degli oggetti sottoforma di arrayattraverso Activity è necessario creare un nuovo oggetto di tipo Parcelable(come spiegato in 3.3.4)

Gli oggetti Parcelable A di�erenza di Serializable, Parcelable è un'in-terfaccia, quindi non ci limiteremo ad estendere la classe, bisognerà ancheimplementare tutti i metodi di interfaccia. Nella documentazione internet[Parcelable - Android Developers], si può trovare un esempio di implementa-zione, da cui è facile partire per poi creare la nostra.

Come realizzarla? É più complesso rispetto all'interfaccia Serialiable,infatti verrà dato direttamente a noi il compito di inviare i vari tipi di datocontenuti nell'oggetto.

Quello che andremo a realizzare sarà l'oggetto codicePrezzo. Per realiz-zarlo copiamo il codice della documentazione e sostituiamo opportunamenteil nome MyParcelable con il nome della classe codicePrezzo dovunque siarichiesto. Le variabili di istanza saranno:

1 pub l i c S t r ing nome ;pub l i c i n t cod i c e ;

3 pub l i c i n t prezzo ;pub l i c i n t quant i ta ;

5 pub l i c S t r ing d e s c r i z i o n e ;

Queste sono le variabili che dobbiamo }salvare~. I metodi da implementareper far sì che questi dati vengano inviati e ricevuti sono rispettivamente wri-teToParcel e il costruttore private codicePrezzo(Parcel in). Vediamocome sono implementati:

1 pub l i c void wr iteToParce l ( Parce l out , i n t f l a g s ) {out . w r i t eS t r i ng (nome) ;

3 out . w r i t e In t ( cod i c e ) ;out . w r i t e In t ( prezzo ) ;

5 out . w r i t e In t ( quant i ta ) ;out . w r i t eS t r i ng ( d e s c r i z i o n e ) ;

7 }p r i va t e cod icePrezzo ( Parce l in ) {

9 nome= in . r eadSt r ing ( ) ;cod i c e= in . r eadInt ( ) ;

11 prezzo=in . readInt ( ) ;quant i ta=in . readInt ( ) ;

86 CAPITOLO 5. ACQUISTO BIGLIETTI

13 d e s c r i z i o n e=in . r eadSt r ing ( ) ;}

L'oggetto out di tipo Parcel ci consente di salvare tutti i tipi di dato standarddi Java e qualsiasi oggetto di tipo Parcelable. Notiamo inoltre che il metodowrite(ogg) accetta come parametro soltanto l'oggetto da salvare (e nonchiave valore come abbiamo visto �n ora), quindi è importante l'ordine in cui imetodi vanno invocati. Come si nota nel listato, la sequenza con cui vengonosalvati i dati nella variabile out è la stessa di quella usata per prelevarli dallavariabile in.

Dall'oggetto codicePrezzo è facile ottenere un oggetto di tipo vista<codicePrezzo>utilizzando il seguente metodo:

pub l i c v i s ta<codicePrezzo> getVi s ta ( ) {2 re turn new v i s ta<codicePrezzo >(nome , d e s c r i z i on e , th i s ,

nu l l , f a l s e , quant i ta ) ;}

Grazie a questo metodo possiamo velocemente creare un layout a videoper permettere all'utente di e�ettuare la scelta multipla. Quando creeremola nostra selectableListaView dovremo invocare il secondo costruttore delmetodo (vedi listato di 5.3.3) in cui al posto di creare array di oggettoVistadal db verranno creati oggettoRiepilogo.

oggettoRiepilogo oggettoRiepilogo estende la classe oggettoVista. Que-sto, come si può notare dalla �gura 5.4, oltre allo spazio per il testo ha ancheun contatore e una descrizione sotto al nome.N.B. vedremo in seguito che questo layout ci sarà utile non solo come selezionemultipla, ma anche nel riepilogo.

Ora che abbiamo creato la lista, dobbiamo dare la possibilità all'utentedi selezionare uno o più biglietti dalla lista. Quello che vogliamo ottenere èuna schermata come quella illustrata sotto al numero 2 in �gura 5.1. Quindioltre alla lista centrale dovremo predisporre anche i tasti + e -. Per sceglieredi comprare un certo numero di biglietti, l'utente dovrà toccare sul nomedel biglietto che vuole acquistare, poi toccare il tasto +. A questo punto ilnumero scritto a destra (inizialmente zero) dovrà crescere al pari delle voltein cui viene schiacciato il tasto + (e diminuire se l'utente tocca il tasto -). Perfar si che ciò accada dobbiamo implementare in questo modo i tasti azionedel pulsante:

5.3. REALIZZAZIONE 87

1 pub l i c void onPiu (View view ) {LinkedList<ogget toL i s ta<codicePrezzo>> sce =

s e l . g e tSe l e c t edOgge t toL i s t a ( ) ;3 i f ( s c e . isEmpty ( ) ) {

Toast . makeText ( getAppl i cat ionContext ( ) , "Devis e l e z i o n a r e un t ipo d i b i g l i e t t o per aumentarlo " ,Toast .LENGTH_SHORT) ;

5 re turn ;}

7 ogget toL i s ta<codicePrezzo> a= sce . remove ( ) ;a . v i s t a . quant i ta++;

9 a . v i s t a . ogg . quant i ta=a . v i s t a . quant i ta ;a . agg io rnaVis ta ( ) ;

11 }pub l i c void onMeno(View view ) {

13 LinkedList<ogget toL i s ta<codicePrezzo>>sce=s e l . g e tSe l e c t edOgge t toL i s t a ( ) ;

i f ( s c e . isEmpty ( ) ) {15 Toast . makeText ( getAppl i cat ionContext ( ) , "Devi

s e l e z i o n a r e un t ipo d i b i g l i e t t o per aumentarlo " ,Toast .LENGTH_SHORT) ;

re turn ;17 }

ogget toL i s ta<codicePrezzo> a= sce . remove ( ) ;19 a . v i s t a . quant i ta=Math .max(0 , a . v i s t a . quantita −1) ;

a . v i s t a . ogg . quant i ta=a . v i s t a . quant i ta ;21 a . agg io rnaVis ta ( ) ;

}

In breve ci facciamo dare l'elemento selezionato tramite il metodo getSelecte-dOggettoLista(), veri�chiamo che e�ettivamente almeno uno sia selezionatoe, in�ne, aggiorniamo la variabile quantita. Nel codice vediamo che vieneaggiornata non solo quella dell'oggettoVista, ma anche quella dell'oggetto co-dicePrezzo contenuto nella variabile ogg. Questo perché quando andremoa comunicare le scelte e�ettuate in questa schermata all'activity successiva,potremo inviare soltanto oggetti di tipo parcelable. Inoltre ricordiamo cheogni volta che cambia l'orientamento del telefono bisogna esplicitamente sal-vare lo stato della schermata, e anche in questo caso andremo a salvare unarray di codicePrezzo.

Alla pressione del tasto {continua| non si dovrà fare altro che inviareun array (o un ArrayList) di codicePrezzo contenente le scelte dell'utenteall'activity successiva.

88 CAPITOLO 5. ACQUISTO BIGLIETTI

Layout Abbonamento Questa schermata non è particolarmente com-plicata, non prevede selezioni elaborate quindi le view standard di androidsono più che su�cienti. Nella �gura 5.1 sotto il 3 infatti, notiamo che ciservono 2 selettori a tendina (Spinner) per il tipo di abbonamento e il ti-po di sconto che si preferisce utilizzare, un selettore di data (datePicker)per determinare la data di inizio dell'abbonamento, e un campo di testo(EditText) per inserire il numero di tessera dell'abbonato.

Per gestire gli sconti utilizzerò comunque l'oggetto codicePrezzo salvandonella variabile di istanza prezzo la percentuale di sconto.

5.3.6 Layout Riepilogo

La schermata di riepilogo degli acquisti consente all'utente di visualizzarecosa sta per acquistare e il prezzo totale. Il layout lo si può vedere nella �gura5.1. Notiamo la forte somiglianza con la schermata di Biglietto Multiplo. Perrealizzare la gra�ca utilizziamo gli stessi metodi già visti in precedenza conl'unica di�erenza che non saranno selezionabili; quindi per creare la listauseremo la classe listaView vista in 5.3.3. Per calcolare il prezzo totale nondovremo fare altro che sommare i prezzi dei vari biglietti moltiplicati perle rispettive quantità- nel caso in cui sia stato selezionato un tipo di scontomodi�care in proporzione il risultato ottenuto- e in�ne mostrare il totale infondo alla pagina.

La schermata è provvista di un tasto continua alla posizionato in fon-do. Ricordiamo che se l'importo è superiore ad una certa quantità si dovràrichiedere un codice di controllo.

Richiesta codice di controllo Come notiamo subito dalla �gura 5.1(ultima foto in basso) notiamo che questo non è un layout normale, infattinon copre l'intero schermo. Quindi non creeremo una nuova Activity bensìun Dialog 5. Per crearlo è necessario innanzitutto un layout in xml in cuimetteremo un textView, un editText e un Button. Possiamo ora crearel'oggetto Dialog in questo modo:

pr i va t e Dialog c r eaR i ch i e s taCod i c e ( ) {2 Dialog r i c= new Dialog ( t h i s ) ;

r i c . s e tT i t l e ( " R i ch i e s t a Codice " ) ;4 r i c . setContentView (R. layout . r i c h i e s t a_cod i c e ) ;

r e turn r i c ;6 }

5la documentazione completa di Dialog è disponibile nella bibliogra�a alla voce[Documentazione di Dialog | Android developer]

5.3. REALIZZAZIONE 89

A questo punto non dobbiamo fare altro che attribuire un'azione al Buttondel layout che abbiamo inserito e gestire il fatto che l'utente possa premereil tasto back :

pr i va t e void r i c h i e d iCod i c e ( f i n a l OnCodeReceived l i s ) {2 f i n a l Dia log d=creaR ich i e s taCod i c e ( ) ;

( ( Button )d . findViewById (R. id . bottoneCodice) ) . s e tOnCl i ckL i s t ene r ( new OnCl ickListener ( ) {

4

@Override6 pub l i c void onCl ick (View v ) {

long code=Long . parseLong ( ( ( EditText)d . findViewById (R. id . cod i c e ) ) . getText ( ) . t oS t r i ng ( ) ) ;

8 l i s . onCodeReceived ( code ) ;d . se tOnDismis sL i s tener ( nu l l ) ;

10 d . d i sm i s s ( ) ;}

12 }) ;d . se tOnDismis sL i s tener (new OnDismissListener ( ) {

14 pub l i c void onDismiss ( D i a l o g I n t e r f a c e d i a l o g ) {l i s . onDismiss ( ) ;

16 }}) ;

18 d . show ( ) ;}

Le righe di codice all'interno del metodo onDismiss(DialogInterface dia-log) vengono eseguite se l'utente preme il tasto back quando il Dialog èmostrato a video.

Nel caso in cui l'utente tocchi il tasto continua, invece, viene eseguito ilmetodo onClick. Vediamo che all'interno del metodo viene prelevato il codiceall'interno dell' editText e lo viene salvato nella variabile code, viene chiamatoil metodo onCodeReceived (che spiegheremo in seguito), viene rimosso ilDismissListener dal Dialog, e in�ne viene chiusa la schermata.

La variabile lis è di tipo OnCodeReceived. Il quale è una interfaccia,creata per l'occasione composta da 2 metodi:

1 i n t e r f a c e OnCodeReceived{pub l i c void onCodeReceived ( long code ) ;

3 pub l i c void onDismiss ( ) ;}

90 CAPITOLO 5. ACQUISTO BIGLIETTI

L'interfaccia viene così implementata:

i f ( costoTota le>Const . costoMassimo ) {2 r i c h i e d iCod i c e (new OnCodeReceived ( ) {

pub l i c void onDismiss ( ) {4 ( ( Button ) view ) . setEnabled ( t rue ) ;

r e turn ;6

}8 @Override

pub l i c void onCodeReceived ( long code ) {10 a . append ( "CODICE: "+code ) ;

conc lud i ( a ) ;12 }

}) ;14

}

Questo è il pezzo di codice che richiama il Dialog. Come si può notare holasciato l' if in modo da sottolineare che si deve richiedere il codice soltantoquando l'acquisto supera un costo massimo. Chiamo il metodo richiedi-Codice passandogli come parametro l' implementazione dell'interfaccia. Sel'utente preme il tasto back allora il bottone continua, che avevamo disabili-tato in precedenza, torna attivo. Se invece l'utente inserisce il codice verràeseguito all'interno del metodo onCodeReceived. Dato che non so ancoracon precisione se il pagamento avverrà via SMS o in altro modo, per il mo-mento la parte del pagamento è sostituita da un messaggio nel LogCat ed èfacilmente modi�cabile editando il metodo concludi(). L'invio di messaggioè stato comunque già trattato in 3.6.

5.3. REALIZZAZIONE 91

Figura 5.1: Albero Decisionale

92 CAPITOLO 5. ACQUISTO BIGLIETTI

Figura 5.2: CheckBox

Figura 5.3: Layout seleziona città

Figura 5.4: Oggetto Riepilogo

Capitolo 6

Pubblicazione dell'applicazione

Adesso che abbiamo completato l'applicazione possiamo pubblicarla su Goo-gle Play Store (il successore dell'Android Market). Per procedere alla pub-blicazione possiamo seguire la guida che Google ci mette a disposizione (di-sponibile nella bibliogra�a [Caricamento di applicazioni - Google Play]). Dicosa abbiamo bisogno?

• L'applicazione salvata in un �le con estensione .apk di dimensionemassima 50 MB.

• Essere registrati a Google

• Un paio di screenshots (almeno)

• Icona applicazione ad alta risoluzione

6.1 Creazione �le .apk

Il �le apk è un �le che contiene tutti i dati della nostra applicazione, compresiquelli nella cartella res. Senza saperlo, per provare la nostra applicazione,abbiamo sempre inviato al cellulare (reale o virtuale) un �le apk generatoautomaticamente, che, però, non è �rmato digitalmente (o meglio è �rmatocon chiave di debug). Google Play Store non accetterà mai un �le apk senza�rma digitale, quindi vediamo come crearlo �rmato. Con Eclipse generare il�le apk �rmato è semplice: clicchiamo col destro sulla nostra applicazione,e clicchiamo su esporta (vedi �gura 6.1). Dall' esplora risorse selezionare lacartella Android e quindi la voce Export Android Application, in�neclicchiamo su Next. Nella prossima schermata controllare che nel campoProject ci sia proprio l'applicazione che vogliamo esportare e andiamo avanti.

93

94 CAPITOLO 6. PUBBLICAZIONE DELL'APPLICAZIONE

Figura 6.1: Eclipse - Esporta

A questo punto dobbiamo creare un portachiavi dove salvare tutte lenostre �rme digitali (chiavi). Mettiamo il pallino su Create new keystore,scegliamo un percorso a nostro piacimento nel campo location, una passworddi almeno 6 caratteri, inseriamola in entrambi i campi e clicchiamo su Next.A questo punto compiliamo i campi della �rma digitale inserendo un alias,una password, la validità della chiave (minimo 25 anni) e il nostro nome ecognome. Gli altri campi sono facoltativi ma è bene compilarli. Finita questafase scegliamo il percorso dove vogliamo che il nostro �le apk venga gene-rato e clicchiamo su Finish. Abbiamo così ottenuto il nostro �le .apk �rmato.

Un paio di note Un'applicazione può essere sostituita da un'altra soltantose ha la stessa chiave, altrimenti saremmo costretti a disinstallare la predentee installare la nuova. Questa restrizione è ai �ni della sicurezza e serve aimpedire che l'utente venga tru�ato con applicazioni non autentiche.

6.2. INVIO DELL'APPLICAZIONE 95

Il �le generato è già in grado di essere installato su qualsiasi cellulareanche senza essere inviato su Google Play Store1.

6.2 Invio dell'applicazione

Prima di tutto bisogna collegarsi al sito https://play.google.com/apps/publish/signupdove inserire tutti i dati richiesti, accettare il contratto di licenza, pagare 25$(USD) e registrarsi a Google (se non si è già registrati). In�ne, seguendola guida, si inviano tutti i �le indicati al punto 6 e saremo in grado dipubblicare la nostra applicazione nel Market.

1A�nché sia possibile installare un �le apk �rmato su un cellulare senza passare daGoogle Play Store è necessario che l'utente abbia autorizzato il cellulare a installare leapplicazioni di origine sconosciuta.

96 CAPITOLO 6. PUBBLICAZIONE DELL'APPLICAZIONE

Capitolo 7

Conclusioni

Come si è potuto notare nel corso della tesi, i cellulari stanno diventandosempre più dei computer a tutti gli e�etti. Con i mezzi che Android ci mettea disposizione siamo in grado di creare applicazioni di ogni tipo. Da quandoAndroid, e più in generale lo smartphone, ha preso seriamente piede nelmercato, sempre più sviluppatori si sono cimentati nella creazione di App,incoraggiati da una documentazione ben fatta e un supporto su internetveramente ottimo.

Inoltre, come abbiamo visto in 3.2.1, si può unire alla facilità di Java,l'e�cienza del C/C++.

7.1 Uno sguardo al futuro

L'utilizzo degli smartphone è in rapido aumento sia in Italia che nel restodel mondo. Molte ditte (come ad esempio la Net-t by Telerete, sede dove hosvolto lo stage) stanno investendo molto in questa nuova tecnologia.

7.1.1 Cellulare VS PC

C'è da chiedersi perché cellulari e tablet stiano avendo una di�usione cosìampia dato che il computer può svolgere le stesse funzioni.

Il cellulare è sicuramente più portabile di qualsiasi PC. Molte delle appli-cazioni per smartphone, infatti, sono pensate per un utilizzo in movimento(vedi navigatori, orari dei treni ecc..).

In secondo luogo abbiamo la semplicità di utilizzo. A di�erenza del com-puter, il cellulare permette di fare le operazioni base in maniera semplice ede�cace, anche grazie al touchscreen. Toccare lo schermo, anziché utilizzarei tasti, lo rende molto più intuitivo oltre che più accattivante.

97

98 CAPITOLO 7. CONCLUSIONI

Altra nota importante sta nella facilità con cui è possibile installare erimuovere programmi. Sia in Android che in iOS1, infatti, si possono repe-rire facilmente grazie a un negozio online (Google play per Android e Appstore per iOS). Rimuoverli è altrettanto facile e sicuro. Basta trascinarel'applicazione nel cestino, oppure dalla lista programmi lanciare la rimozio-ne. A di�erenza del pc, la rimozione del cellulare non lascia tracce residueall'interno del cellulare (come operazioni di background, �le sparsi, librerieecc..)2.

Ad aumentare la facilità d'uso degli smartphone sarà l'assistente vocale.Ovvero un applicazione che consente all'utente di impartire ordini al cellularee ricevere informazioni, semplicemente conversando col cellulare, ampliandocosì sempre più il range di persone in grado di utilizzarlo correttamente.

Un cellulare, oltretutto, è, in media, meno costoso di un pc ed è dotatodi tutte le periferiche necessarie al suo funzionamento.

Ovviamente non potrà mai sostituire il PC, però l'utente medio non habisogno di potenza di calcolo elevata, tantomeno di programmi che faccianoqualcosa di complesso.

7.2 Potenzialità

Nel corso della tesi abbiamo visto come sia possibile ricevere informazionida videocamera, elaborarle a piacimento, e interagire con il mondo tramiteSMS e internet. Non abbiamo sfruttato gli strumenti di geo-localizzazione(GPS3), la bussola interna, gli accelerometri ecc...

Sfruttando adeguatamente tutti gli strumenti di cui sono dotati smart-phone, si possono creare applicazioni in grado di rivoluzionare il modo di fareinformazione. In futuro potrebbero sparire code agli sportelli della posta, al-le biglietterie dei cinema, agli sportelli automatici. Sempre meno personeavranno bisogno di chiedere indicazioni come raggiungere un determinatoluogo, passando sempre per la via meno tra�cata. Sarà sempre più facileed intuitivo creare ritrovi, condividere la propria posizione ecc... Il tutto conuna facilità estrema e sempre a portata di dito.

1iOS (iPhone Operative System) è il sistema operativo sviluppato da Apple per iPhone,iPod touch e iPad.

2In realtà i dati di con�gurazione di ogni applicazione rimangono salvati all'interno delcellulare anche dopo la rimozione, ma la quantità di memoria che occupano è veramentepiccola e non in�uisce sulle prestazioni di sistema

3Global Positioning System

Bibliogra�a

[Android, Guida per lo sviluppatore] Titolo: Android, Guida per losviluppatore. Autore: Massimo Carli, Casa Editrice: Apogeo.

[Parcelable - Android Developers] Documentazione internetdell'interfaccia Parcelable, Disponibile all'indirizzo:http://developer.android.com/reference/android/os/Parcelable.html

[Marakana - Using NDK to Call C code from Android Apps]Guida per usare l'NDK per chiamare funzioni cin applicazioni Android, disponibile all'indirizzo:http://marakana.com/forums/android/examples/49.html, 4

[Using Tesseract Tools for Android to Create a Basic OCR App] Usaretesseract tools per android per creare una semplice applica-zione OCR per Android. La guida è disponibile a questo link:http://rmtheis.wordpress.com/2011/08/06/using-tesseract-tools-for-android-to-create-a-basic-ocr-app/

[Log - Wikipedia] Descrizione di Log, disponibile all'indirizzo:http://it.wikipedia.org/wiki/Log

[Cat - Wikipedia] Descrizione di Cat, disponibile all'indirizzo:http://it.wikipedia.org/wiki/Cat_(Unix)

[Documentazione di Dialog | Android developer] La docu-mentazione di Dialog è disponibile all'indirizzo:http://developer.android.com/reference/android/app/Dialog.html

4Se non dovesse funzionare il programma della guida dovete modi�care la riga return(*env)->NewStringUTF(env, Hello World!); togliendo l'asterisco a env e scriverla in que-sto modo (env)->NewStringUTF(Hello World!); Se non riuscite a trovare javah doveteinstallarlo a parte

99

100 BIBLIOGRAFIA

[Caricamento di applicazioni - Google Play] La guida completa sucome inviare un applicazione a google è disponibile al-l'indirizzo http://support.google.com/googleplay/android-developer/bin/answer.py?hl=it&answer=113469

[Tesseract - Wikipedia] La descrizione completa di tesseract è disponibile aquesto indirizzo: http://it.wikipedia.org/wiki/Tesseract_

[Java Native Interface - Wikipedia] Java Native Interface FromWikipedia, the free encyclopedia. Link originale:http://en.wikipedia.org/wiki/Java_Native_Interface

[ZXing (Zebra Crossing)] Sito u�ciale di ZXing disponibile all'indirizzo:http://code.google.com/p/zxing/