Sei sulla pagina 1di 64

Impariamo a sviluppare

su piattaforma Android
applicazioni per smartphone,
tablet e dispositivi embedded
che ci permetteranno di
interfacciarci col mondo
CORSO

elettronico esterno
e con i nostri progetti.
Prima puntata.

An
di
dr
Ch
ea

po
iap
ri
1
PROGRAMMIAMO CON
ndroid nasce nel 2007 da Google come nel mondo Android e prende-
A un sistema operativo open-source per
dispositivi embedded e negli anni successivi
remo familiarità con gli strumenti forniti
gratuitamente dalla comunità di sviluppo di
ha ottenuto sempre enormi consensi, tali da questo nuovo sistema operativo per poi dare
renderlo attualmente il più valido concorren- vita alle nostre prime applicazioni su qualsiasi
te di iOS di Apple. smartphone o tablet e con le quali saremo in gra-
Ad oggi, sono moltissimi gli smartphone do di controllare remotamente i nostri progetti
ed i tablet sui quali è installato Android, ed elettronici, impartire comandi e ricevere da essi
ultimamente stanno nascendo anche molti utili informazioni che potranno essere visualiz-
sistemi di sviluppo in grado di supportare zate sul display del nostro dispositivo.
questo sistema operativo che, essendo open- La comunità di sviluppo Android è, infatti, sem-
source, permette un facile adattamento ed pre in continua evoluzione (come è facilmente
un’ampia modularità verso vari dispositivi riscontrabile visitando il sito http://developer.
hardware, diversi tra loro per funzionalità e android.com) e ci fornisce tutti gli strumenti e le
caratteristiche, pronti a soddisfare ogni tipo librerie di codice per prendere il pieno possesso
di esigenza. del nostro smartphone e di tutti i suoi sensori e
moduli interni. Facilmente il dispositivo, smar-
Breve introduzione al corso tphone o tablet, su cui svilupperemo la nostra
In questo corso a puntate, ci addentreremo applicazione, avrà un modulo bluetooth inter-

Elettronica In ~ Settembre 2012 103


CORSO
KIT DI SVILUPPO
PER DISPOSITIVI ANDROID
Android non è solo un mondo esterno e conferendo valore aggiunto
progetto software, ma ai nostri progetti elettronici. In particolare
coinvolge anche l’hardwa- realizzeremo, nelle puntate successive, una
re e lo dimostra una serie
scheda dotata di modulo bluetooth e di alcu-
di piattaforme di sviluppo
ni relé che piloteremo dal nostro smartphone
nate attorno a questo
sistema operativo open Android ed una con a bordo un modulo
source per sfruttare e Fig. 2 Wi-Fi per permettere scambio di dati tra due
controllare svariate perife- o più dispositivi.
riche. Un esempio ci viene Esistono poi, anche schede di sviluppo
fornito dalla scheda di realizzate da terze parti su cui troviamo
sviluppo IOIO Android (Fig. microcontrollori Microchip o Atmel che si
1) che presenta una porta interfacciano direttamente tramite cavetto
USB funzionante come
USB a qualsiasi dispositivo che potremo per-
host e a cui andrà colle-
visibile in Fig. 2. sonalizzare a nostro piacimento. Alcune di
gato il cavetto dati dello
Non poteva certo manca- queste demoboard sono presentate nel Box
smartphone; da Google
re una delle più recenti “Kit di sviluppo per dispositivi Android”.
Play (il market Android)
board della famiglia
Arduino che oltre all’in-
terfaccia USB Host per Programmare su Android
interfacciarsi con Android Per programmare con Android viene uti-
presenta, rispetto alle lizzato il linguaggio java. In realtà occorre
schede precedenti, un più fare prima una breve precisazione perchè il
potente ATmega2560 ed linguaggio usato non è esattamente lo stesso
una connetività maggiore java tradizionale che siamo abituati a cono-
verso l’esterno. scere. Normalmente dopo la compilazione di
Si chiama Mega ADK For
Fig. 1 un programma scritto in java viene generato
Android ed è compatibile
con i numerosi shield per
un byte code che viene eseguito da una JVM
sarà possibile scaricare (Java Virtual Machine). Possiamo pensare la
Arduino (Fig. 3).
applicazioni sul proprio JVM come una implementazione software
dispositivo per prenderne il
che permette al nostro codice java di collo-
pieno controllo o realizzar-
ne di proprie.
quiare col sistema operativo.
Anche Microchip si è affac- Su Android il byte-code generato dopo la
ciata sul mondo Android compilazione non è proprio lo stesso di
presentando un kit di quello generato da un compilatore java ed
sviluppo di questo tipo ba- il ‘motore’ a cui questo viene passato non è
sato sul microcontrollore Fig. 3 una JVM classica, ma una DVM ovvero Dal-
a 16 bit PIC24F, come vik Java Machine; ogni dispositivo Android
ha una propria DVM in grado di eseguire
questo particolare byte-code. Senza scendere
no, così come un GPS integrato, il Wi-Fi, un troppo nei dettagli, possiamo affermare che
accelerometro a tre assi, una fotocamera, la differenza tra le due macchine virtua-
oltre, ovviamente, al display e ad un pannel- li consiste nel fatto che la DVM è pensata
lo touch, senza dimenticare l’altoparlante, proprio per dispositivi embedded in cui la
un microfono e il supporto per una memoria quantità di memoria non potrà essere così
esterna micro SD di qualche GByte. Tutte ca- elevata come quella di un PC e quindi è otti-
ratteristiche che forse oggi possono apparire mizzata per migliorarne le prestazioni.
scontate ai più, ma che non troppo tempo Di sicuro, a livello di programmazione, chi
fa, si sarebbero potute trovare insieme solo già conosce java non avrà problemi ad entra-
in una scheda di sviluppo professionale di re nell’ottica Android , chi invece preferisse
fascia alta. Con Android ed una base di pro- comunque programmare in C o C++ potrà
grammazione avremo la possibilità di mette- essere soddisfatto ugualmente perchè è
re insieme e pilotare questi moduli a nostro disponibile un tool di sviluppo (sempre tra-
piacimento per realizzare interfacce verso il mite developer.android.com) chiamato NDK

104 Settembre 2012 ~ Elettronica In


ANDROID, VERSIONI
E NOMI IN CODICI
(Native Development Tool) che permetterà
di scrivere le nostre applicazioni in codice
nativo aumentando così prestazioni, velocità
e facilitando anche la riusabilità del proprio
codice C o C++.

Platform SDK Android


CORSO

Ora possiamo incominciare ad installare Fig. 4


sul nostro computer (PC, Mac o macchina
Linux) tutto il necessario per sviluppare su
piattaforma Android. Ad ogni versione del passaggio da Eclair a Froyo,
I passi da seguire sono molto semplici, sistema operativo Android, in cui abbiamo possibilità
prima di tutto occorrerà installare (per chi rilasciata da Google, viene di trasformare il dispositivo
associato un nome in co- Android (connesso in rete)
non l’avesse già fatto) il run-time java (JDK)
dice sulla base di prodotti in un hotspot Wi-Fi (questa
essenziale per la compilazione dei progetti. dolciari. In contemporanea, proprietà è chiamata te-
Successivamente dovremo installare la tool- al Googleplex, il quartier thering) ed il trasferimento
chain di sviluppo per Android, chiamata Pla- generale di Google (in Cali- tramite modulo bluetooth
tform SDK, dal seguente sito http://developer. fornia), viene realizzata una dei dati e non solo della
android.com/sdk. scultura che lo rappresenta voce come nelle versioni
Durante l’installazione potremo scegliere (Fig. 4). Come si può notare precedenti. Quest’ultima
quali pacchetti installare tra quelli disponi- dalla Tabella 1, le iniziali caratteristica è utilissima
bili in rete. Esistono numerose versioni di del nome di ogni versione per i nostri scopi qualora
sono in ordine alfabetico. volessimo usare il nostro
Platform SDK corrispondenti alle versioni di
La versione 2.1 (Eclair) ha dispositivo Android per con-
Android disponibili sul mercato; è possibile mantenuto lo stesso nome trollare a distanza i nostri
anche sceglierle tutte, ma almeno agli inizi e per via di alcuni problemi progetti, come vedremo in
per non creare troppa confusione consiglia- della 2.0 che rendevano il una delle prossime puntate.
mo di concentrarsi solo su una, scegliendola sistema instabile. È comunque possibile ag-
in base alla versione presente nel sistema Le versioni HoneyComb giornare il proprio sistema
(smartphone o tablet) a nostra disposizione e sono state pensate apposi- Android ad una versione
sul quale svilupperemo in futuro. tamente per i tablet, men- successiva rispetto a quella
Per avere un’ idea generale sulle versioni di tre le versioni IceCream, preinstallata, consultando
uscite di recente solo su il sito del produttore del
Android e dei loro nomi in codice, rimandia-
alcuni dispositivi di fascia vostro dispositivo.
mo al Box “Android, versioni e nomi in co- alta, hanno un’ interfaccia Essendo Android open-
dice”, per adesso basti sapere che le versioni unificata sia per smartpho- source, i più smanettoni
1.5 e 1.6 sono ormai obsolete, le versioni ne sia per tablet. potranno anche modificare
dalla 2.1 alla 2.3 nascono principalmente per Il più grande salto di ed installare altri sistemi
dispositivi smartphone mentre le versioni versione in termini di operativi alternativi, ma
3.0, 3.1 e 3.2 sono dedicate ai tablet. Infine le miglioramento di presta- prendendosi i rischi di avere
più nuove versioni 4.0 possono funzionare zioni e migliorie è stato nel un dispositivo instabile.
indipendentemente sia su tablet sia su smar-
Tabella 1
tphone pur scrivendo gli stessi ‘frammenti’
VERSIONI ANDROID
di codice, oltre ad avere funzionalità in più
come ad esempio il supporto per il Wi-Fi Numero versione Nome in codice API Level
Connect ovvero la possibilità di connettere 1.0 Apple Pie 1
1.1 Banana Bread - 2
direttamente due dispositivi Wi-Fi senza
1.5 Cupcake - 3
ricorrere ad un router centrale.
1.6 Donut 4
Questo non vuol dire, però, che un’ applica-
2.0.x Eclair 5, 6
zione sviluppata usando le librerie 2.1 non
2.1.x Eclair 7
possa “girare” su uno smarthpone su cui 2.2.x Fro Yo (Frozen Yogurt) 8
è installato un Android 2.3, ma anzi potrà 2.3.x Gingerbread 9, 10
funzionare correttamente anche su un tablet, 3.x Honeycomb 11, 12, 13
ovviamente non sfrutterà le funzionalità in 4.x Ice_Cream_Sandwich 14, 15

Elettronica In ~ Settembre 2012 105


CORSO
COMPILARE DA LINEA DI COMANDO
È possibile compilare un progetto direttamente da linea di comando grazie a due semplici script.
Per prima cosa occorre creare il progetto con tutte le sue directory necessarie lanciando il seguente comando di
esempio:

Android.bat create project --target 1 --name AndroidApplication –path ./myProject --activity AndroidActivity --package
com.packagename.android.
Di seguito una breve descrizione dei parametri inseriti:
--target specifica la versione delle librerie usate; ad esempio per le librerie API 8 (Android 2.2) il valore
deve essere 1.
--name indica il nome del progetto
--path indica il percorso della directory in cui verrà creato il progetto con le sue sotto-directory.
--activity specifica il nome della classe principale che verrà creata in automatico
--package deve essere un nome univoco nel caso si voglia pubblicare l’applicazione sul market Google Play
e normalmente per questo motivo si usa un indirizzo web al contrario, ad esempio
com.packagename.android

A questo punto non ci resta che lanciare il secondo comando:

ant debug o ant release

e seguire a video i passi della compilazione. Al termine di essa, nella sotto cartella bin troveremo il risultato finale,
ovvero un file con estensione .apk e vedremo in seguito come poterlo testare direttamente sul dispositivo finale o sul
proprio pc, tramite emulatore.
Per comodità consigliamo di aggiungere alla variabile di ambiente PATH di windows la directory in cui abbiamo
installato i tool di Android in modo da poter eseguire i comandi da ogni posizione.

più offerte dalle versioni successive. Per gioco durante la compilazione, ma se voles-
questo motivo e per il fatto che attualmente simo avere a disposizione un pratico am-
nella grande maggioranza degli smartphone biente di sviluppo integrato che ci permetta
sono installate le versioni 2.2 o 2.3, consi- di editare i sorgenti, compilare e debuggare
gliamo di scegliere come libreria di svilup- su un emulatore o direttamente sul dispo-
po SDK la 2.2, ed eventualmente installare sitivo fisico in tempo reale, controllando il
successivamente versioni più recenti. flusso del programma, possiamo ricorrere
Tra i vari pacchetti installabili sono presenti ad Eclipse, l’IDE di sviluppo più usato
anche alcune librerie di Google che possono per programmare dispositivi embedded e
essere usate se si desidera impiegare nelle creato in Java. Esistono diverse versioni di
proprie applicazioni la funzionalità di navi- Eclipse scaricabili gratuitamente a questo
gazione nelle mappe geografiche sfruttando indirizzo http://www.eclipse.org/downloads,
il motore Google Maps. Al termine dell’
installazione avremo a disposizione i tool
necessari per compilare la prima nostra
applicazione, anche se per ora solo da linea
di comando. Infatti, grazie ad una serie di
comandi presenti nella sotto directory tools
che ora troveremo nel percorso in cui ab-
biamo precedentemente scelto di installare
SDK-Android, possiamo creare un progetto
minimale e successivamente compilarlo.
Maggiori dettagli al riguardo li potete trova-
re nel Box “Compilare da linea di comando”.
,
ECLIPSE, l ambiente di sviluppo grafico
Compilare da riga di commando è utile per
capire i vari passaggi ed i file che entrano in
Fig. 5

106 Settembre 2012 ~ Elettronica In


CORSO

Fig. 6 Fig. 7

ma per i nostri scopi è sufficiente anche una nel sistema e configurarlo opportunamente.
versione base, come, per esempio, Eclipse Questo plugin si chiama ADT Plugin ed è
Galileo. scaricabile dal sito di Android al seguente
Sfortunatamente Eclipse non è in grado di indirizzo http://developer.android.com/sdk/
compilare nativamente sorgenti per An- eclipse-adt.html, oppure più comodamente
droid, ma è possibile scaricare un plugin on-line all’interno dell’Ide, cliccando sulla
gratuito, che permetterà di riconoscere la voce di menu Help e poi cliccando su Install
Tool-Chain di sviluppo Android installata New Software e riempiendo i campi con l’in-

Fig. 8

Elettronica In ~ Settembre 2012 107


CORSO
Fig. 9

dirizzo opportuno come indicato in Fig. 5. se la build è andata a buon fine e nel caso
Al termine dell’installazione dovremo apri- contrario, ci darà indicazioni aggiuntive
re la finestra di dialogo Preferences accessi- sugli errori ed eventuali warning.
bile dalla voce di menu Window ed indicare
,
nel campo Android in quale directory risiede L emulatore Android
il Platform SDK; otterremo un elenco delle Rimandando maggiori dettagli di Eclipse
librerie installate simile a quello in Fig. 6. alle puntate successive possiamo concludere
A questo punto possiamo creare un nuovo analizzando un altro importante strumento
progetto Android come in Fig. 7 e seguire i che ci viene offerto dal pacchetto SDK Android
vari passi del wizard (che come potrete no- appena installato, e cioè l’emulatore. Questo
tare richiederanno l’inserimento degli stessi permetterà di testare l’applicazione e debug-
parametri visti nel caso di creazione da linea garla sul PC senza dover disporre necessaria-
di comando) prima di eseguire la prima mente di un dispositivo fisico e accelerando,
build cliccando col tasto destro sul nome così, i tempi di produzione.
del progetto nella vista PackageExplorer sulla Inoltre, offre un vantaggio non trascurabile su
parte sinistra dell’ Ide e cliccando infine su applicazioni Android destinate a funzionare su
Build Project. dispositivi dotati di caratteristiche hardware
Non ci soffermeremo in questa prima pun- diverse tra di loro, basti pensare alla risoluzio-
tata nel descrivere tutte le funzionalità di ne e alle dimensioni degli schermi di differenti
Eclipse, ma come primo passo sarà sufficien- marche e modelli.
te tenere d’occhio la “vista” Problems, nella Se infatti, avessimo intenzione di pubblicare
parte bassa dell’Ide che ci darà informazioni una nostra applicazione sul market Google

108 Settembre 2012 ~ Elettronica In


Play (il market store Android visitabile a alcune macchine piuttosto lente, potrebbero
https://play.google.com/), questa dovrà esserci ancora problemi di questo tipo per
essere in grado di funzionare ed adattarsi cui consigliamo di installare un emulatore
su qualunque dispositivo e non potendo di terze parti che è molto usato e soprattut-
averli tutti a disposizione possiamo crearli to molto più veloce. Si chiama YouWave
a nostro piacimento sull’emulatore e testarli Android, è scaricabile da questo sito http://
separatamente. Questo è possibile tramite youwave.com e, anche se non è gratuito, non
CORSO

l’eseguibile AVD Manager (Android Virtual ha un costo proibitivo ed esiste la possibilità


Device) presente nella cartella installata (o di provarlo per un tempo limitato di sette
direttamente da Eclipse) e visibile in Fig. 8. giorni (Fig. 10). Copiando l’applicazione
Dalla finestra principale possiamo creare generata da Eclipse, (un file .apk che avremo
nuovi dispositivi virtuali, ognuno con una modo di analizzare in seguito) in una speci-
diversa risoluzione ed associare loro una fica cartella di YouWave saremo in grado di
specifica versione delle librerie SDK (API) installarla e provarla su questo emulatore in
scelte tra quelle installate in precedenza tempi veramenti brevi.
(nel nostro caso Android2.2, API Level 8).
Una volta creati quelli desiderati possiamo Conclusioni
sceglierne uno ed avviarlo direttamente da Ora abbiamo tutti gli strumenti per svi-
questa finestra e dopo pochi minuti potre- luppare su Android, ma per il momento ci
mo navigare all’interno del nostro nuovo di- fermiamo qui, permettendovi di testare il
spositivo usando il mouse come se fosse un nuovo ambiente e rimandando alla prossima
touch e lanciando le varie applicazioni di si- puntata i primi esempi di codice java e le
stema come su un vero smarphone Android prime prove sul dispositivo fisico. g
(Fig. 9). Questo si rivela molto utile anche
per prendere un po’ di familiarità e dimesti- Fig. 10
chezza col nuovo mondo Android, perchè,
al di là della nostra applicazione, possiamo
navigare nelle cartelle e nelle applicazioni di
sistema come se fosse uno smarthone reale.
Ovviamente l’emulatore potrà essere esegui-
to direttamente da ambiente Eclipse in se-
guito ad una nuova compilazione cliccando
col tasto destro del mouse sul nome e
successivamente su Run As – Android Appli-
cation. Seguirà una finestra in cui potremo
scegliere quale dispositivo vogliamo emula-
re tra quelli creati in precedenza e la nostra
applicazione verrà eseguita direttamente
sull’emulatore. Da notare come non sia il
caso di chiudere la finestra dell’emulato-
re al termine delle nostre prove, ma anzi,
conviene tenerla sempre aperta in modo da
ridurre i tempi di caricamento; al successivo
conseguimento della build, il sistema troverà
l’emulatore già attivato e l’applicazione
verrà caricata più rapidamente per essere
eseguita e testata nuovamente.
Con le ultime versioni del Platform SDK An-
droid, sono stati fissati alcuni bug che rende-
vano i tempi di caricamento del dispositivo
virtuale veramente lunghi e tali da renderne
impraticabile il test ed il debug. Tuttavia, su

Elettronica In ~ Settembre 2012 109


Riprendiamo il
corso di Android
soffermandoci
sull’ambiente di
sviluppo Eclipse per
emulare la nostra
CORSO

prima applicazione
ed eseguirla su un
dispositivo reale.
Seconda Puntata.

An
di
dr
Ch
ea

po
iap
ri
2
PROGRAMMIAMO CON
opo l’introduzione al mondo Android mente Open Source, è sviluppato in Java; es-
D della puntata precedente siamo pronti
a programmare la nostra prima applicazione
sendo un ambiente multi-linguaggio è possibile
sviluppare applicazioni programmando anche
sfruttando l’ambiente di sviluppo Eclipse in C/C++, a patto di installare i plugin necessari.
presentato in precedenza ed analizzando la È infatti bene ricordare che Eclipse, da solo, ci
struttura di un progetto nei suoi dettagli. consente di sviluppare programmi in Java, ma
Saremo in grado di testare il nostro prodot- non basta a creare un progetto Android. Nel
to sull’emulatore fornito dal plugin ADT di nostro caso programmeremo in Java, ma avremo
Android e finalmente di vederlo in azione comunque bisogno del plugin ADT (Android
sul nostro dispositivo. Sarà ancora un pro- Developers Tools) già installato nella puntata
getto senza particolari funzionalità pratiche e precedente, per permettere ad Eclipse di creare
privo di interfaccia grafica, ma ci permetterà un nostro progetto e di fornirci strumenti spe-
di comprendere il processo completo per cifici come l’emulatore, i driver, le librerie e un
programmare, testare e pubblicare una nostra insieme di esempi.
futura applicazione sul market Android. Esistono altri ambienti di sviluppo per pro-
grammare con Android (come ad esempio
Eclipse IntellijIDEA e NetBeans per citarne alcuni) , ma
Eclipse è un ambiente di sviluppo integrato abbiamo scelto di utilizzare Eclipse perchè è il
(IDE) nato da IBM e diventato successiva- più usato in ambito di applicazioni embedded;

Elettronica In ~ Ottobre 2012 115


CORSO
Fig. 1

molti dei lettori lo avranno già utilizzato e Struttura del progetto


chi si ritroverà ad utilizzarlo anche per altre Prima di iniziare a modificare ed aggiungere
piattaforme che non siano Android, avrà più il nostro codice è bene dare uno sguardo alla
dimestichezza nel navigare al suo interno. struttura del progetto, che risulta organizza-
D’altro canto questo non vuole essere un cor- to in tante sotto-directory.
so specializzato su Eclipse, che inizialmente Queste sono visualizzate nella vista (o view)
potrà apparire un pò ostico, per la presenza a sinistra dello schermo e rispecchiano le
di molte funzioni che non analizzeremo in directory del file system che potrete trovare
queste pagine, ma una base dalla quale par- navigando con Esplora Risorse nella cartella
tire per avvicinarci alla programmazione di del progetto appena creata.
sistemi Android. Possiamo vedere come si presenta in Fig. 3
A questo punto possiamo iniziare. analizzandole brevemente qui di seguito:
La directory src contiene i file sorgenti con
Il nostro primo progetto estensione .java. Al momento della creazione
Una volta aperto Eclipse possiamo creare
il nostro primo progetto scegliendo File-
NewProject dal menu, cliccando su Android
Project si aprirà una finestra come in Fig. 1,
da dove inizierà la procedura guidata nella
quale inseriremo i parametri già visti nella
puntata precedente.
Ricordiamoci di scegliere, nella schermata
di Fig. 2, le librerie opportune in base alla
versione Android installata sul dispositivo
finale; nel nostro esempio abbiamo scelto
le 2.2 perchè utilizzeremo uno smartphone
con Android Froyo (ovviamente funzionerà
anche su versioni successive).
Terminata questa fase Eclipse avrà creato per
noi lo scheletro di un progetto che visualiz-
zerà il classico “Hello Word” sullo schermo
in modalità console. Fig. 2

116 Ottobre 2012 ~ Elettronica In


Fig. 3 Fig. 4
CORSO

del progetto conterrà un solo file con l’unica to per i più curiosi, perchè fornisce informa-
classe principale dell’applicazione. zioni su come prendere il controllo del nostro
Ogni file con estensione .java presente all’in- dispositivo e può suggerirci nuovi spunti per
terno di questa cartella verrà compilato all’ le nostre future applicazioni.
esecuzione del comando build, per cui se La directory bin contiene i file binari che
volessimo aggiungere le nostre classi basterà vengono utilizzati dall’applicazione, nonchè
creare un file all’interno della stessa cartella l’applicazione stessa in formato nativo (.dex)
ed aggiornare la vista premendo F5 o col co- ed il prodotto finale con estensione .apk. Ri-
mando Refresh. Questa cartella conterrà il co- cordiamo che il risultato ultimo della nostra
dice del progetto ma, come vedremo ora, da compilazione sarà un file .apk che è un file
sola non basta per generare il prodotto finale. compresso contenente al suo interno le risor-
La directory gen conterrà invece file autoge- se dell’applicazione, l’eseguibile nativo per
nerati dal progetto durante la compilazione; Android (.dex) e qualche altro file che sarà
questi contengono riferimenti alle risorse del possibile vedere rinominando il file apk con
progetto come ad esempio bottoni, immagini, estensione .zip. Il file con estensione .apk sarà
file audio e stringhe, ma dal momento che il solo che andremo ad uploadare sul market
non dobbiamo modificarli, possiamo tempo- android quando avremo terminato il nostro
raneamente trascurarla. progetto.
La directory Android 2.2 è in realtà un colle- La directory res contiene le risorse utilizzate
gamento ad un file precompilato .jar che non dal nostro progetto; questa cartella è a sua
si trova all’interno della cartella del progetto, volta suddivisa in altre sotto-directory in
ma nelle cartelle create al momento dell’in- base al tipo della risorsa specifica. È bene
stallazione del plugin ADT e contiene tutte analizzare meglio le cartelle ed i file contenu-
le librerie che ci permettono di sviluppare ti in questa directory, perchè assumono un
con Android. aspetto considerevole per quanto riguarda
Eclipse ci permette di visualizzarle tutte ed l’aspetto, lo stile e l’interfaccia grafica della
elencare anche le funzioni che ognuna di esse nostra applicazione e che quindi non va tra-
presenta. Questa caratteristica, anche se non lasciata perchè sarà il primo diretto contatto
è indispensabile, può tornare utile soprattut- con l’utente finale. Analizziamo perciò, di

Elettronica In ~ Ottobre 2012 117


CORSO
Fig. 5 Fig. 6
seguito le sotto-directory della cartella res. cliccando sul tab posizionato in basso alla
La sotto-directory values presenta al suo finestra con scritto il nome del file, come in
interno un file .xml strings.xml, che contiene Fig. 5.
le stringhe utilizzate dalla nostra applicazio- Nulla ci vieta, anzi la maggior parte delle
ne. Ad esempio nel progetto appena creato volte è anche estremamente consigliabile,
questo file contiene la stringa ”Hello World”, modificare l’aspetto della nostra applicazione
noi potremmo naturalmente aggiungere altre inserendo controlli e settando le loro proprie-
stringhe e vedremo in seguito in che modo tà (colore, posizione, stile..) scrivendo diret-
fare riferimento ad esse per poterle usare ri- tamente nel file xml. Questo presuppone una
chiamandole dal codice dell’applicazione. La maggiore conoscenza delle librerie Android,
directory values potrebbe contenere anche ma di sicuro permette un maggior controllo e
più di un file di stringhe, possiamo infatti chiarezza quando dovremo modificare o ag-
pensare ad una applicazione che supporti giungere altri elementi. Rimane comunque il
più linguaggi per le diverse nazionalità. vantaggio che dopo aver scritto manualmen-
La sotto-directory layout contiene file .xml te il file .xml potremmo vederne il risultato
che rappresentano l’interfaccia grafica della tornando in modalità grafica, cliccando sul
nostra applicazione. Al momento ne vedia- tab Graphical Layout. Prossimamente vedre-
mo solo uno, (main.xml) ed è molto scarno, mo più in dettaglio questo aspetto.
ma in seguito la completeremo a seconda Le sotto-directory drawable-hdpi, drawable-
delle nostre esigenze aggiungendo i bottoni mdpi, drawable-ldpi contengono le immagini
ed i controlli che più ci torneranno utili. Se utilizzate dall’applicazione e sono più di una
alla nostra applicazione servisse anche una per permettere l’adattamento a dispositivi
finestra di dialogo, piuttosto che una finestra con diverse dimensioni dello schermo. Al
per settare le opzioni, o ancora un menu, momento della creazione del progetto queste
basterà aggiungere altri file .xml, ognuno con cartelle conterranno già la classica icona An-
la propria veste grafica che richiameremo droid che rappresenterà la nostra applicazio-
direttamente dal codice come vedremo più ne. In ognuna di queste cartelle troveremo un
avanti. Cliccando su questi file si aprirà sulla file immagine in formato png, chiamato con
vista principale l’aspetto della nostra form lo stesso nome, ma con risoluzione diversa
(visibile in Fig. 4) in cui potremmo aggiun- in base alla densità dello schermo. In partico-
gere tutti i controlli possibili (presenti sulla lare nella cartella drawable-hdpi troveremo
sinistra) semplicemente trascinandoli sopra. l’immagine dell’icona maggiormente definita
Eclipse, in base alle nostre azioni, genere- e pensata per schermi ad alta risoluzione
rà automaticamente il file xml (nel nostro (solitamente 480x640), nella cartella drawa-
esempio main.xml) che potremo visualizzare ble-mdpi avremo la stessa icona, ma meno

118 Ottobre 2012 ~ Elettronica In


Fig. 7

definita, per schermi di media risoluzione


(320x480) e nella cartella drawable-ldp l’ico-
na sarà per schermi a bassa risoluzione come
negli smartphone più piccoli (240x320).
Ovviamente quando andremo ad inserire le
nostre eventuali risorse grafiche dovremo
preoccuparci di generare la stessa immagi-
CORSO

ne, ma con risoluzione diversa ed inserirla


nella corrispondente cartella. La risoluzione
deve rispettare certi canoni che non svilup-
peremo in questa sede, ma che si trovano
dettagliatamente spiegati in un documento
fornito dalla comunità di Android al seguen-
te indirizzo: http://developer.android.com/guide/
practices/screens_support.html
La cartella assets può contenere dati e risorse do al codice automaticamente generato da
di ogni genere, come immagini, audio, testo, Eclipse e prendere confidenza con i comandi
file binari; solitamente non è molto usata, basilari dell’ interfaccia Eclipse.
ma esiste una funzione di libreria ( getAs- Come visibile in Fig. 6, il codice risulta piut-
sets() ) con la quale si può accedere a questi tosto semplice e può servire per introdurre
file ed utilizzarli per i propri scopi all’interno alcune nozioni che vedremo più in dettaglio
della nostra applicazione. nelle prossime puntate.
Infine, concludendo con la struttura dei file In questa applicazione basilare, la classe è
di progetto, troveremo, fuori da ogni directo- una sola (HellowordActivity) e tramite la
ry, il file manifest.xml, anch’esso editabile sia keyword extends deriva dalla classe base
graficamente sia manualmente e contenente Activity (da qui il motivo dell’importazione
informazioni specifiche riferite all’applicazio- import android.app.Activity). L’Activity
ne, come il nome vero e proprio, il numero in Android può essere pensata come una
di versione, la funzione di partenza, lo stile schermata grafica o “screen” al quale può
grafico, l’orientazione e l’elenco dei permes- essere associato un layout che altro non è che
si. È infatti buona norma rendere disponibile un file xml presente nella sotto directory la-
all’utilizzatore finale le informazioni su quel- yout, esaminata in precedenza e che conterrà
lo che potrà utilizzare la nostra applicazione. l’interfaccia grafica vera e propria. Questa
Se ad esempio pensiamo di dover utilizzare associazione avviene tramite la funzione
il modulo bluetooth o accedere alla rete, piut- setContentView() al quale viene passato
tosto che ai file interni alla sd-card dovremo come argomento R.layout.main che rappre-
specificarlo opportunamente in modo che al senta proprio il file xml main.xml. Più in
momento dell’installazione venga segnalato dettaglio, per entrare nella filosofia Android,
all’utente finale che potrà, così, scegliere o R è una classe autogenerata da Eclipse e la
meno di proseguire. possiamo trovare nel file R.java all’inter-
no della directory gen; nella classe R sono
Uno sguardo al codice contenute altre classi che corrispondono alle
Abbiamo già visto nella puntata precedente risorse dell’applicazione e che contengono
come Android fornisca insieme al plugin un valore univoco (Id) dei singoli elementi
ADT anche l’emulatore per poter testare la (immagini, audio, stringhe, file xml). Per cui
propria applicazione, prima di scaricarla con R.layout.main viene proprio specificato
direttamente sul dispositivo reale. di utilizzare la risorsa main.xml contenuta
A questo punto siamo in grado di generare la nella directory layout all’interno della cartel-
nostra prima applicazione apk e provarla pri- la di risorse res. Da notare come occorra non
ma sull’emulatore ed infine sul dispositivo specificare l’estensione xml per il file main.
Android. Prima di lanciare la compilazione xml. Una applicazione Android potrà con-
dell’applicazione, possiamo dare uno sguar- tenere più di una activity ed ognuna potrà

Elettronica In ~ Ottobre 2012 119


CORSO
Fig. 8

essere associata ad un file di risorse diverso La prima volta che viene avviato l’emulatore
in modo da presentare un layout diverso e potranno trascorrere svariati secondi, ma
quindi una interfaccia grafica diversa. saremo in grado di seguire i vari passi (ed
La funzione setContentView() deve essere eventuali errori) del processo tramite la vista
chiamata una volta sola ed al momento della Console di Eclipse, al termine dei quali otter-
partenza dell’applicazione, pertanto viene remo una finestra come in Fig. 7.
ridefinito, tramite override, il metodo on- L’applicazione è ovviamente molto semplice,
Create() della classe base (Activity) al quale ma può servire per comprendere meglio al-
viene aggiunta proprio la funzione setCon- cuni meccanismi di Android. Da notare come
tentView(), dopo la classica onCreate() della la stringa “Hello world, HelloWordActivity!”
classe base. Con la keyword super viene, che compare nella parte alta dello schermo
infatti, indicato che la onCreate() da utilizza- non sia presente nel codice del file Hellowor-
re è quella della classe base Activity. dActivity.java esaminato in precedenza. La
spiegazione è che la stringa è inserita nel file
Test su emulatore di risorse strings.xml all’interno della cartella
Ora, per compilare, possiamo lanciare la res ed è identificata con il nome hello, questo
build cliccando col tasto destro sul progetto stesso nome lo ritroviamo nel file di risorse
e poi su Build Project sul menu; all’interno main.xml che rappresenta il nostro layout.
della cartella bin troveremo il file helloword. Questo file è stato autogenerato presentando
apk appena generato. Nel caso la compilazio- già al suo interno un primo semplice con-
ne non fosse andata a buon fine potremmo trollo, un TextView che presenta, tra le sue
leggere gli errori o i warnings nella finestra proprietà, la stringa iniziale che è appunto
situata nella parte inferiore di Eclipse ed in hello (Fig. 8).
particolare sulla vista chiamata Problems Durante la fase di emulazione è anche possi-
dove vengono indicate le righe interessate bile visualizzare i messaggi di log che vengo-
dagli errori. no inviati dal sistema Android verso l’ester-
A questo punto possiamo provare l’appli- no, tramite la vista LogCat di Eclipse, situata
cazione con l’emulatore, cliccando col tasto nella parte inferiore della finestra. Possiamo
destro sul progetto e poi su Run As e An- però, come vedremo nelle puntate successi-
droid Application come mostrato in Fig. 6. ve, inserire alcune righe di log personalizzato

120 Ottobre 2012 ~ Elettronica In


Fig. 10

Fig. 9
CORSO

all’interno della nostra applicazione in modo


da seguirne il flusso tramite questa finestra.

Test su dispositivo Android


Per completare i passaggi possiamo ora ana-
lizzare la procedura per scaricare finalmente
la nostra app su un dispositivo reale.
Occorrerà prima di tutto aver installato i
driver USB del dispositivo che solitamente Saremo cosi’ forniti di una chiave che potre-
sono forniti al momento dell’acquisto del di- mo utilizzare per firmare le nostre applica-
spositivo Android, in modo che il vostro PC zioni.
lo riconosca correttamente una volta collega- Sempre da Eclipse, col tasto destro sul nome
to con il cavetto USB. del progetto scegliamo Android Tools e poi
Poi sarà necessario impostare il dispositivo Export Signed Application Package. Si aprirà
in modo tale da permettere lo scarico ed il una finestra come in Fig. 10 in cui potremo
debug dell’applicazione. Per fare questo scegliere se utilizzare una nuova chiave op-
bisognerà entrare nel menu Impostazioni di pure usarne una già creata in precedenza.
Android, selezionare la voce Applicazioni, Una volta completati questi passi avremo il
poi Sviluppo e spuntare la casella Debug nostro pacchetto apk firmato che potremo
USB come in Fig. 9. Comparirà allora il caricare su Google Play tramite la nostra pa-
simbolino Android sulla parte alta del vostro gina internet dell’account Google. Da questa
dispositivo che sarà così in grado di collo- pagina dovremo inserire almeno due scre-
quiare tramite Eclipse. enshot della nostra applicazione, una breve
Al prossimo avvio di Eclipse, infatti, ri- descrizione e una icona ad alta risoluzione
lanciando l’applicazione tramite Run As e che la rappresenti. Sono inoltre disponibili
Android Application da tasto destro la vista a questo indirizzo https://support.google.com/
Console vi informerà sullo scarico e in pochi googleplay/android-developer una serie di
secondi vedrete la vostra applicazione sul linee guida da seguire per rispettare lo stile
dispositivo reale. Android e rivolte esplicitamente a sviluppa-
Naturalmente anche in questa fase nella vista tori Android.
LogCat saranno presenti i messaggi di log Infine dopo alcune ore dal nostro upload po-
del dispositivo. tremo trovare la nostra applicazione diretta-
mentre sul market Android (Google Play).
Pubblicare App su Market Android
Non è di sicuro lo scopo principale di que- Conclusioni
sto articolo anche perchè le applicazioni Abbiamo ora tutte le basi per poter iniziare
che vedremo nelle prossime puntate saran- a prendere confidenza col mondo Android e
no sempre abbinate ai nostri dispositivi a creare qualcosa di nuovo.
microcontrollore, ma se volessimo pubblicare Nella prossima puntata analizzeremo il
le nostre app sul Google Play (il market di codice di una prima applicazione che si
Android) possiamo farlo in brevi passi. interfaccerà con dispositivi elettronici esterni,
Innanzi tutto occorre registrarsi al seguente prendendo spunto per esaminare alcuni con-
url https://market.android.com/publish come trolli grafici di Android e la loro interazione
g
programmatore Android e pagare 25 dollari. con l’utente.

Elettronica In ~ Ottobre 2012 121


Realizziamo una
prima applicazione
Android in grado
di controllare
dispositivi elettronici
remoti utilizzando il
CORSO

protocollo Bluetooth
e soffermandoci in
questa puntata sulla’
interfaccia utente.
Terza Puntata.

di
An
dr
Ch
ea

po
iap
ri
3
PROGRAMMIAMO CON
bbiamo visto, nelle puntate precedenti, tramite la porta seriale. Lo schema di principio è
A come realizzare un semplice progetto
Android, ma ancora privo di controlli visuali
rappresentato in Fig. 1. Prima di analizzare l’har-
dware, occupiamoci del software e, più in detta-
ed interfaccia utente. In queste pagine verran- glio, dell’interfaccia grafica che verrà visualizzata
no mostrati i primi passi con cui daremo vita sul dispositivo Android.
ad una applicazione Android completa ed in
grado di comunicare con il mondo esterno. Il Software
Amplieremo, pertanto, la trattazione prece- Android gestisce l’interfaccia grafica tramite uno
dente analizzando l’inserimento di controlli o più file xml, mediante i quali possiamo defini-
grafici che ci consentiranno di variare la re quello che viene chiamato layout, ovvero la
luminosità di una striscia led RGB (o qualsiasi disposizione in cui andremo ad inserire i controlli
altro dispositivo luminoso) collegata su una necessari per la nostra applicazione.
piattaforma Arduino con lo shield RGB-Shield Al momento della creazione di un progetto ab-
presentato nella rivista numero 159. biamo già a disposizione un layout basilare che
Per realizzare tutto questo senza fili, utilizze- troviamo all’interno della sottocartella res/layout
remo il protocollo Bluetooth, visto che, ormai, come già visto nella puntata precedente.
tutti i dispositivi ne sono dotati, e lo useremo Questo file, però, sarà piuttosto scarno perchè
per comunicare con un altro modulo Bluetooth presenta solamente il controllo TextView, ovvero
(questa volta esterno), collegato ad Arduino una casella di testo (per chi programma con C#

Elettronica In ~ Novembre 2012 149


CORSO
Fig. 1 che più di un file xml e ad ognuno di questi
corrisponderà una schermata grafica con le
corrispondenti view da noi inserite; in questo
modo, qualora la nostra applicazione richie-
desse altre finestre supplementari - come
una dialog-box, una schermata iniziale o una
qualsiasi altra finestra - potremo caricarla al
momento opportuno dal codice. Vediamo
come nel Listato 1 dove viene caricata la
pagina principale. Al momento della crea-
zione dell’applicazione (classe Activity) viene
chiamato il metodo onCreate() che a sua volta
corrisponde al controllo Label) su cui viene utilizza setContentView() passandogli il nome
scritta una stringa; adesso siamo in grado di del file di layout che verrà usato e che visua-
arricchirlo con controlli grafici come bottoni, lizzerà l’interfaccia grafica contenuta al suo
slide, caselle di testo e tanti altri che ci permet- interno.
teranno di interagire col modulo Bluetooth Ricordiamo, come già visto precedentemente,
esterno. Questi controlli grafici sono chiamati che la stringa R.layout rappresenta il percor-
viste (view) in quanto la classe base di ogni so della risorsa all’ interno del progetto (R
controllo è appunto la classe View. identifica la cartella res e layout la sua sotto
Nella cartella layout possiamo inserire an- cartella) e main è appunto, il nome del file
xml privato della sua estensione. Nulla vieta
di chiamarlo con altri nomi. Appartengono
Listato 1 al layout (e quindi andranno inseriti in questa
@Override stessa sottocartella) anche i file xml per la
public void onCreate(Bundle savedInstanceState)
{
gestione dei menu, ovvero quella schermata
super.onCreate(savedInstanceState); a comparsa che viene visualizzata premendo
setContentView(R.layout.main); il tasto di sistema. Vedremo successivamente

} come sarà gestita. Per semplicità inzieremo il
nostro progetto con un unico file xml.

Fig. 2

150 Novembre 2012 ~ Elettronica In


Listato 2
<?xml version=”1.0” encoding=”utf-8”?>
<LinearLayout xmlns:android=”http://schemas.android.com/apk/res/android”
android:layout_width=”fill_parent”
Interfaccia grafica android:layout_height=”fill_parent”
android:orientation=”vertical” >
Vediamo ora come inserire i
nostri controlli (o viste) nel <TextView
file main.xml che costituirà il android:id=”@+id/text01”
android:layout_width=”fill_parent”
corpo principale dell’interfac- android:layout_height=”wrap_content”
cia grafica. android:textSize=”34”
android:textColor=”#00F”
Chi è abituato a sviluppare android:text=”RGB LED Control” />
CORSO

applicazioni grafiche con Vi-


</LinearLayout>
sual Studio (ad esempio con
C#) si aspetterà un sistema
analogamente semplice per
inserire i vari controlli, ovve- stretti a capire la struttura xml che c’è dietro
ro scegliendoli e trascinandoli nell’area voluta ad ogni interfaccia e, a parte le inevitabili
dello schermo. In realtà è così anche con piccole difficoltà iniziali, saremo in grado di
Eclipse, ma non proprio altrettanto semplice controllare ogni sua parte e di modificarla con
ed occorrono, perciò, alcune precisazioni. facilità quando se ne presenterà l’occasione.
Aprendo un file di layout comparirà una Inoltre queste due modalità (grafica e testuale)
finestra come quella in Fig. 2, a questo punto vengono aggiornate insieme e costantemen-
potremmo scegliere di visualizzarlo in for- te da Eclipse, così se inseriamo un controllo
mato testo (cliccando sul tab main.xml) o in agendo sul file xml, potremmo tornare sulla
formato grafico (selezionando il tab Graphical finestra Graphical Layout per valutare il risulta-
Layout). In quest’ultimo formato possiamo se- to visivamente ed eventualmente correggere o
lezionare i controlli desiderati sulla sinistra e modificare, senza nemmeno dover ricompilare
trascinarli nella schermata centrale, ma questa il progetto continuamente. Una volta capiti i
operazione può portare, alla lunga, ad errori meccanismi che regolano la scrittura del layout
di posizionamento ed è suscettibile di continui potremmo impadronirci anche della sola mo-
aggiustamenti e correzioni, pertanto consiglia- dalità grafica, ma andiamo per gradi.
mo questa modalità solo una volta divenuti Innanzitutto è bene sapere che esistono diver-
più esperti. Utilizzando, invece, la modalità se modalità con cui generare il proprio layout;
testuale per inserire i controlli, saremo co- il layout più usato è quello lineare (LinearLa-

Fig. 3

Elettronica In ~ Novembre 2012 151


CORSO
Listato 3
<?xml version=”1.0” encoding=”utf-8”?>
<LinearLayout xmlns:android=”http://schemas.android.com/apk/res/android”
android:layout_width=”fill_parent”
yout) , mostrato nel Listato android:layout_height=”fill_parent”
android:orientation=”vertical” >
2 nel quale i controlli ven-
gono inseriti in sequenza, <TextView
android:id=”@+id/text01”
nello spazio a disposizione android:layout_width=”fill_parent”
sullo schermo; uno sotto android:layout_height=”wrap_content”
android:textSize=”34”
l’altro (nel caso in cui la android:textColor=”#00F”
sua proprietà orientation sia android:text=”RGB LED Control” />
settata a vertical) oppure
<TextView
uno a fianco all’altro (nel android:id=”@+id/text02”
caso in cui orientation sia android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
settata a horizontal). Occorre android:textSize=”20”
prima fare una precisazione android:textColor=”#FFF”
android:text=”control your bluetooth application” />
essenziale, senza la qua-
le potremmo riscontrare </LinearLayout>
alcuni problemi di incom-
patibilità con quanto scritto
sopra: tutti i controlli, ma anche gli stessi la- un controllo all’altro specie quando dovremo
yout, hanno sempre due proprietà fondamen- utilizzare il controllo dal codice java. Il Listato
tali, layout_width e layout_height che possono 3 è, alla fine, semplicemente lo stesso file main.
assumere due valori, wrap_content e fill_parent xml che è già stato scritto al momento della
(a partire dalle API Level 8 è stato rimpiaz- creazione del progetto a cui abbiamo aggiunto
zato da match_parent). Useremo fill_parent se un altro TextView e modificato alcune proprie-
vogliamo estendere la dimensione del control- tà, come il colore blu (nel formato #RGB), il
lo fino al controllo “padre” (nel nostro caso testo e la dimensione (textSize). Ora possiamo
il LinearLayout principale), mentre usereno aggiungere i controlli per interagire effetti-
wrap_content se vogliamo limitare la dimen- vamente con lo shield RGB e modificare le
sione del controllo nei limiti del necessario. componenti di colore del nostro dispositivo
Questo vuol dire che se al nostro TextView luminoso.
del Listato 2 (in cui il LinearLayout è impostato Visto che utilizzeremo il Bluetooth occorrerà
con orientazione verticale) volessimo aggiun- un pulsante con cui inizializzare o terminare
gere un altro controllo e visualizzarlo nella la comunicazione (useremo nello specifico un
zona sottostante, dovremmo settare layout_ ToggleButton) e tre slide (in Android chiamate
height del primo TextView a wrap_content, SeekBar) con cui cambiare le tre componenti di
altrimenti settandolo a fill_parent occuperebbe colore in modo indipendente.
tutto lo shermo a disposizio-
ne ed il secondo TextView
rimarrebbe coperto, proprio
come se non esistesse.
Il file xml risultante dopo
aver aggiunto il controllo è
mostrato nel Listato 3 ed il
risultato grafico è mostrato in
Fig. 3. Allo stesso modo, se il
LinearLayout avesse orienta-
zione orizzontale e volessimo
inserire un altro controllo
a fianco, questa volta sarà
la proprietà layout_width
che dovrà essere impostata
a wrap_content. Da notare,
inoltre, anche la proprietà id
che deve essere diversa da Fig. 4

152 Novembre 2012 ~ Elettronica In


Listato 4
<?xml version=”1.0” encoding=”utf-8”?>
<LinearLayout xmlns:android=”http://schemas.android.com/apk/res/android”
android:layout_width=”fill_parent”
android:layout_height=”fill_parent”
Per impratichirci maggior- android:orientation=”vertical” >
mente con l’aspetto grafico,
<TextView
aggiungeremo ad entrambi android:id=”@+id/text01”
i lati di ogni singola SeekBar android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
un bottone (Button) con cui android:textSize=”34”
regolare più finemente la android:textColor=”#00F”
luminosità. Per fare que- android:text=”RGB LED Control” />

CORSO

sto, utilizzeremo lo stesso <TextView


LinearLayout impostato android:id=”@+id/text02”
android:layout_width=”fill_parent”
verticalmente, ma questa android:layout_height=”wrap_content”
volta conterrà al suo interno android:textSize=”20”
android:textColor=”#FFF”
altri LinearLayout impostati android:text=”control your bluetooth application” />
orizzontalmente dove dispor-
remo i due bottoni e la slide. <LinearLayout
android:paddingTop=”34dp”
È infatti possibile annidare android:orientation=”horizontal”
più layout uno dentro l’ altro android:layout_width=”fill_parent”
android:layout_height=”wrap_content” >
ed anche di formati diversi, <TextView
come vedremo. Nel Listato 4 android:id=”@+id/text03”
android:layout_width=”wrap_content”
è visualizzato il file main.xml android:layout_height=”wrap_content”
con l’ inserimento della slide android:text=”turn bluetooth” />
che cotrolla la componente
<ToggleButton
del rosso (R). All’interno del android:id=”@+id/toggleButtonOnOff”
layout principale abbiamo android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
aggiunto un altro Text- android:text=”ToggleButton” />
View (con id/textViewR) che </LinearLayout>

utilizzeremo da codice per <TextView
scrivere il valore di intensità android:id=”@+id/textViewR”
della singola componen- android:layout_width=”fill_parent”
android:layout_height=”wrap_content”
te di colore. Da notare la android:textSize=”25”
proprietà layout_weight che android:text=”R Value” />

stabilisce una proporzione <LinearLayout
tra i vari controlli contenuti android:orientation=”horizontal”
android:layout_width=”fill_parent”
all’ interno di un LinearLa- android:layout_height=”fill_parent”
yout: inserendo un valore più android:layout_weight=”1”>
elevato per la slide al centro,

questa si estenderà per una <Button
larghezza maggiore rispetto android:id=”@+id/buttonRMinus”
android:layout_width=”wrap_content”
ai due bottoni laterali che android:layout_height=”wrap_content”
sono impostati con valore android:layout_weight=”1”
android:text=”-” />
molto più piccolo (1). Ovvia-
mente le proprietà di ogni <SeekBar
controllo sono molto nume- android:id=”@+id/seekbarR”
android:layout_width=”wrap_content”
rose e potrete sbizzarrirvi ad android:layout_height=”wrap_content”
aggiungerne e a modificarle a android:max=”20”
android:layout_weight=”25” />
seconda dei vostri gusti gra-
fici, senza dover ogni volta <Button
android:id=”@+id/buttonRPlus”
ricompilare il progetto, ma android:layout_width=”wrap_content”
semplicemente passando alla android:layout_height=”wrap_content”
modalità grafica (tab Graphi- android:layout_weight=”1”
android:text=”+” />
cal Layout) dopo aver scritto </LinearLayout>
il file xml. Inoltre, un altro
</LinearLayout>
vantaggio offerto da Eclipse è

Elettronica In ~ Novembre 2012 153


CORSO
Listato 5
<TableLayout xmlns:android=”http://schemas.android.com/apk/res/android”
android:layout_width=”fill_parent”
android:layout_height=”fill_parent”
android:stretchColumns=”1”>
rappresentato dalla possibilità <TableRow>
di avere il completamento au- <TextView
tomatico della sintassi, così vi android:id=”@+id/textViewR”
android:layout_width=”fill_parent”
basterà scrivere il namespace android:layout_height=”wrap_content”
“android:” ed avrete sul menu android:textSize=”18dp”
android:text=”R Value” />
contestuale tutte le possibili </TableRow>
proprietà che potrete utiliz-
<TableRow>
zare. Al Listato 4 basterà ora <Button
aggiungere altri due LinearLa- android:id=”@+id/buttonRMinus”
yout per gestire le componenti android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
Verde e Blu, ricordandosi di android:layout_weight=”1”
cambiare Id ai controlli, in android:text=”-” />
<SeekBar
maniera corrispondente. Otter- android:id=”@+id/seekbarR”
remo un aspetto grafico come android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
mostrato in Fig. 4. android:layout_weight=”25” />
Nella modalità grafica di <Button
Eclipse è anche possibile android:id=”@+id/buttonRPlus”
android:layout_width=”wrap_content”
visualizzare lo schermo vir- android:layout_height=”wrap_content”
tuale in modalità landscape, in android:layout_weight=”1”
android:text=”+” />
modo tale da verificare la coe- </TableRow>
renza dell’interfaccia anche nel
</TableLayout>
caso in cui il dispositivo reale
venga ruotato di 90 gradi.
Alternativamente si può anche decidere che di utilizzare questo tipo di Layout anche per la
la nostra applicazione venga visualizzata a nostra applicazione; nel Listato 5 è mostrato il
schermo, sempre e solo in una modalità, ad codice per inserire uno dei nostri slide utiliz-
esempio landscape come in questo caso in zando il TableLayout all’ interno del LinearLa-
cui avere una slide più larga ci permetterà yout principale.
una miglior regolazione. Per
fare questo dovremo settare
Listato 6
una proprietà nel file Android-
Manifest.xml ed in particolare <RelativeLayout xmlns:android=”http://schemas.android.com/apk/res/
android”
screenOrientation = “Landsca- android:layout_width=”fill_parent”
pe”. Ora in qualsiasi posizione android:layout_height=”fill_parent”
android:paddingBottom=”30px”
ruoteremo il nostro dispositivo android:padding=”0px” >
Android, il layout si presenterà
<Button
sempre con un solo orienta- android:id=”@+id/buttonRMinus”
mento. android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:layout_weight=”1”
Altri layout android:text=”-” />
Fin qui abbiamo analizzato
<SeekBar
il Linear Layout che è anche android:id=”@+id/seekbarR”
quello più utilizzato, ma in android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
qualche altra applicazione android:layout_toRightOf=”@+id/buttonRMinus”
potremo trarre maggiori android:minWidth=”200dp” />
vantaggi o trovarci semplice-
<Button
mente più comodi usando altri android:id=”@+id/buttonRPlus”
tipi di layout. Se ad esempio android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
volessimo disporre oggetti android:layout_toRightOf=”@+id/seekbarR”
in forma tabellare potremmo android:text=”+” />

usare il TableLayout che facilita </RelativeLayout>
la composizione. Nulla vieta

154 Novembre 2012 ~ Elettronica In


Listato 7
<?xml version=”1.0” encoding=”utf-8”?>
<FrameLayout xmlns:android=”http://schemas.android.com/apk/res/android”
android:layout_width=”fill_parent”
android:layout_height=”fill_parent” >
Il codice si commenta da solo. <ImageView
Un altro tipo di layout che possia- android:id=”@+id/image01”
mo utilizzare è il RelativeLayout android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
in cui la posizione dei nostri android:layout_marginLeft=”40dp”
controlli è relativa agli altri già android:src=”@drawable/rgb” />

esistenti.Utilizzando questo tipo <LinearLayout


di layout avremo a disposizio- android:layout_width=”fill_parent”
android:layout_height=”fill_parent”
CORSO

ne alcune proprietà particolari android:orientation=”vertical” >


(come layout_toRightOf, layout_to-
LeftOf, layout_below, layout_above) …

e sarà necessario specificare l’id </LinearLayout>
del controllo di riferimento. Nel
</FrameLayout>
Listato 6 viene mostrato lo stesso
esempio, ma utilizzando questo
tipo di layout.
Il RelativeLayout ci permette di Listato 8
posizionare i nostri controlli in <ScrollView
android:layout_width=”fill_parent” android:layout_height=”wrap_content”>
modo indipendente dalle dimen-
sioni dello schermo che sono <RelativeLayout>
spesso molto diverse col variare …
</RelativeLayout>
del dispositivo finale. Per questo
motivo è preferibile ad un altro </ScrollView>
tipo di layout, l’AbsoluteLayout,
in cui le posizioni degli oggetti sono deter- di sfondo. Occorre inserire l’intero LinearLa-
minate, appunto, da coordinate assolute. yout principale all’ interno di un FrameLayout
L’AbsoluteLayout è sconsigliato dalla comunità ed anteporre un nuovo controllo adatto per
Android e pertanto non lo analizzeremo in contenere immagini, l’ImageView. In questo
questa sede. Un altro tipo di layout è il Frame- controllo setteremo la proprietà src con il path
Layout in cui i controlli vengono posizionati relativo dell’immagine che avremo caricato
tutti a partire dall’angolo superiore sinistro nella cartella drawable (interna alla sottocar-
dello schermo in ordine di scrittura, ottenen- tella res) e di seguito copieremo i controlli già
do pertanto una sovrapposizione dei control- utilizzati precedentemente, che ora verrrano
li. Questo può essere utilizzato anche nella sovrapposti all’immagine, ottenendo il risul-
nostra applicazione per inserire un’immagine tato in Fig. 5. Da notare, anche in questo caso,
come il nome del file immagine debba essere
scritto nel file xml, senza estensione. Se poi la
nostra applicazione avesse bisogno di molti
più controlli di quelli visualizzabili su scher-
mo, potremmo utilizzare un controllo parti-
colare, chiamato ScrollView che ci permetterà
di spostarci nella parte nascosta tramite una
classica scrollbar. Occorrerà inserire i control-
li, ma anche interi layout, all’interno della
ScrollView come nel Listato 8.

Conclusioni
A questo punto, terminato l’aspetto grafico
della nostra applicazione, ci possiamo preoc-
cupare di scrivere il codice associato alle azio-
ni che verranno eseguite sui nostri controlli,
ovvero la gestione degli eventi, che analizze-
g
Fig. 5 remo nella prossima puntata.

Elettronica In ~ Novembre 2012 155


Proseguiamo
con la descrizione
dell’applicazione
per controllare,
tramite smartphone,

la luminosita
CORSO

di una striscia a LED


RGB, utilizzando il
protocollo Bluetooth
e presentando
il progetto di un
apposito shield per
Arduino.

ri
4
Quarta puntata.

po
iap
Ch
ea
dr
An
di

PROGRAMMIAMO CON
bbiamo visto, nella puntata precedente,
A come gestire l’interfaccia grafica su un di-
spositivo Android; lo abbiamo fatto usando come
esempio l’inserimento nella nostra applicazione
di tre barre scorrevoli per variare la tonalità
di colore di una striscia a LED RGB, collegata
in remoto tramite un dispositivo Bluetooth e
Arduino. Ci eravamo lasciati con la presentazio-
ne del layout grafico senza preoccuparci della Fig. 1
gestione degli eventi; ora è giunto il momento di
analizzare il codice Android che ci permetterà
di associare alle azioni dell’interfaccia grafica gli
eventi che ci interessano, in modo da prendere
il controllo del modulo Bluetooth integrato nel
nostro smartphone ed inviare dati ad un altro
modulo Bluetooth esterno. Analizzeremo anche
il modulo necessario per ricevere dati dal nostro
smartphone, per tradurli in formato seriale e per
poi trasferirli ad Arduino. Lo schema di principio
dell’applicazione è rappresentato in Fig. 1.

Elettronica In ~ Dicembre 2012 / Gennaio 2013 113


CORSO
CLASSI BLUETOOTH
1. BluetoothAdapter 2. BluetoothDevice 3. BluetoothSocket
Rappresenta il modulo radio Bluetooth Rappresenta il modulo Rappresenta
locale, ovvero quello del nostro smartphone. Bluetooth remoto e di l’interfaccia tramite la
Da esso possiamo ottenere informazioni esso possiamo ottenere le quale avviene lo scambio
sullo stato (abilitato o meno), il nome, la stesse informazioni di dati tra i due dispositivi
classe, l’indirizzo e ancora altro. dell’adapter. Bluetooth.

CODICE ANDROID: GESTIONE BLUETOOTH sul quale “viaggeranno” i dati in entrambe le


Incominciamo riprendendo il nostro progetto direzioni, ma in modalità half-duplex, ovvero
su Eclipse ed aggiungendo le funzionalità che non simultaneamente. Il client, per comunicare
ci permetteranno di gestire il modulo Bluetooth con il server, dovrà prima connettersi al “server
del nostro dispositivo. Tutto ciò ci viene permes- socket” (tramite funzione connect()) utilizzando il
so con facilità dalle API Android che abbiamo “MAC address” del dispositivo che funziona da
a disposizione dopo aver installato il plugin server.
Adt (Android Development Toolkit) visto nella Nel nostro progetto utilizzeremo lo smartphone
prima puntata, senza ricorrere all’uso di librerie come client ed il modulo Bluetooth esterno come
esterne. Basterà semplicemente aggiungere i server. Il MAC address è un identificatore uni-
seguenti import nel codice principale, prima di voco di sei byte assegnato alle interfacce di rete;
dichiarare la classe: conoscendolo, siamo sicuri che il client comuni-
cherà esclusivamente col server scelto.
import android.bluetooth.BluetoothAdapter; Come fare a conoscere questo indirizzo? So-
import android.bluetooth.BluetoothDevice; litamente il client effettua una scansione sul
import android.bluetooth.BluetoothSocket; Bluetooth per verificare la presenza di altri
dispositivi remoti attivi, da cui ricava i relativi
Saremo, pertanto, in grado di utilizzare le classi MAC address in modo che l’utente possa sce-
Bluetooth che utilizzeremo nel nostro progetto gliere a quale di questi server connettersi. Una
e che sono brevemente descritte nel riquadro volta scelto un MAC address, otterremo le infor-
CLASSI BLUETOOTH che trovate in queste pa- mazioni del dispositivo remoto con le seguenti
gine. Prima di analizzare nel dettaglio il codice righe di codice:
è opportuno ricordarsi di aggiungere nel file
AndroidManifest.xml del nostro progetto l’auto- mBluetoothAdapter = BluetoothAdapter.getDefaul-
rizzazione ad usare il modulo Bluetooth dello tAdapter();
smartphone; potete fare ciò attraverso le seguen- BluetoothDevice device = mBluetoothAdapter.
ti righe di codice: getRemoteDevice(serverAddress);

<manifest> Da notare come mBluetoothAdapter rappresenti


… il dispositivo locale ottenuto tramite il metodo
<uses-permission android:name=”android.permis- getDefaultAdapter() e grazie al metodo getRemote-
sion.BLUETOOTH” /> Device() a cui viene passato il MAC address del
<uses-permission android:name=”android.permis- dispositivo remoto, otteniamo un BluetoothDevice
sion.BLUETOOTH_ADMIN” /> dal quale avremo informazioni sul suo nome,
... indirizzo e classe. Infine, sempre dal dispositivo
</manifest> remoto possiamo ottenere il server socket (btSo-
cket) come nella seguente riga di codice:
Queste informazioni, oltre ad essere usate nei
filtri interni di Google Play (il market-store di btSocket = device.createRfcommSocketToServiceReco
Android) servono ad avvertire l’utilizzatore fina- rd(MY_UUID);
le, al momento dello scaricamento dell’ applica-
zione, che questa utilizzerà il modulo Bluetooth. e per connetterci al server useremo la riga:
In una comunicazione Bluetooth tra due
dispositivi, uno si comporterà come server e btSocket.connect();
l’altro come client. Il server viene attivato per
primo e mette a disposizione un “server socket” Per ora ci occuperemo di inviare dati in una sola

114 Dicembre 2012 / Gennaio 2013 ~ Elettronica In


Listato 1
int initRet = BtCore.initBluetooth();
Log.v(TAG, “initBluetooth: “ + initRet);
direzione, ovvero dal nostro smartphone ad Ar- if (initRet == -1)
duino (al quale sono collegati sia la barra a LED, {
Toast.makeText(BtclientActivity.this, “Bluetooth
sia il modulo Bluetooth). Questo vuol dire che is not available.”, Toast.LENGTH_LONG).show();
una volta connessi al socket Bluetooth del server, Log.e(TAG, “error -1”);
lo useremo solo in scrittura, inviando i dati rela- finish();
}
tivi alle componenti di colore scelte dall’interfac- if (initRet == -2)
cia, utilizzando le linee di codice: {
Toast.makeText(BtclientActivity.this, “Please
CORSO

enable your BT and re-run this program.”, Toast.


outStream = btSocket.getOutputStream(); LENGTH_LONG).show();
Log.e(TAG, “error -2”);
outStream.write(data); finish();
}
Per adesso e per semplicità, utilizzeremo un
int crtRet = BtCore.createSocketBluetooth();
MAC address cablato nel codice e uguale a Log.v(TAG, “createSocketBluetooth: “ + crtRet);
quello del nostro modulo Bluetooth. Per ottenere
retCon = BtCore.connectBluetooth();
tale valore possiamo leggerlo sull’etichetta del Log.v(TAG, “connectBluetooth:” + retCon);
dispositivo dove solitamente è scritto, oppure
if (retCon != 0)
ricavarlo interrogando quest’ultimo con coman- {
di seriali, come spiegato in seguito. Più avanti Toast toastStart = Toast.makeText(BtclientActivity.
mostreremo come eseguire una scansione di tutti this, “no module bluetooth found!”, Toast.LENGTH_LONG);
toastStart.show();
i server Bluetooth attivi ed ottenere il MAC ad- Log.v(TAG, “exit”);
dress direttamente dalla nostra applicazione per
_toggleOnOff.setChecked(false);
rendere il codice più usabile e modulare.
Ricapitolando, alla pressione del nostro pulsante return;
}
“OnOff” dovremo inizializzare il modulo Blueto- Log.v(TAG, “On”);
oth, verificando prima che il nostro smartphone
lo supporti e successivamente che sia abilitato.
In caso di errore visualizzeremo un messaggio di Da notare come l’API Log può avere diversi livelli
errore in una finestra di dialogo a scomparsa che a seconda della sua rilevanza: Log.e() è utilizzata
Android chiama MessageToast (Listato 1). per identificare un messaggio di errore, Log.v() un
A connessione avvenuta, ad ogni tocco sulla semplice messaggio informativo e Log.w() un mes-
barra di scorrimento verrà inviata una stringa saggio di attenzione (warning). Questa diversità
con un messaggio del tipo: “R123\n” verso il viene tradotta in un diverso colore con cui LogCat
dispositivo Bluetooth remoto, dove con R viene (il monitor Android) mostra questi messaggi. La
indicata la componente di colore rosso (G nel Fig. 2 mostra un esempio d’uso di Log().
caso del verde e B nel caso del blu), con “123” Il TAG, invece, è sempre una stringa di testo e
il valore della componente di colore che può
variare da 0 a 255 e con “\n” il ritorno a capo per
determinare la fine del messaggio. Listato 2
La scelta della sintassi del messaggio conte-
public static void sendMessageBluetooth(String message)
nuto nella stringa è personalizzabile a piacere. {
Potremmo inviare stringhe di qualsiasi tipo: if (outStream == null)
l’importante è che vengano coerentemente parse- {
Log.e(TAG, “stream is Null”);
rizzate ed interpretate dal modulo remoto che return;
le riceverà; nel nostro caso sarà Arduino ad occu- }
byte[] msgBuffer = message.getBytes();
parsi di ciò (in seguito vedremo come). try
In ogni caso, l’invio di questo messaggio è reso {
outStream.write(msgBuffer);
possibile proprio accedendo in scrittura al server Log.v(TAG, “btSended:” + message);
socket visto prima, come viene fatto nella funzio- }
ne del Listato 2. catch (IOException e)
{
Come si vede nei listati 1 e 2, a corredo del nostro Log.e(TAG, “seekbarLeft: Exception during write.”, e);
codice e per semplificarne il debug abbiamo }
}
aggiunto nei punti strategici qualche riga di log.

Elettronica In ~ Dicembre 2012 / Gennaio 2013 115


CORSO
remo sul pulsante. In realtà, non verrà passa-
ta esattamente la funzione, ma una classe di
interfaccia view.OnClickListener che contiene già
alcuni metodi tra cui la onClick(), che potremo ri-
empire a nostro piacimento. Solitamente questa
classe viene creata direttamente (con l’operatore
new) nel parametro di passaggio, ottenendo un
codice come quello del Listato 3.
Fig. 2 Nel caso del toggleButton, che ammette due
stati, sul relativo metodo onClick() andremo a
serve per differenziare i messaggi di log in modo verificare in quale dei due stati si trovi (accesso
da poterli filtrare ed analizzarli senza mischiarli o spento) e ci comporteremo di conseguenza
con quelli di sistema. andando a creare il socket di comunicazione con
Inoltre LogCat è attivo anche senza lanciare le funzioni Bluetooth create ed analizzate prece-
l’applicazione in modalità di debug dell’appli- dentemente. Se cliccheremo una seconda volta
cazione, ma semplicemente collegando il cavo sul pulsante, chiuderemo il socket Bluetooth.
USB ed abilitando la modalità debug nel nostro Il discorso è più semplice per i pulsanti normali
dispositivo Android. che nella puntata precedente avevamo disposto
Tutte le funzioni per la gestione del protocollo ai lati delle singole progress-bar per regolare
Bluetooth sono state inserite in una classe nuova finemente la luminosità.
(BtCore), contenuta nel file BtCore.java in modo La funzione per settare la callback sul click è
da poter essere riutilizzata in seguito nelle nostre sempre la setOnClickListener() e nella onClick()
future applicazioni che richiederanno l’uso di un incrementeremo (per il pulsante +) il valore
modulo Bluetooth. Occorre, però, ricordarsi che aggiornando la barra di scorrimento tramite
i file java dello stesso progetto (all’interno della metodo setProgress() e cambieremo opportuna-
cartella src) dovranno avere come intestazione lo mente il valore della stringa di testo _textViewG,
stesso package, che nel nostro caso è “package tramite il metodo setText(). Naturalmente, alla
com.kiand.bt”. fine invieremo il valore raggiunto verso Arduino
usando la nostra funzione sendMessageBluetooth()
CODICE ANDROID: EVENTI che scriverà il messaggio (ad esempio la stringa
Ora vediamo come associare queste azioni “G234\n”) sul socket di comunicazione.
all’interfaccia grafica della nostra applicazione: Per l’evento sullo spostamento delle barre di
ad esempio al pulsante _toggleOnOff che use- scorrimento tramite touch, useremo un metodo
remo per creare il socket Bluetooth. Prima di
tutto definiamo un oggetto (detto anche widget)
ToggleButton (ricordandoci sempre di definire il Listato 3
corrispettivo import android.widget.ToggleButton); _toggleOnOff = (ToggleButton) findViewById(R.id.toggleButtonOnOff);
_toggleOnOff.setBackgroundColor(Color.GREEN);
nella funzione oncreate() dell’Activity principale, _toggleOnOff.setText(“ON”);
assoceremo tale oggetto alla risorsa preceden- _toggleOnOff.setOnClickListener(new View.OnClickListener()
temente generata nel file main.xml, tramite la {
public void onClick(View v)
funzione findViewById(): {

if (_toggleOnOff.isChecked())
_toggleOnOff =(ToggleButton)findViewById(R. {
id.toggleButtonOnOff); // ...
// attivazione Bluetooth
//...
A questo punto possiamo modificare le proprie- }
tà del pulsante _toggleOnOff a nostro piacimento, else
{
modificando ad esempio la scritta (tramite // ...
metodo setText()) o il colore (tramite il metodo // chiusura Bluetooth
// ...
setBackgroundColor()) nel modo che appare nel }
listato; possiamo quindi associare la funzione }
}
(callback) che verrà chiamata quando clicche-

116 Dicembre 2012 / Gennaio 2013 ~ Elettronica In


diverso: mentre per i pulsanti abbiamo creato
tutto all’interno del metodo onCreate() dell’acti-
vity, che viene eseguita solo la prima volta, per
la progress bar utilizzeremo il metodo onProgres-
sChanged() che sarà proprio dell’activity stessa.
In ogni caso nella onCreate() sarà necessario,
prima di tutto, associare l’ oggetto seekBar alla
CORSO

risorsa corrispondente e settare la callback a


sè stessa, come visibile nel Listato 4. In questo
modo, ogni volta che toccheremo una delle tre
barre di scorrimento ci troveremo nella onPro-
gressChanged() che è dichiarata direttamente
nell’activity (notare che è stato necessario usare
implement SeekBar.OnSeekBarChangeListener per
poterla utilizzare all’interno della classe stessa).
In particolare, la callback onProgressChanged() ha
tre parametri, visibili sempre nel Listato 4, che al
momento del touch saranno riempiti con i valori
appropriati in modo da poter rilevare quale
delle tre barre di scorrimento (seekBar) è stata
mossa e con quale valore (progress).
Il primo valore verrà confrontato con ognuna
delle risorse definite nel file main.xml (grazie al Schema elettrico dello shield Bluetooth
metodo getId()) e il secondo parametro verrà in- per Arduino descritto nell’articolo.
viato come stringa verso Android. Infine, il terzo
parametro avrà valore True o False a seconda della scheda remota (nel nostro caso Arduino),
che il movimento sia stato generato o meno gestire i messaggi ricevuti in formato seriale ed
dall’utente; tuttavia nel nostro caso non verrà interpretarli per svolgere le funzionalità che ci
preso in considerazione. interessano (nel nostro caso, il controllo della
A questo punto la nostra applicazione Android barra a LED).
ha tutto l’essenziale per controllare una piatta- Potremmo passare alla descrizione del codice
forma hardware esterna, che sarà ovviamente su Arduino, ma andiamo per gradi e occupia-
dotata di un modulo Bluetooth. Spetterà al cuore moci prima del modulo Bluetooth esterno che
rappresenta il tramite per la comunicazione tra
Android ed Arduino.
Listato 4
@Override MODULO BLUETOOTH
public void onCreate(Bundle savedInstanceState)
{
Esistono tanti modelli di moduli Bluetooth che
// ... si basano su chipset differenti e che, pertanto,
_seekBarR = (SeekBar)findViewById(R.id.seekbarR); richiedono un diverso tipo di pilotaggio; si
_seekBarR.setOnSeekBarChangeListener(this);
// ... differenziano per numero di pin e rispettive
} funzionalità, ma tutti possono comportarsi sia
public void onProgressChanged(SeekBar seekBar, int da server che da client e supportano il profilo
progress, boolean fromTouch) SPP (Serial Port Profile) ovvero la capacità di
{
int id = seekBar.getId();
emulare una seriale RS232 convertendo i segnali
radio in entrambe le direzioni. Inoltre, proprio
if (id == R.id.seekbarR) come una comune porta seriale, possono essere
{
_RValue = progress; collegati direttamente ad un microcontrollore
_textViewR.setText(“R:” + progress); tramite i soli due fili TX ed RX e sono in grado di
BtCore.sendMessageBluetooth(“R” + progress +”\n”);
} gestire diverse velocità di comunicazione.
//… Come fare, allora, per configurarli, se sono
}
collegati con soli due fili? Ebbene, esistono due

Elettronica In ~ Dicembre 2012 / Gennaio 2013 117


CORSO
Fig. 3

vi consigliamo di collegarlo alla seriale del


PC tramite uno di quei convertitori di livello
seriale (per adattare i 12 V del PC ai 3,3 V del
modulo) ed usare HyperTerminal (o qualsiasi
altro terminale seriale su PC); potrete così fare
tutte le prove che desiderate.
Sapere già come gestire il modulo Bluetooth
vi eviterebbe anche di compilare ogni volta il
codice su Arduino, soprattutto in fase di test.

LO SHIELD BLUETOOTH RN-42


Per realizzare l’esercitazione descritta in questa
diverse modalità di funzionamento: una di co- puntata del corso abbiamo preparato uno shield
mando e una dati. per Arduino basato sul modulo RN-42: si tratta
Nella modalità comando i segnali della seriale di un circuito comunque utilizzabile in tutte le
vengono usati per impartire istruzioni al nostro applicazioni in cui serva instaurare una con-
modulo, come ad esempio per settare la configu- nessione su Bluetooth. Di seguito ne diamo una
razione (server/client) o per richiedere infor- breve descrizione, partendo dai connettori di
mazioni sulla versione, conoscere il MAC (di cui dispone, che servono ad interfacciarlo con il
cui abbiamo già parlato), il nome (e tante altre mondo esterno; escludiamo quelli di connessio-
caratteristiche) alle quali il modulo risponderà ne con Arduino. Il primo è quello siglato RS232,
con stringhe di conferma o di errore. le cui connessioni sono le seguenti:
Nel nostro progetto abbiamo utilizzato uno • 1 = CTS;
shield con a bordo un modulo Bluetooth di • 2 = RTS;
ultima generazione (RN-42), i cui comandi e set- • 3 = TX;
taggi sono già stati ampiamente documentati sul • 4 = RX;
numero 169 della rivista, ma volendo utilizzare • 5 = GND.
anche moduli Bluetooth più datati che si basano
su sistema “BlueSMiRF” si può fare riferimento Tale connettore è l’interfaccia seriale grazie
ai comandi AT descritti nella documentazione alla quale avviene la comunicazione di dati e
del produttore e ad un piccolo esempio di co- comandi. Si accede alla fase di comando invian-
municazione tra modulo e seriale del PC (vedere do la sequenza di escape “$$$<cr>” sul pin RX
Fig. 3). del modulo Bluetooth, tramite la seriale locale
Normalmente, dando alimentazione al modulo, del PC o direttamente da Arduino. Per uscire
questo entra direttamente nella modalità dati; da questa fase ed entrare nella fase dati occorre
in questa, i pin della seriale vengono utilizzati inviare la sequenza “---”.
esclusivamente per scambiarsi messaggi. Durante la fase dati ed una volta instaurata una
Per entrare nella modalità comando, invece, connessione Bluetooth, ciò che viene inviato sul
viene usata una particolare sequenza di caratteri, pin RX verrà diretto, via radio, al dispositivo
detta sequenza di escape, che nel nostro caso remoto, mentre sul pin TX ritroveremo ciò che
è “$$$”. Quindi inviando sul piedino RX del viene ricevuto dal dispositivo remoto.
modulo Bluetooth questa sequenza, il modulo Solitamente si usano solo questi tre pin (TX, RX
risponde con una sua stringa di conferma e ri- e GND), mentre CTS ed RTS vengono impiegati
mane in attesa di altri comandi. Per ritornare alla per il controllo di flusso, ma rimangono comun-
modalità dati, esistono altre sequenze di escape que disponibili.
che variano da modulo a modulo. Nel caso si voglia utilizzare anche CTS ed RTS
Possiamo inviare questi comandi al modulo occorre scollegare il jumper PROG. Il pin CTS
Bluetooth utilizzando la seriale di Arduino; può essere anche usato per “svegliare” il modu-
nel nostro progetto faremo proprio così. lo Bluetooth dalla fase di sleep (in cui consuma
Se volete fare un po’ di pratica con il modulo meno di 2 mA) mediante il passaggio da stato
Bluetooth, ad esempio per imparare come logico “0” a stato logico “1”.
configurare la seriale e fare altre impostazioni, Il secondo connettore dello shield è quello sigla-

118 Dicembre 2012 / Gennaio 2013 ~ Elettronica In


MASCHERA I/O VALORE OUTPUT Fig. 4
IO IO IO IO IO IO IO IO
x x x x x x x x
11 10 9 8 11 10 9 8
0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0

to IO e raggruppa tre degli I/O disponibili nel


modulo RN-42; la sua piedinatura è la seguente: Il connettore veicola un bus SPI e tramite esso
• 1 = GPIO9; si può scrivere nella Flash interna al modulo
• 2 = GPIO10; RN-42 in modo da aggiornare o sostituire il
• 3 = GPIO11; firmware; tale connessione non è comunque
• 4 = GND.
CORSO

indispensabile.
Nello shield abbiamo anche un connettore sigla-
Per leggere o pilotare questi I/O è necessario to PCM, la cui piedinatura è la seguente:
entrare nella modalità comandi (da seriale locale • 1 = GND;
o direttamente da Arduino). Per controllare tali • 2 = P-OUT;
linee di I/O bisogna fare riferimento al comando • 3 = P-IN;
S*,<word> in cui word è una stringa esadecimale • 4 = P-CLK;
di 16 bit i cui 8 bit più significativi rappresenta- • 5 = P-CS.
no la maschera per determinare i pin di output
e di input, mentre gli 8 bit meno significativi L’interfaccia PCM offre la possibilità di inviare
corrispondono al valore (1 per portare il pin a via radio, ad un altro dispositivo Bluetooth, uno
livello alto e zero per portarlo a livello basso). La stream audio ad una velocità di trasferimento di
struttura è quella mostrata nella Fig. 4. 64 kbps. Per i nostri scopi questa possibilità non
Per esempio: viene sfruttata.
S*,0202 abilita GPIO9 come output e lo mette al L’ultimo connettore che vediamo è quello USB,
valore “1” che evidentemente veicola una connessione
S*,0E00 abilita mette tutti i GPIO dello strip a “0” USB; la sua piedinatura è:
• 1 = GND;
Il comando GK restituisce una stringa • 2 = DP;
esadecimale col valore dei pin allo stato attuale; • 3 = DM;
tali pin sono collegati anche ai pin di Arduino. • 4 = Vcc.
In particolare:
• GPIO9 è collegato ad A4; Collegando questo connettore al PC tramite un
• GPIO10 è collegato ad A3; cavo USB è possibile utilizzare la modalità “HCI
• GPIO11 è collegato ad A2. over USB” del modulo Bluetooth ed ottenere ve-
locità maggiori (fino a 3 Mbps). Tuttavia, questo
E adesso passiamo al connettore AD, i cui è possibile utilizzando solo la versione USB del
contatti sono solo ingressi analogici e possono modulo RN-42 (chiamata appunto RN-42-USB)
essere utilizzati dal modulo Bluetooth per ac- o aggiornando opportunamente il firmware
quisire alcune informazioni, come ad esempio il tramite il bus SPI.
livello di tensione sulla batteria. La piedinatura Bene, analizzati i connettori vediamo gli altri
di questo connettore è: elementi previsti nello shield, partendo dal
• AIO0; dip-switch SW1, che può collegare alcuni pin
• AIO1; (GPIO3, GPIO4, GPIO6, GPIO7) del modulo
• GND. Bluetooth a VCC (3,3V) tramite quattro resi-
stenze; questi pin servono per impostare alcune
In questa puntata del corso il connettore IO non modalità di funzionamento del modulo RN-42
viene usato. e sono normalmente collegati a massa tramite
Passiamo adesso al connettore SPI, la cui piedi- resistenze di pull-down integrate nel modulo.
natura è: Spostando il rispettivo dip verso la scritta “ON”
• 1 = S-MOSI; viene collegato il pin del modulo a Vcc e quindi
• 2 = S- SCLK; a livello logico 1, abilitando la relativa funzio-
• 3 = S-MISO; nalità. Tutti questi pin vengono interpretati al
• 4 = S-CS; momento dell’avvio, quindi cambiando lo stato
• 5 = GND. dei dip durante il funzionamento dello shield

Elettronica In ~ Dicembre 2012 / Gennaio 2013 119


CORSO
[Shield Bluetooth, piano di MONTAGGIO]
Elenco Componenti:

R1: 220 ohm (0805)


R2: 220 ohm (0805)
R3: 220 ohm (0805)
R4: 1 kohm (0805)
R5: 1 kohm (0805)
R6: 1 kohm (0805)
R7: 1 kohm (0805)
R8: 20 kohm (0805)
R9: 10 kohm (0805)
C1: 100 nF ceramico (0805)
U1: RN-42
RST: Microswitch
LD1: LED rosso (0805)
LD2: LED verde (0805)
LD3: LED giallo (0805)
SW1: Dip-Switch 4 vie

Varie:
- Strip M/F 3 vie (2 pz.)
- Strip M/F 6 vie
- Strip M/F 8 vie (2 pz.)
- Strip M/F 10 vie
- Strip Maschio 2 vie
- Strip Femmina 3 vie
- Strip Femmina 4 vie (2 pz.)
- Strip Femmina 3 vie (3 pz.)
- Jumper
- Circuito stampato

non si otterranno gli effetti desiderati. vo dip, prima di alimentare lo shield; una volta
Le modalità si possono anche configurare tramite alimentato occorre riportarlo allo stato “0”, poi allo
software, entrando nella fase comando tramite la stato “1” , di nuovo allo stato “0” e ancora ad “1”.
sequenza di escape “$$$”. Ad ogni cambiamento di stato deve trascorrere un
Di seguito descriviamo i pin che fanno capo al secondo circa.
dip-switch. • MASTER. È collegato al pin GPIO6 del modulo
• AUTO. È collegato al pin GPIO3 del modulo Bluetooth. Per impostazione predefinita, il modulo
Bluetooth. Se abilitato, permette l’accoppiamento Bluetooth si comporta come un dispositivo slave
automatico con un altro dispositivo remoto Blueto- e quindi può venire rilevato da altri dispositivi
oth, senza pertanto dover digitare il codice segreto Bluetooth e connettersi ad essi, ma non è in grado
(detto “Passkey”). Se però il dispositivo remoto di iniziare per primo una connessione. Portando a
è settato per richiedere l’autenticazione, verrà ON il rispettivo dip, il modulo lavorerà in modali-
comunque richiesto l’inserimento della Passkey tà Master. In questa fase non potrà essere rilevato
per instaurare un collegamento sicuro. La Passkey da altri dispositivi, ma sarà esso ad effettuare
predefinita è 1234, ma può essere modificata, una connessione verso un particolare dispositivo
entrando in modalità comando, tramite sequenza remoto che avremo memorizzato in precedenza
di escape “$$$”. nella Flash del modulo RN-42, tramite il comando
• DEFAULT. È collegato al pin GPIO4 del modulo SR,<address> (in cui <address> è l’indirizzo MAC
Bluetooth. Permette di ripristinare tutti i settaggi del dispositivo remoto scelto). Nel caso in cui non
interni al modulo ai valori iniziali di “fabbrica”. ci fosse alcun indirizzo memorizzato, l’RN-42 effet-
Può essere utile quando vengono impartiti coman- tuerà una scansione e si connetterà al primo dispo-
di che portano il modulo in conflitto. Per effettuare sitivo remoto trovato. Sono inoltre possibili altre
questo reset , occorre portare verso ON il relati- modalità Master che variano dal modo più o meno

120 Dicembre 2012 / Gennaio 2013 ~ Elettronica In


Fig. 5
particolare di comportarsi; che nell’altra, durante la fase
queste si possono settare solo di connessione.
da software tramite il comando
SM,<x>, in cui x rappresenta CODICE ARDUINO
la modalità e varia da 0 a 5. In A questo punto siamo pronti
particolare SM,0 farà ritornare per analizzare il codice per
il modulo in modalità slave. Arduino, che riceverà i dati
• BAUDRATE. Questo dip
CORSO

Bluetooth già convertiti in


è collegato al pin GPIO7 del seriale dal modulo esterno e
modulo Bluetooth. Abilitando- li tradurrà in azioni, ovvero
lo, viene forzata la velocità di cambierà tonalità alla nostra
comunicazione per dati e co- barra a LED. Questi dati
mandi a 9.600 bps. Altrimenti Bluetooth altro non sono che
verrà utilizzata la velocità desi- i messaggi creati in prece-
derata che avremo precedente- denza (esempio “R200\n”
mente scritto in Flash tramite il per portare la componente
comando SU, <rate> (esempio: del rosso ad un valore 200) e
SU,57.6 per selezionare 57.600 che arriveranno ad Arduino
baud). Per impostazione sul piedino RX della seriale.
predefinita, questo valore è Arduino piloterà la barra a
pari a 115.200 baud. Tramite LED tramite uno shield RGB
software è possibile impostare (già presentato nel fascicolo
anche velocità di trasferimento n° 159) il cui schema elettrico
non standard. è visualizzato nella Fig. 5;
lo shield viene alimentato a
Notate che disponendo di due 12V ed utilizza tre MOSFET
shield è possibile metterli in di potenza pilotati dai
comunicazione punto-punto piedini analogici 3, 5 e 6 di
tra di loro per permettere Arduino.
comunicazione tra due PC (o altri dispositivi) Nella fase di setup del codice Arduino, inizializ-
evitando l’uso di un cavo seriale. In questo caso zeremo i piedini 3,5 e 6 come output e configure-
uno dei due dovrà comportarsi come master remo la seriale con le stesse proprietà e velocità
(spostando il relativo dipswitch) e l’altro rimarrà del modulo Bluetooth al quale Arduino è colle-
slave. Su entrambi dovrà essere settato il dip- gato. Il modulo in questione ha una velocità di
switch AUTO in modo da non richiedere codice comunicazione predefinita di 115.200 bps, ma è
di autenticazione. possibile configurarlo con altri valori. Addirittura
Bene, passiamo adesso al LED di stato LD1 (ros- può funzionare a velocità non consentite da una
so) collegato al pin GPIO5 del modulo Bluetooth: normale seriale RS232 da PC, come ad esempio 1
all’avvio, nella modalità slave, lampeggia ad una Mbps. Noi lo abbiamo configurato a 9.600 bps.
frequenza di circa 1 Hz per indicare che è in atte- È bene ricordare che utilizzando uno di questi
sa di essere rilevato e di ricevere la connessione. moduli Bluetooth esterni, andremo ad occupare
Quando si entra in modalità comando lampeggia interamente la seriale di Arduino e ci mancherà
a frequenza maggiore (circa 10 Hz). Una volta un possibile feedback che risulta utile in fase di
effettuata la connessione con un dispositivo test e di debug per verificare lo stato di funzio-
remoto, rimane accesso. namento del nostro codice.
Il secondo LED presente nel circuito (LD2, verde) In questo caso possiamo sempre ricorrere ad
collegato al pin GPIO2 del modulo Bluetooth, una libreria seriale esterna (SoftwareSerial) che ci
rimane sempre acceso e si spegne solamente permette emulare la porta seriale su due piedini
durante la fase di connessione. Il terzo diodo qualsiasi di Arduino e perciò di tracciare tracciare
luminoso (LD3, di colore giallo) è invece connes- e risolvere eventuali problemi.
so al pin GPIO8 del modulo Bluetooth ed indica, Infine, sempre nella fase di setup, faremo entrare
pulsando, il passaggio di dati sia in una direzione il modulo Bluetooth nella modalita’ comando,

Elettronica In ~ Dicembre 2012 / Gennaio 2013 121


CORSO
Listato 5
void setup()
{
pinMode(RED_LED, OUTPUT);
pinMode(BLUE_LED, OUTPUT); possibile visualizzare questa parte di codice. Ora
pinMode(GREEN_LED, OUTPUT);
pinMode(BOARD_LED, OUTPUT); saremo in grado di controllare la luminosità della
barra tramite la nostra applicazione Android;
digitalWrite(GREEN_LED, HIGH);
digitalWrite(BLUE_LED, HIGH); vediamo come. Nella funzione loop(), Arduino
digitalWrite(RED_LED, HIGH); rimarrà in attesa di ricevere tutti i dati seriali ed
Serial.begin(115200); isolerà le stringhe che terminano col carattere ‘\n’
mySerialDbg.begin(9600); tramite la nostra funzione getSerialLine(). A questo
delay(500);
mySerialDbg.println(“Test RGB COntroller”); punto la singola stringa di messaggio verrà
parserizzata dalle funzioni getColorFromString()
delay(1000);
e getComponentFromString(), create per ricevere
// enter in command mode le informazioni su quale componente di colore
Serial.println(“$$$”);
delay(1000); settare e a quale determinato valore. Ora possia-
mo pilotare i piedini di uscita con la funzione di
// force to slave mode
Serial.println(“SM,0”); sistema analogWrite() che imposterà un segnale
delay(3000); PWM sul piedino corrispondente. Tutto questo è
//enter in data mode mostrato nel Listato 6.
Serial.println(“---”);
delay(2000);
CONCLUSIONI
lampLed(2); In queste pagine abbiamo descritto come rea-
digitalWrite(GREEN_LED, LOW); lizzare un’applicazione Android minimale che
digitalWrite(BLUE_LED, LOW); possiamo già usare; tuttavia per dotarla di funzio-
digitalWrite(RED_LED, LOW);
} nalità aggiuntive che la rendano più utilizzabile
e soprattutto che ci permettano di ampliare le
tramite l’invio della sequenza di escape su seriale nostre conoscenze sullo sviluppo Android, man-
e lo forzeremo in modalità slave, per poi farlo cano ancora alcune cose. Nella prossima puntata
tornare alla fase di scambio dati. Per tutta la fase vedremo come inserire un menu, in che modo
di setup abbiamo mantenuto i LED di tutti e tre creare più di una activity e come utilizzare l’og-
i colori accessi (quindi la barra a LED risulterà getto ListView; in questo modo doteremo la no-
di colore bianco); al termine, prima di passare stra applicazione di un datalogger Bluetooth con
al loop infinito, spegneremo tutti i LED (tramite ricerca automatica dei dispositivi remoti attivi e la
funzione digitalWrite()) in modo da segnalare l’av- possibilità di impostare le variazioni della tonalità
venuto passaggio nella fase dati. Nel Listato 5 è della barra a LED tramite un temporizzatore. 

Listato 6
Lo shield Bluetooth descritto in questo
void loop()
{ progetto (cod. FT1032M) costa 34,00 Euro;
String strRec = getSerialLine();
per il MATERIALE
la scatola di montaggio comprende tutti i
digitalWrite(BOARD_LED, HIGH);
componenti, il modulo radio Roving Networks
Serial.println(“Rec:” + strRec + “\n”); RN-42, la basetta forata e serigrafata nonché
char colComponent = getComponentFromString(strRec); tutte le minuterie. Nello stesso progetto
int colValue = getColorFromString(strRec);
viene utilizzato un shield RGB (cod. RGB_
if(colComponent == ‘R’)
{
SHIELD) che costa 12,00 Euro nonché una
analogWrite(RED_LED, colValue); board Arduino UNO, Euro 24,50. Il modulo
}
else if(colComponent == ‘G’) Roving Networks RN-42 è anche disponibile
{
analogWrite(GREEN_LED, colValue); separatamente al prezzo di 21,00 Euro.
}
else if(colComponent == ‘B’)
Tutti i prezzi si intendono IVA compresa.
{
analogWrite(BLUE_LED, colValue); Il materiale va richiesto a:
} Futura Elettronica, Via Adige 11, 21013 Gallarate (VA)
}
Tel: 0331-799775 • Fax: 0331-792287 - www.futurashop.it

122 Dicembre 2012 / Gennaio 2013 ~ Elettronica In


Riprendiamo lo
shield Bluetooth
per completare la
nostra applicazione
dotandola di ulteriori
caratteristiche
CORSO

e funzionalita e

approfittando per
introdurre nuovi
concetti Android.
Quinta puntata.

An
di
dr
Ch
ea

po
iap
ri
5
PROGRAMMIAMO CON
ella puntata precedente abbiamo ana-
N lizzato il firmware per Android e per
Arduino che permette di variare la lumino-
CREARE IL MENU
Per non appesantire ulteriormente il layout grafico
della schermata principale, gestiremo la possibilità
sità ed il colore di una striscia a LED RGB ad di programmare questa sequenza in una View de-
interfaccia I²C-Bus. Immaginiamo ora di volere dicata, che sarà pertanto richiamabile tramite una
che la nostra striscia (o anche un singolo LED voce di menu. Come saprete, uno dei tasti “fisici”
RGB) possa passare da un colore all’altro ad in uno smartphone Android è quello che permette
intervalli prestabiliti ed in tempi più o meno di mostrare nella parte bassa dello schermo un
rapidi; questo potrebbe essere utile, ad esem- menu contestuale con più opzioni, in modo da
pio, per creare effetti alba-tramonto in pre- arricchire l’applicazione con maggiori informazio-
sepi o modellini, senza dover ricorrere a più ni o configurazioni. Esistono due modi per creare
costose centraline dedicate. Dovremo perciò i menu: uno “statico”, che consiste nel ricorrere
aggiungere alla nostra applicazione una moda- a un file xml, e l’altro dinamico, in cui si possono
lità di programmazione tramite cui scegliere la aggiungere o togliere voci di menu direttamente
sequenza di colori che invieremo ad Arduino dal codice. Sono modi equivalenti nel risultato, ma
tramite Bluetooth. Quest’ultimo memorizzerà i quello dinamico ci permette di modificare le opzio-
dati nella propria EEPROM, così sarà in grado ni che compariranno nel menu anche a run-time, a
di riprodurre la sequenza anche una volta che seconda di determinate azioni compiute dall’uten-
la connessione Bluetooth sarà terminata. te. Vedremo comunque entrambe le soluzioni.

Elettronica In ~ Febbraio 2013 147


CORSO
Fig. 1

uso solamente di lettere minuscole.


A questo punto abbiamo il nostro menu, ma
è ancora “scollegato” e non visibile nella no-
stra applicazione. Per farlo comparire sullo
schermo ogni qualvolta venga premuto il ta-
sto “fisico” di menu, occorre scrivere ancora
le poche righe di codice mostrate nel Listato
1, attraverso le quali istruiremo Android
affinché possa utilizzare il file xml preceden-
Innanzi tutto dovremo aggiungere i seguenti temente creato, specificando il percorso della
import nel nostro codice java, per poter uti- risorsa che sarà, appunto, R.layout.menuseq.
lizzare le librerie interne adibite alla gestione Anche in questo caso menuseq è il nome del
del menu. file xml privo della sua estensione e presente
all’interno della cartella layout.
import android.view.Menu; Il risultato che otterremo è visibile in Fig. 2.
import android.view.MenuItem; Possiamo anche ottenere lo stesso risultato
import android.view.MenuInflater; senza scrivere alcun file xml, ma modifican-
do la funzione onCreateOptionsMenu() vista
A questo punto possiamo riprendere il co- prima, come mostrato nel Listato 2.
dice della puntata precedente ed all’interno
dell’Activity principale (class BtClientActivi-
ty) ridefinire la classe onCreateOptionMenu()
in modo da poter gestire un menu. Ogni
volta che l’applicazione ridisegnerà la View
principale verrà chiamata questa funzione;
sarà proprio in questo punto che andremo a
scrivere il nostro codice.
Nel caso come quello in oggetto, in cui sap-
piamo che le voci del menu saranno sempre
le stesse, possiamo scrivere un file xml come
quello visibile in Fig. 1, che salveremo nella
cartella layout. Come si può notare, ogni tag
<item> rappresenta una voce di menu e ad
ogni item possiamo associare un Id univoco,
una stringa di testo (nel campo android:title)
Fig. 2
ed il percorso della relativa immagine (nel
campo android:icon) che deve essere presente Sebbene i due metodi siano equivalenti, se
nel progetto, normalmente all’interno della utilizzate lo stesso menu anche in altre even-
cartella drawable. tuali View vi consigliamo di usare il primo,
Ricordiamo inoltre, come già visto in prece- perché vi risparmia di ripetere lo stesso
denza nel caso di risorse grafiche, di non spe- codice più volte.
cificare l’estensione dell’icona (che nel nostro Inoltre, tramite il file xml è possibile aggiun-
caso è .png) nel nome del percorso e di fare gere con semplicità maggiori caratteristiche,
sfruttando il code-assist di Eclipse.
Qualunque metodo sia stato seguito, dob-
Listato 1 biamo ora associare la pressione della voce
@Override di menu scelta (detta anche MenuItem) con
public boolean onCreateOptionsMenu(Menu menu) la corrispondente azione da eseguire: que-
{
MenuInflater inflater = getMenuInflater(); sto viene realizzato ridefinendo la funzione
inflater.inflate(R.layout.menuseq, menu); onOptionsItemSelected() nella quale gestiremo
return true;
}
appunto il parametro item.
Tale funzione, esplicitata nel Listato 3, effet-

148 Febbraio 2013 ~ Elettronica In


Listato 2
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
menu.add(Menu.NONE, 0, 0, “Scan”).setIcon(getResources().getDrawable(R.drawable.icon_scan));
menu.add(Menu.NONE, 1, 1, “New Seq”).setIcon(getResources().getDrawable(R.drawable.icon_seq));
menu.add(Menu.NONE, 2, 2, “About”).setIcon(getResources().getDrawable(R.drawable.icon_
information));
return super.onCreateOptionsMenu(menu);
}
CORSO

tua un controllo sull’Id della voce di menu quisire nuovi concetti riguardanti il mondo
selezionata (che viene restituito dall’ API Android.
di sistema getItemId()); in base al suo valore
effettueremo le nostre azioni, chiamando le CREARE NUOVE ACTIVITY
rispettive funzioni. Finora in questa applicazione abbiamo utiliz-
Adesso bisogna fare una piccola precisazione zato una singola Activity; in prima istanza,
a proposito della funzione getItemId(), la quale possiamo considerare un’Activity come la
ritorna un valore intero: nel caso avessimo finestra grafica che permette all’utente di
utilizzato il metodo dinamico per creare il no- interagire con l’applicazione. Fisicamente
stro menu, tale valore corrisponderà a quello un’Activity è una classe contenuta in un file
specificato nel metodo Add (Listato 2) e quin- sorgente con estensione .java (nel nostro
di, nel nostro caso, il valore 0 corrisponderà
alla prima voce di menu (“Scan”), il valore
1 alla seconda e così via. Se invece avessimo Listato 3
creato il menu utilizzando il file xml menuseq. @Override
public boolean onOptionsItemSelected(MenuItem item)
xml, allora la lettura del codice risulterebbe {
semplificata per via della stringa inserita nel super.onOptionsItemSelected(item);
switch(item.getItemId())
campo android:id del relativo item (Fig. 1) che {
ci permette di riscrivere il codice del Listato case 0:
3, utilizzando il prefisso R seguito dal nome startScannerActivity();
break;
dell’Id. case 1:
Il tutto sarà più chiaro leggendo il codice openSequencerView();
break;
così trasformato, nel Listato 4. In particolare, case 2:
abbiamo utilizzato tre voci di menu: la prima openAboutDialog();
break;
per effettuare la scansione dei dispositivi }
Bluetooth circostanti, la seconda per creare le return true;
nostre sequenze di luce ed infine la terza per }

aggiungere ulteriori informazioni. È sempre


buona norma, infatti, dotare le nostre appli-
cazioni di un menu, sia per non appesantire
la View principale con troppi pulsanti, sia per Listato 4
poter aggiungere, oltre alle eventuali finestre @Override
public boolean onOptionsItemSelected(MenuItem item)
di opzione, anche una finestra di dialogo {
con le informazioni sulle nostre generalità, super.onOptionsItemSelected(item);
switch(item.getItemId())
il nostro logo o semplicemente la versione {
dell’applicazione. case R.id.scan:
A riguardo rimandiamo al riquadro “Finestre startScannerActivity();
break;
di dialogo Android” che trovate nella pagina case R.id.seq:
seguente, dove si utilizza la classe Android openSequencerView();
break;
AlertDialog per visualizzare le informazioni case R.id.abt:
aggiuntive accessibili con la terza voce del openAboutDialog();
break;
menu. }
Andiamo ora ad implementare le altre due return true;
voci di menu che ci permetteranno di ac- }

Elettronica In ~ Febbraio 2013 149


CORSO
FINESTRE DI DIALOGO ANDROID
Abbiamo già visto in precedenza come realizzare
un messaggio a comparsa tramite la classe
MessageToast, ma Android mette a disposizione anche Listato 5
finestre di dialogo più complesse e facilmente gestibili, private void openSequencerView()
che possiamo utilizzare per visualizzare informazioni {
veloci come quelle in una classica finestra di “About”. Intent sequencerIntent = new Intent(this, BtSequencerActivity.class);
startActivity(sequencerIntent);
Si tratta della classe AlertDialog, la quale con pochi }
metodi, come quelli elencati qui di seguito, permette
di ottenere il risultato di Fig. 3 senza costringerci ad
utilizzare un’altra Activity. visto come un messaggio col quale riusciamo
ad ordinare ad Android l’esecuzione di una
AlertDialog dlg = new AlertDialog.Builder(this).create();
dlg.setTitle(“RGB Controller”); generica azione di sistema.
dlg.setMessage(“Elettronica In\n Copyright ® 2012”); In questo caso dovremo creare un nuovo
dlg.setIcon(R.drawable.icon_main); Intent passandogli l’informazione sulla
dlg.show(); nuova classe Activity ed utilizzare il meto-
do startActivity() al quale passeremo come
Fig. 3
argomento proprio l’Intent. Tutto questo è
visibile nel Listato 5, in cui viene implemen-
tata la funzione chiamata dopo aver premuto
la seconda voce del menu (New Seq).
L’Activity che andremo a creare sarà una
classe estesa della super classe Activity e
pertanto dovremo obbligatoriamente imple-
mentare i metodi ereditati da essa. In Fig. 4 è
possibile vedere un template minimale della
nostra Activity in cui i metodi, che rappre-
Inoltre questa finestra verrà correttamente “distrutta”
sentano il passaggio tra i suoi vari stati e
semplicemente premendo il tasto fisico “Back” sul
dispositivo Android, senza doverci preoccupare di quindi il suo ciclo di vita, non fanno altro
dotarla di un pulsante di uscita. (almeno per ora) che richiamare le funziona-
lità della sua super classe.

caso, BtSequencerActivity.java) che andremo


a creare all’interno della cartella src, al pari
degli altri file sorgente. Quando l’applica-
zione richiede particolari configurazioni o
funzionalità aggiuntive, è meglio servirsi di
nuove Activity per alleggerire la complessità,
distribuendola su più finestre.
La principale differenza che troviamo rispet-
to al mondo Windows o Linux, al quale sia-
mo più abituati, è che può essere attiva una
sola Activity alla volta; nel caso di due o più
Activity presenti nella nostra applicazione,
se una di esse è in primo piano, le altre, oltre
a non essere visibili, vengono configurate
da Android in uno stato di secondo piano
(o background) e, continuando a mantenere il
loro ciclo di vita, potranno riprendere il loro
stato di attività in futuro.
Senza scendere troppo nei dettagli, vediamo
come poter lanciare una seconda Activity da Fig. 4
quella principale. Occorre a questo punto in-
trodurre il concetto di Intent, che può essere

150 Febbraio 2013 ~ Elettronica In


CORSO

Fig. 5

Tutti questi metodi (onCreate(), onStart(), tivity sta prendendo il suo posto per andare
onResume(), onPause(), onStop(), onDestroy()) in foreground, ed il metodo onStop() quando
vengono chiamati direttamente dal sistema proprio l’Activity non è più visibile. Infine, il
operativo e noi possiamo gestirli in modo at- metodo onRestart() è chiamato ogni qualvolta
tivo inserendo codice personalizzato a secon- l’Activity venga ridisegnata ed il metodo on-
da delle nostre esigenze, oppure demandare Destroy() appena prima che l’Activity venga
tutto alla super classe tramite, ad esempio, la distrutta.
riga di codice super.onStop() valida nel caso Per poter utilizzare l’Activity appena creata,
del metodo onStop(). non dobbiamo dimenticarci di informare
Possiamo spiegare brevemente questi meto- Android della sua esistenza; allo scopo la
di dicendo che la onCreate() viene chiamata aggiungiamo nel file AndroidManifest.xml,
una sola volta, non appena viene eseguita la già visto ed utilizzato nel corso delle puntate
startActivity() vista precedentemente. È pro- precedenti, che si presenterà come in Fig. 5.
prio qui che andremo ad impostare il layout Da notare il tag <intent-filter> che deve essere
grafico scelto per questa Activity (tramite la presente solo nell’ Activity principale, con
riga setContentView(R.layout.sequencer.xml) e la quale parte l’applicazione e dalla quale
ad aggiungere il nostro codice per associare verranno lanciate le altre Activity.
le azioni agli eventi generati dalla pressione Spiegate le generalità, vediamo ora come
di tasti e controlli generici. useremo le Activity in questa puntata.
Il metodo onStart() viene chiamato anch’esso
una sola volta e poco prima che l’Activity LIGHT SEQUENCER
venga fisicamente disegnata. Il metodo onRe- Premendo sulla voce di menu New Seq pre-
sume() viene chiamato prima che l’Activity cedentemente creata, apparirà ora una nuova
passi in stato running, quindi verrà chiamato finestra con i controlli che abbiamo aggiun-
la prima volta, ma anche ogni volta che l’Ac- to nel nuovo file di layout sequencer.xml. e
tivity esca dallo stato stopped e torni attiva. visibile in Fig. 6. Come noterete, sono gli
Il metodo onPause() viene chiamato quando stessi controlli usati nell’Activity principale,
l’Activity è ancora visibile, ma un’altra Ac- ma oltre a questi ci sono anche altri pulsanti

Elettronica In ~ Febbraio 2013 151


CORSO
Listato 6
<SeekBar
android:id=”@+id/seekbarB”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
android:max=”255”
android:progress=”0”
android:progressDrawable=”@drawable/custom_seek_bar”
android:thumb=”@drawable/custom_seek_bar_thumb”
android:thumbOffset=”0dip”
android:maxHeight=”10dip”
android:layout_weight=”80”
/

Fig. 6

per consentire maggiori funzionalità. L’idea R000 G255 B000 T40


è quella di creare una sequenza di colori R000 G000 B255 T50
direttamente sul nostro smartphone, quindi
inviarla ad Arduino sotto forma di strin- Dall’altra parte Arduino, dovrà essere in gra-
ga Bluetooth. Abbiamo inserito anche una do di parserizzare le stringhe correttamente,
ulteriore SeekBar attraverso la quale settare il salvare i valori in un array, memorizzarli
tempo (in secondi) in cui resterà attivo il co- internamente e utilizzarli quando, dalla no-
lore scelto. Cliccando poi sul pulsante “Next stra applicazione Android, riceverà la stringa
Color” il colore attivo ed il relativo intervallo “P0”.
verranno memorizzati in una lista di stringhe
e potremo scegliere un altro colore col suo CONTROLLI PERSONALIZZATI
relativo intervallo. Una volta terminata la Non ci addentreremo nel codice dell’Activi-
nostra sequenza possiamo premere il pulsan- ty BtSequencerActivity perché molto simile a
te “Stop Seq” e successivamente il pulsante quello dell’Activity principale già vista, ma
“Play Seq” per riprodurla mediante l’RGB possiamo approfittare dell’occasione per darvi
shield, oppure “Send Seq” per inviarla ad alcuni suggerimenti su come personalizzare le
Arduino, che la memorizzerà sulla propria vostre applicazioni creando controlli custom
EEPROM. A questo punto, premendo “Play che presentino un’ interfaccia grafica a vostro
Seq” nella finestra principale la sequenza piacimento, un po’ diversa da quelle classiche.
verrà rieseguita con un loop infinito anche Prendiamo, ad esempio, le quattro SeekBar
dopo aver terminato l’applicazione sullo inserite nel layout in Fig. 6 ed immaginia-
smartphone. Per l’invio della sequenza pos- mo di volerne cambiare alcuni parametri
siamo inventarci un protocollo di messaggi estetici come la forma del cursore o il colo-
come preferiamo; sapendo che non abbiamo re di riempimento della barra. Questa, per
vincoli di tempo stringenti utilizzeremo sem- impostazione predefinita è arancione, ma nel
plicemente l’invio di stringhe. nostro caso sarebbe interessante fosse rossa
Sulla pressione del pulsante “Send Seq” per la SeekBar relativa alla componente rossa,
abbiamo, così,scelto di inviare il carattere ‘S’ verde per la componente verde e blu per
seguito dal numero totale di colori e succes- la componente blu. Android ci offre questa
sivamente le informazioni sulle componenti
di colore e l’intervallo di tempo tramite i
Listato 7
caratteri R, G,B e T.
<selector xmlns:android=”http://schemas.android.com/
Se, ad esempio, volessimo una sequenza apk/res/android”>
che inizi con un colore rosso per poi passare
dopo 30 secondi ad un verde e dopo 40 al <item android:state_pressed=”true”
android:drawable=”@drawable/custom_thumb_state_pressed” />
blu, e desiderassimo rieseguire il loop dopo
50 secondi, invieremo un messaggio di que- <item android:state_focused=”true”
android:drawable=”@drawable/custom_thumb_state_selected” />
sto tipo: <item android:state_selected=”true”
android:drawable=”@drawable/custom_thumb_state_selected” />
<item android:drawable=”@drawable/custom_thumb_state_default” />
S3 </selector>
R255 G000 B000 T30

152 Febbraio 2013 ~ Elettronica In


barra a sinistra del cursore (progress). Ognu-
no di questi item è personalizzabile tramite
una serie di tag, come corners per definire gli
angoli della barra, solid per definire un colore
di sfondo pieno, piuttosto che gradient per
definire l’effetto di sfumatura desiderato.
Allo stesso modo possiamo modificare la
CORSO

forma e il colore del cursore, aggiungendo il


campo android:thumb ed impostandolo un file
di risorsa come quello nel Listato 7, che a sua
volta presenta collegamenti ad altri file di
risorsa in base ai differenti stati del cursore
(pressed, focused, selected).
Possiamo allora sbizzarrirci su forma, colore
ed altri effetti, creando tanti file xml o imma-
Fig. 7
gini per costruire tre SeekBar di colori diversi
e con caratteristiche estetiche differenti, come
si può vedere in Fig. 8.
A questo punto, sulla base delle informazio-
ni viste prima, potremmo anche creare un
nostro controllo e dargli un nome a nostra
scelta, semplicemente creando un nuovo file
xml (non importa il nome del file, basta che
sia presente il tag <resources>) e che andremo
a mettere all’interno della cartella values. Un
file di questo tipo è visibile nel Listato 8.
Ora nel nostro file di layout potremmo usare
direttamente questo controllo usando il tag
Fig. 8 con lo stesso nome che gli abbiamo dato in

Listato 8
<?xml version=”1.0” encoding=”UTF-8”?>
<resources>
<style name=”CustomSeekBar” parent=”android:Widget.SeekBar”>
<item name=”android:progressDrawable”>@drawable/custom_seek_bar_r</item>
<item name=”android:thumb”>@drawable/custom_seek_bar_thumb</item>
<item name=”android:thumbOffset”>0dip</item>
</style>
</resources>

possibilità in modo tutto sommato semplice: precedenza (nel nostro caso <CustomSeek-
riprendiamo il file di layout sequencer.xml Bar>) preoccupandoci solo delle dimensioni
ed aggiungiamo all’interno del tag <Seek- e della sua posizione all’interno della View.
Bar> che ci interessa modificare, il campo
android:progressDrawable, che imposteremo SCANSIONE BLUETOOTH
con un file di risorsa personalizzato (vedere Negli esempi fatti fino ad ora la comuni-
il Listato 6). cazione Bluetooth tra Android ed Arduino
Questo file di risorsa (@drawable/custom_seek_ è avvenuta cablando nel codice l’indirizzo
bar_R) è un file xml (nulla, però, ci vieta di MAC del modulo RN-42; ciò rende l’applica-
usare un’immagine) come quello visibile in zione poco flessibile e nel caso in cui volessi-
Fig. 7 in cui sono presenti due item: uno per mo riutilizzare questo stesso codice per gesti-
ridisegnare lo sfondo della barra di scorri- re un altro modulo Bluetooth ci troveremmo
mento (background) e l’altro per ridisegnare la fortemente vincolati.

Elettronica In ~ Febbraio 2013 153


CORSO
Ora che abbiamo introdotto il menu, possia- re appunto i thread, implementando il metodo
mo lanciare un’Activity dedicata che gestisca run() di questa classe.
la ricerca dei dispositivi remoti nelle vicinan- Infine troviamo i due IntentFilter, discove-
ze e restituisca il MAC Address del dispositi- ryFilter e foundFilter (visibili nel Listato 10)
vo selezionato. La prima voce di menu, “Scan che sono i responsabili della rilevazione dei
Device” fa proprio questo. Essendo un’Ac- dispositivi Bluetooth. Un IntentFilter può
tivity da cui vogliamo ottenere un risultato essere visto come una sorta di direttiva con
che utilizzeremo nell’Activity principale, cui informiamo il sistema operativo su quali
dovremmo lanciarla in un modo diverso dal Intent (messaggi provenienti dal sistema ope-
precedente e quindi, anziché utilizzare il rativo) siamo in grado di gestire e con quale
metodo startActivity(), useremo startActivity- oggetto di classe.
ForResult() (Listato 9). Nel nostro caso l’Intent foundFilter sarà attivo
Sempre nell’Activity principale, dovremo ge- sulla ricezione del messaggio Bluetooth
stire questo risultato quando il sistema ope- ACTION_FOUND, e quindi ogni qual volta
rativo chiamerà il metodo onActivityResult() venga rilevato un nuovo dispositivo questo
(vedere ancora il Listato 9) in cui ricaviamo il sarà gestito nel metodo onReceive() dell’ ogget-
MAC Address dal parametro data. to _foundReceiver, dal momento che, tramite il
Analizziamo ora la classe BtScannerActivi- metodo registerReceiver(), abbiamo associato
ty che ha poche righe di codice ma è molto l’intent a questo oggetto. Nel metodo onRecei-
particolare. ve() inseriremo il nuovo dispositivo nella lista
Nel metodo onCreate() inseriremo il solito file grafica, che così verrà continuamente aggior-
di layout discovery.xml, che questa volta avrà nata (ShowDevices()).
un controllo nuovo: la lista ListView, in cui Per far terminare correttamente il thread e
verranno elencati i dispositivi rilevati. Sempre quindi la scansione, useremo proprio il secon-
nella onCreate(), troviamo la creazione dina- do IntentFilter (discoveryFilt er) che, invece, ge-
mica di un controllo ProgressScan (il circoletto stirà il messaggio Bluetooth di fine scansione
rotante che in Android indica l’attesa dell’ese- (ACTION_DISCOVERY_FINISHED) facendo
cuzione di un’operazione) attraverso la quale terminare il thread _discoveryWorker e lascian-
viene lanciato un thread parallelo (_discove- do all’utente la scelta del dispositivo.
ryWorker) che effettua la scansione Bluetooth A questo punto il controllo passerà alla on-
vera e propria. ListItemClick() (sempre nel Listato 10) in cui
Precisiamo che _discoveryWorker è un oggetto saremo noi a creare un messaggio (Intent) con
della classe base Runnable creato nello stesso il MAC Address del dispositivo scelto, che
file e che in Android viene utilizzato per gesti- restituiremo all’Activity principale.

Listato 9
public void startScannerActivity()
{
Log.v(TAG, “Start Scan”);
Intent btScanIntent = new Intent(this, BtScannerActivity.class);
startActivityForResult(btScanIntent, BtCore.REQUEST_DISCOVERY);
}
// after select, connect to device
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
if (requestCode != BtCore.REQUEST_DISCOVERY)
{
return;
}
if (resultCode != RESULT_OK)
{
return;
}
BluetoothDevice device = data.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
BtCore.serverAddressAfterScan = device.getAddress();
}

154 Febbraio 2013 ~ Elettronica In


Listato 10
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND,WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
setContentView(R.layout.discovery);
Log.w(“RGB-CONTR-SCAN”, “onCreate”);

// BT isEnable
if (!_bluetooth.isEnabled())
{
Log.w(“RGB-CONTR-SCAN”, “Disable!”);
CORSO

finish();
return;
}
// Register Receiver
IntentFilter discoveryFilter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
registerReceiver(_discoveryReceiver, discoveryFilter);
IntentFilter foundFilter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
registerReceiver(_foundReceiver, foundFilter);
ProgressScan.indeterminateInternal(BtScannerActivity.this, _handler, “Scanning...”, _discoveryWorker,
new OnDismissListener()
{
public void onDismiss(DialogInterface dialog)
{
for (; _bluetooth.isDiscovering();)
{
_bluetooth.cancelDiscovery();
}
_discoveryFinished = true;
}
}, true);
}
private Runnable _discoveryWorker = new Runnable()
{
public void run()
{
// Start search device
_bluetooth.startDiscovery();
Log.d(“RGB-CONTR-SCAN”, “Starting Discovery”);
for (;;)
{
if (_discoveryFinished)
{
Log.d(“RGB-CONTR-SCAN”, “Finished”);
break;
}
try
{
Thread.sleep(100);
}
catch (InterruptedException e)
{
}
}
}
};
protected void onListItemClick(ListView l, View v, int position, long id)
{
Log.d(“RGB-CONTR-SCAN”, “Click device”);
Intent result = new Intent();
result.putExtra(BluetoothDevice.EXTRA_DEVICE, _devices.get(position));
setResult(RESULT_OK, result);
finish();
}

CONCLUSIONI zare anche nei nostri prossimi progetti. Durante


Abbiamo completato la nostra applicazione le puntate seguenti analizzeremo come gestire
Bluetooth rendendola più completa e funzionale da un’applicazione Android, la ricezione e
ed acquisendo alcuni concetti Android (Intent, l’invio di messaggi e chiamate telefoniche verso
g
FilterIntent, Runnable) che ci troveremo ad utiliz- dispositivi remoti dotati di modulo cellulare.

Elettronica In ~ Febbraio 2013 155


Presentiamo una
nuova applicazione
in grado di gestire
chiamate GSm ed
SMS, affrontiamo
la creazione di un
CORSO

servizio Android
ed approfondiamo
l’utilizzo di alcuni
sensori del nostro
smartphone, come
accelerometro e
localizzatore GPS.

ri
6
Sesta puntata.

po
iap
Ch
ea
dr
An
di

PROGRAMMIAMO CON
opo aver spiegato l’interazione tra il modu- sa impostabile (Fig. 1). Questo ci dà l’occasione di
D lo Bluetooth RN-42 e Android, ora vedremo
come gestire direttamente chiamate e messaggi
fare una panoramica sui sensori di movimento e sul
localizzatore GPS di Android, ma anche di introdurre
GSM e in che modo interagire con i sensori di un nuovo concetto Android che è il “servizio”, il quale
movimento e localizzazione dello smartphone ci risparmia di dover tenere sempre attiva e a pieno
allo scopo di pilotare lo shield Arduino GSM schermo la nostra applicazione durante il suo ciclo di
presentato nelle puntate precedenti. In parti- vita. Anche se questa applicazione nasce per scopo
colare, dopo questa lezione saremo in grado di didattico, potrà comunque tornarci utile per realizzare
attivare dispositivi esterni collegati allo shield, automatismi comandati a distanza, come ad esempio
tramite una chiamata gestita internamente dalla un apricancello o un avviso di presenza.
nostra applicazione, ed impartire diversi coman-
di grazie ad SMS personalizzati. SERVIZIO ANDROID
Potremo dotare la nostra applicazione di mag- Mentre nell’applicazione descritta nel fascicolo prece-
giori funzionalità: ad esempio far partire una dente (RGBLedController) abbiamo sempre avuto a che
chiamata verso un numero telefonico prestabilito fare con le Activity, che sono classi dotate di un’inter-
in seguito ad uno “scuotimento” ripetuto dello faccia grafica verso l’utente e con le quali è possibile
smartphone, oppure appena questo si troverà in interagire attivamente, questa volta occorre fare in
prossimità di una certa zona geografica, anch’es- modo che il nostro codice funzioni in background,

Elettronica In ~ Marzo 2013 141


CORSO
Fig. 1

con cui potremo lanciare e arrestare il servizio


dall’Activity.
Come visibile in Fig. 2, per avviare il servizio
occorre creare, sempre nell’Activity principale,
un Intent al quale passeremo il nome della no-
stra classe GSMService (che poi è lo stesso nome
del file GSMService.java) e che aggiungeremo al
progetto nella sottocartella src.
permettendoci di utilizzare il nostro dispositi- Successivamente chiameremo il metodo startSer-
vo normalmente, ma rimanendo comunque in vice() passandogli come parametro l’Intent appe-
attesa di alcuni eventi esterni (come segnali da na creato. Analogamente, per fermare il servizio
sensori di movimento, luminosità, GPS ed altri) useremo il metodo stopService() usando sempre
ai quali esso reagirà di conseguenza. lo stesso Intent.
Per fare ciò useremo appunto un servizio, che in Soffermiamoci ora sulla scrittura del nostro
Android altro non è che una classe (e quindi un servizio, ovvero del file GSMService.java.
file) estesa della classe base Service. Per facilitare la comprensione abbiamo aggiunto
Prima di addentrarci nella creazione del nostro i messaggi Toast all’ingresso delle funzioni che
primo servizio, creiamo un nuovo progetto gestiscono il servizio e che ci segnalano il ciclo
seguendo gli stessi passi delle puntate prece- di vita del servizio.
denti, anche perché la prima classe con cui viene In Fig. 3 è mostrato lo scheletro di un servizio
lanciata l’applicazione sarà sempre una Activity che ora andremo a completare secondo le nostre
(quindi dotata di interfaccia grafica) attraver- esigenze. In seguito al lancio del servizio tramite
so la quale potremo avviare il servizio vero e startService(), questo viene prima creato e subito
proprio, arrestarlo e soprattutto configurarlo a dopo avviato. In queste fasi (rispettivamente
nostro piacimento, ad esempio per cambiare i metodo onCreate() e onStart() ) inseriremo il co-
numeri di telefono di destinazione, la sensibilità dice per permettere lo svolgimento delle azioni
dei sensori ed altre opzioni che vedremo più che vogliamo siano eseguite in background.
avanti. Solitamente andremo a registrare alcune parti-
Quindi, inizialmente aggiungeremo due sem- colari classi dette “Listener” che si metteranno
plici pulsanti Start e Stop con cui avvieremo e in ascolto di particolari eventi del sistema opera-
arresteremo il servizio, ed una TextView che ci tivo e che vedremo in seguito.
indicherà lo stato del servizio (Fig.1). Il metodo onDestroy() viene chiamato quando il
Senza scendere nei dettagli, assoceremo alla servizio termina in seguito alla chiamata del me-
pressione di due pulsanti le funzioni di sistema todo stopService() dell’Activity, oppure a quella
Fig. 2 Fig. 3

142 Marzo 2013 ~ Elettronica In per iPad


Fig. 4

del metodo stopSelf() interno alla classe Service; premendo il pulsante


in questa fase vengono anche deregistrate le Stop. Per verificare
classi Listener. lo stato del servizio,
Infine il metodo onBind() nel nostro caso ritorne- useremo la casella di
rà sempre valore nullo, perché in questa appli- testo textServiceStatu-
cazione non gestiremo servizi di tipo Bounded sInfo.
nei quali un’altra Activity può interfacciarsi al Un primo layout
CORSO

servizio. (main.xml) dell’appli-


Possiamo ora aggiungere alla nostra classe, cazione è mostrato in
anche un nostro metodo privato getStatusServi- Fig. 4.
ce() attraverso il quale controlleremo lo stato del Prima di continuare
servizio (attivo o arrestato) all’avvio dell’appli- può essere utile una
cazione: si tratterà semplicemente di settare una breve precisazione
nostra variabile interna a true nella fase di Start e sulla gestione della
a false nella fase di Destroy. RAM in un sistema
È bene, infine, ricordare che un servizio, in Android: quando
quanto tale, può essere attivo anche se l’Activi- chiudiamo una qualsiasi applicazione Android,
ty principale della nostra applicazione è stata questa in realtà continuerà a rimanere in memo-
chiusa; esso continuerà a funzionare e lo potre- ria fino a quando una successiva applicazione
mo arrestare riaprendo la nostra applicazione e richiederà una quantità di memoria superiore

Listato 1
// GSMService.java

@Override
public void onCreate()
{
Toast.makeText(this, “Service Created”, Toast.LENGTH_LONG).show();
Log.d(TAG, “onCreate”);

// sensor
_sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
Sensor s = _sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);

_sensorManager.registerListener(_accelerometerSensorListener, s, SensorManager.SENSOR_DELAY_NORMAL);

}

@Override
public void onStart(Intent intent, int startid)
{
Toast.makeText(this, “Service Started”, Toast.LENGTH_LONG).show();
Log.d(TAG, “onStart”);

_statusService = true;

}

@Override
public void onDestroy()
{
Toast.makeText(this, “Service Stopped”, Toast.LENGTH_LONG).show();
Log.d(TAG, “onDestroy”);

_sensorManager.unregisterListener(_accelerometerSensorListener);
_sensorManager = null;

_statusService = false;

Elettronica In ~ Marzo 2013 143


CORSO
Fig. 5

dal market Android (Google Play) o già presenti


nel dispositivo, che sono in grado di monitorare
tutti i servizi attivi sul tablet o smartphone.

GESTIONE DEI SENSORI ANDROID


Andiamo ora a “riempire” il servizio creato, fa-
cendo in modo che reagisca ad eventi di sistema
come l’attivazione di sensori presenti nel nostro
dispositivo. Per capire quanti e quali sensori
sono presenti nel nostro smartphone, inseria-
mo le seguenti linee, per esempio, nel metodo
onCreate() dell’Activity principale:
SensorManager manager = (SensorManager)
getSystemService(SENSOR_SERVICE);
List<Sensor> list = manager.getSensorList(Sensor.
TYPE_ALL);
a quella rimasta libera, oppure finché non sarà La classe SensorManager rappresenta il gestore
trascorso un certo periodo di tempo dall’ultimo “globale” di tutti i sensori presenti. Dall’oggetto
utilizzo. manager siamo in grado di ottenere un oggetto
Un servizio in background ha una priorità mi- appartenente alla classe Sensor che rappresenta
nore di un’Activity in primo piano, ma sempre il nostro sensore vero e proprio. Il metodo get-
maggiore di un’Activity che rimane in memo- SensorList() dell’oggetto manager ci restituisce il
ria dopo essere stata chiusa; in questo modo sensore desiderato.
siamo sicuri che, salvo condizioni di sistema Avendo usato l’identificativo TYPE_ALL per
estremamente particolari, difficilmente Android indicare tutti i dispositivi, il manager ci restituirà
interromperà il nostro servizio. la lista completa dei sensori presenti; se invece
Così come fatto in precedenza, quando abbiamo avessimo voluto cercare un solo sensore, avrem-
aggiunto altre Activity al progetto, dobbiamo mo potuto usare direttamente il tipo appropria-
informare il sistema dell’utilizzo della classe to, come ad esempio TYPE_PRESSURE nel caso
GSMService, aggiungendo la seguente istruzione del sensore di pressione.
al file AndroidManifest.xml: Nel nostro caso useremo il metodo getDefault-
<service android:enabled=”true” android name=”. Sensor() che restituisce direttamente il singolo
GSMService” /> oggetto Sensor corrispondente. Nel riquadro
Questa riga è fondamentale per la corretta “Sensori Android” che trovate in queste pagine
esecuzione del servizio, altrimenti anche se la potete vedere con quali tipi poter accedere a
compilazione del progetto avrà ugualmente tutti i sensori disponibili.
luogo, otterremo un errore a run-time. Di questi, nella nostra applicazione useremo
Il file AndroidManifest.xml completo è visibile l’accelerometro e quindi accederemo al nostro
nella Fig. 5. oggetto Sensor tramite il tipo TYPE_ACCELE-
A questo punto, dopo aver lanciato il nostro ser- ROMETER. Dall’oggetto Sensor siamo anche in
vizio dall’Activity principale, possiamo anche grado di ottenere alcune interessanti informa-
chiudere l’applicazione (tramite il pulsante fisi- zioni come la risoluzione, la potenza usata dal
co back) ed usare il nostro cellulare normalmen- sensore, il raggio d’azione ed altre funzionalità.
te, sapendo che potremo arrestarlo rilanciando Volendo ottenere tutti i dati dei sensori in
l’applicazione e premendo il pulsante Stop. modalità asincrona, senza perciò interrogarli a
Quando accederemo nuovamente all’applica- polling, occorrerà registrare un oggetto di classe
zione, la casella di testo ci informerà sullo stato Listener (_accelerometerSensorListener) attraverso
del servizio. il metodo registerListener() del nostro gestore
Adesso abbiamo un servizio, che tuttavia, per il SensorManager, come si vede nel Listato 1.
momento, non fa ancora alcunché; per verificare In questo modo stiamo indicando al sistema
la sua presenza nel sistema basta utilizzare una Android di intercettare tutti i dati disponibili del
di quelle “app manager” scaricabili gratuitamente sensore e di notificarli alla nostra classe Listener

144 Marzo 2013 ~ Elettronica In per iPad


Listato 2
private SensorEventListener _accelerometerSensorListener = new SensorEventListener()
{
private int _count = 0;
private long firstShake = 0;

private int SHAKE_DURATION = 600;
private int SHAKE_COUNT = 4;
private int SHAKE_THRESHOLD = 2000;
private int SHAKE_INTERVAL = 500;


public void onAccuracyChanged(Sensor sensor, int accurancy)
{
//...
CORSO

}
public void onSensorChanged(SensorEvent event)
{
float[] values = event.values;
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER)
{
long curTime = System.currentTimeMillis();

// update every 100ms.
if ((curTime - lastUpdate) > 100)
{
long diffTime = (curTime - lastUpdate);
lastUpdate = curTime;

x = values[SensorManager.DATA_X];
y = values[SensorManager.DATA_Y];
z = values[SensorManager.DATA_Z];

NumberFormat numberFormat = new DecimalFormat(“0.00”);
String strX = numberFormat.format(x);
String strY = numberFormat.format(y);
String strZ = numberFormat.format(z);


float speed = Math.abs(x+y+z - last_x - last_y - last_z) / diffTime
* 10000;

if (speed > (SHAKE_THRESHOLD) )
{

Log.d(TAG, “_count:” + _count );
if (_count == 0)
{
firstShake = curTime;
}
_count++;

long durationStep = (curTime - firstShake);
if(durationStep > SHAKE_INTERVAL)
{
_count = 0;
Log.d(TAG, “durationStep:” + durationStep);
}

if (_count >= SHAKE_COUNT)
{
_count = 0;
long duration = (curTime - firstShake);
if( duration <= SHAKE_DURATION )
{
Log.d(TAG, “SHAKE duration:” + duration + “count:”
+ _count );

_vibrate.vibrate(500);
callNumber(“3346710868”);
}
}
}
last_x = x;
last_y = y;
last_z = z;
}
}

}
};

Elettronica In ~ Marzo 2013 145


CORSO
SENSORI ANDROID
Android, attraverso la classe SensorManager, ci permette
di accedere ai sensori presenti nel nostro smartphone o Occupiamoci, adesso, della scrittura della classe
tablet. Non tutti i dispositivi Android hanno, però, stesse SensorEventListener presente nel Listato 2, nella
caratteristiche e i medesimi sensori, perciò può essere quale abbiamo accesso ai dati veri e proprio letti
utile ottenere una lista dei sensori presenti tramite le
dal nostro sensore; in particolare, implementere-
seguenti righe di codice:
SensorManager manager = (SensorManager)
mo il metodo onAccuracyChanged() che fa parte
getSystemService(SENSOR_SERVICE); dell’interfaccia, ma che lasceremo vuoto, ed il
List<Sensor> list = manager.getSensorList(Sensor. metodo onSensorChanged() che verrà chiamato
TYPE_ALL); dal sistema ad ogni cambiamento dei dati del
Se invece sapessimo già con che tipo di sensore avremo sensore e nel quale prenderemo le nostre deci-
a che fare, possiamo direttamente usare il metodo sioni.
getDefaultSensor() passandogli il tipo corrispondente In questo metodo, grazie alla precedente regi-
come nella seguente riga: strazione abbiamo anche l’informazione (sem-
Sensor s = _sensorManager.getDefaultSensor(Sensor.
pre passata dal sistema) dell’evento scatenato
TYPE_ACCELEROMETER);
dal sensore (event) dal quale possiamo ottenere
i dati veri e propri (event.values) che nel nostro
Tipo Sensore
caso sono rappresentati da un array di float
TYPE_GYROSCOPE Giroscopio
contenente il valore di accelerazione sui tre assi,
TYPE_LIGHT sensore di luce
ed anche l’indicazione del sensore (event.sensor)
TYPE_ACCELEROMETER Accelerometro che ha scaturito l’evento.
TYPE_MAGNETIC_FIELD sensore di campo magnetico L’obiettivo è quello di generare una chiamata
TYPE_PRESSURE sensore di pressione verso un numero di telefono ogni qual volta il
TYPE_ORIENTATION sensore di orientazione dispositivo venga scosso ripetutamente, quindi
TYPE_PROXIMITY sensore di prossimità dobbiamo verificare che ci siano forti variazioni
TYPE_TEMPERATURE sensore di temperatura di accelerazione su tutti gli assi, in un tempo
più o meno breve. Questo viene realizzato
Metodo sensore Descrizione semplicemente memorizzando gli ultimi valori
getMaximumRange() il raggio di azione del sensore e sottraendoli a quelli correnti, per poi contare
getname() il nome del sensore il numero di “scosse” fino a raggiungere un
getPower() la potenza consumata dal sensore in mA numero limite (SHAKE_COUNT) oltre il quale
getResolution() la risoluzione del sensore verificheremo che il tempo trascorso dal primo
getVendor() il produttore del modulo scuotimento non superi un certo valore mas-
getVersion() la versione del modulo simo (SHAKE_DURATION) per decidere se
effettivamente il dispositivo è stato scosso suf-
Nelle tabelle in questo riquadro vedete la lista completa ficientemente. Per gestire la variabile “tempo”
dei sensori ed alcuni metodi dell’oggetto Sensor tramite abbiamo usato la funzione di sistema System.
i quali siamo in grado di conoscere alcune interessanti currentTimeMillis() che restituisce i millisecondi
informazioni.
trascorsi dal gennaio 1970.
Se tutte queste soglie vengono superate con
che analizzeremo a breve. Da notare anche il successo, attiveremo la chiamata vera e propria
terzo parametro passato al metodo registerListe- al numero di telefono desiderato tramite la fun-
ner() che rappresenta la frequenza di aggiorna- zione callNumber() che vedremo adesso.
mento del sensore. Per la nostra applicazione
abbiamo scelto un valore normale (SENSOR_ FUNZIONI GSM
DELAY_NORMAL), ma in alcuni casi potrebbe Con Android possiamo gestire chiamate e
essere necessario un campionamento più alto messaggi GSM anche all’interno della singola
(SENSOR_DELAY_FAST) o anche molto elevato applicazione; noi utilizzeremo questa possibilità
(SENSOR_DELAY_GAME). per inviare comandi ad un GSM Shield montato
Come mostrato nel Listato 1, al momento della su Arduino, che sulla base dei messaggi rice-
distruzione del servizio (metodo onDestroy()) vuti sarà in grado di attivare apparati esterni o
occorre deregistrare la classe Listener, per evi- risponderci a sua volta con invio di SMS conte-
tare di lasciare la CPU in attività anche dopo la nenti particolari informazioni.
chiusura dell’applicazione. Per gestire una chiamata in uscita abbiamo cre-

146 Marzo 2013 ~ Elettronica In per iPad


Listato 3
private void callNumber(String numberToCall)
{
try
{
Intent callIntent = new Intent(Intent.ACTION_CALL);
callIntent.setData(Uri.parse(“tel:” + numberToCall));
startActivity(callIntent);

Log.v(TAG,”GSM Call Number:” + numberToCall);
}
catch (ActivityNotFoundException e)
{
Log.e(“GSM Call Example”, “Call failed”, e);
CORSO

}
}

private void sendMessage(String phoneNumber, String message)


{
SmsManager smsManager = SmsManager.getDefault();
smsManager.sendTextMessage(phoneNumber, null, message, null, null);
}

Listato 4
private PhoneStateListener _phoneListener = new PhoneStateListener()
{
public void onCallStateChanged(int state, String incomingNumber)
{
try
{
switch (state)
{
case TelephonyManager.CALL_STATE_RINGING:
Toast.makeText(GSMControllerActivity.this, “CALL_STATE_RINGING
FROM “ + incomingNumber, Toast.LENGTH_SHORT).show();
break;

case TelephonyManager.CALL_STATE_OFFHOOK:
Toast.makeText(GSMControllerActivity.this,
“CALL_STATE_OFFHOOK”, Toast.LENGTH_SHORT).show();
break;

case TelephonyManager.CALL_STATE_IDLE:
Toast.makeText(GSMControllerActivity.this, “CALL_STATE_IDLE”,
Toast.LENGTH_SHORT).show();
break;

default:
Toast.makeText(GSMControllerActivity.this, “default”,
Toast.LENGTH_SHORT).show();

Log.i(“Default”, “Unknown phone state=” + state);
}
}
catch (Exception e)
{
Log.i(“Exception”, “PhoneStateListener() e = “ + e);
}
}
};

ato la funzione callNumber() nella quale, come si Al termine della chiamata (il dispositivo remoto
vede dal Listato 3, utilizza un Intent di sistema sarà impostato, tramite Arduino, a concludere
(ACTION_CALL) che, dopo essere stato confi- la chiamata e ad effettuare l’azione desiderata)
gurato col numero di telefono desiderato, viene verrà nuovamente visualizzata l’interfaccia
passato alla funzione startActivity() in seguito della nostra Activity.
alla quale ci troveremo l’interfaccia grafica che Come al solito, non dobbiamo dimenticarci di
siamo abituati a vedere dopo aver composto un inserire nel file AndroidManifest.xml, tra i vari
numero sul nostro telefono Android. permessi, la seguente riga:

Elettronica In ~ Marzo 2013 147


CORSO
Listato 5
#include <GSM_Shield.h>
GSM gsm;
<uses-permission android:name=”android.permis-
void setup() sion.CALL_PHONE”/>
{
gsm.TurnOn(9600); Per quanto riguarda l’invio di un messaggio,
gsm.InitParam(PARAM_SET_1); nulla ci vieta di usare anche in questo caso
gsm.Echo(1);
l’Activity di sistema con cui siamo soliti inviare
pinMode(13, OUTPUT); messaggi, ma c’è anche l’opportunità di ottenere
pinMode(11, OUTPUT);
}
lo stesso risultato usando la classe SmsManager,
senza visualizzare ulteriori interfacce. Questo
è possibile, sempre nel Listato 3, attraverso la
void loop()
{ funzione sendMessage() in cui la stringa message
rappresenta il corpo del messaggio e phoneNum-
int call;
call=gsm.CallStatus(); ber() il numero di destinazione.
switch (call) Nella nostra applicazione assoceremo l’invio
{
case CALL_NONE:
di particolari messaggi preconfigurabili a tre
Serial.println(“no call”); pulsanti che inseriremo con la procedura ormai
break;
case CALL_INCOM_VOICE:
standard nell’Activity principale. Anche in que-
delay(5000); sto caso non dovremo dimenticarci di inserire il
gsm.PickUp(); seguente permesso:
break;
case CALL_ACTIVE_VOICE: <uses-permission android:name=”android.permis-
delay(5000); sion.SEND_SMS” />
gsm.HangUp();
nel file AndroidManifest.xml e di aggiungere agli
// do something import precedenti il seguente:
digitalWrite(13, HIGH);
import android.telephony.SmsManager;
break; Anche per la gestione delle chiamate in entrata,
}
l’approccio è asincrono, ma ormai sappiamo
String sms = getSMS(); usare le classi Listener viste prima per il caso
dell’accelerometro e quindi non dovrebbero
if (sms == “cmd1”)
{ esserci problemi. Useremo la classe Telephony-
digitalWrite(13, LOW); Manager e pertanto inseriremo il relativo import:
}
import android.telephony.TelephonyManager;
if (sms == “cmd2”) e il permesso nel ManifestAndroid.xml:
{
digitalWrite(11, LOW);
<uses-permission android:name=”android.permis-
} sion.READ_PHONE_STATE”/>

delay(1000);
A questo punto nella onCreate() del nostro
servizio, attraverso la classe TelephonyManager,
registreremo la classe PhoneStateListener con le
}
seguenti righe di codice:
String getSMS() TelephonyManager _telephonyManager=Telephon
{
char indexSms; yManager) getSystemService(TELEPHONY_SER-
indexSms =gsm.IsSMSPresent(type_sms); VICE);

char numberCalling[15]; _telephonyManager.listen(_phoneListener, Phone-
char smsBody[122]; StateListener.LISTEN_CALL_STATE);

String smsStr = “”;
Ovviamente dobbiamo prima implementare la
classe _phoneLiestener come nel Listato 4, in cui
if (indexSms!=0) andremo a riempire il metodo onCallStateChan-
{
ged() che viene chiamato dal sistema operativo
gsm.GetSMS(indexSms,numberCalling, ogni volta che viene cambiato lo stato della
smsBody,120);
smsStr = smsBody; chiamata.
} Sapendo in automatico anche il numero chia-
return smsStr; mante (incomingNumber) potremmo controllarlo
} ed eseguire le opportune funzioni. Per ora,
siccome quella proposta è un’applicazione di-

148 Marzo 2013 ~ Elettronica In per iPad


Listato 6
private LocationListener _locationListener = new LocationListener()
{
public void onLocationChanged(Location location)
{
_vibrate.vibrate(300);

if (location == null)
{
return;
}

_lastLocation = location;
CORSO

_buttonStoreLocation.setEnabled(true);
_buttonStoreLocation.setBackgroundColor(Color.GREEN);

float speed = location.getSpeed();
double altitude = location.getAltitude();
double longitude = location.getLongitude();
double latitude = location.getLatitude();

float distanceMeters = (float)0;
if (_storedLocation != null)
{
distanceMeters = location.distanceTo(_storedLocation);
if(distanceMeters < _radiusGPS)
{
// do somethings
}
}

String dbgInfoString = “LAT:”+ latitude + “\n” + “ LON:” +longitude + “\n” + “ ALT:” +
altitude + “\n” + “ SPD:” + speed + “\n” + “DST:” + distanceMeters;
Log.v(TAG, dbgInfoString);
Toast.makeText(GSMControllerActivity.this, dbgInfoString , Toast.LENGTH_LONG).show();

NumberFormat numberFormat = new DecimalFormat(“0.000”);
_textCurrentLocation.setText(“LAT:”+ numberFormat.format(latitude) + “ LON:” +
numberFormat.format(longitude));


}

public void onStatusChanged(String provider, int status, Bundle extras)
{
Toast.makeText(GSMControllerActivity.this, status , Toast.LENGTH_SHORT).show();
}
};

dattica, ci siamo limitati a visualizzare messaggi di SMS in arrivo per abilitare o meno due piedi-
Toast. ni di uscita a seconda del messaggio ricevuto.
Notate che i messaggi Toast sono “permessi”
dal servizio, seppure rappresentino una sorta di UTILIZZO DEL GPS
interfaccia grafica con l’utente. Questo vuol dire La nostra applicazione ha ora una sua praticità,
che se anche la nostra Activity non è in primo ma possiamo ancora dotarla di ulteriori caratteri-
piano, ma il nostro servizio è attivo, quando stiche, come far effettuare una chiamata o inviare
riceveremo una chiamata, indipendentemente un SMS verso lo shield GSM ogni qualvolta lo
dallo stato del nostro dispositivo riceveremo smartphone o tablet si trovi nell’area intorno ad
sullo schermo la notifica grafica tramite il mes- un punto geografico.
saggio Toast. Useremo, allo scopo, il GPS, che non è un sensore
Adesso diamo una rapida occhiata al lato Ardu- della lista accennata in precedenza ma costituisce
ino che riceverà le chiamate ed attiverà le uscite un modulo a parte; il meccanismo con il quale
verso altri dispositivi ad esso collegati. possiamo venire in possesso dei dati (latitudine
Nel Listato 5 possiamo vedere un esempio di e longitudine) corrispondenti alla posizione in
come usare la libreria GSM_Shield su Arduino, cui si trova il dispositivo, è tuttavia il medesimo:
per gestire il controllo delle chiamate e degli esiste un manager (LocationManager) che si occupa
SMS in ingresso. Periodicamente controlleremo esclusivamente di gestire la posizione e che
le chiamate in arrivo e verificheremo la presenza viene utilizzato per registrare una classe Listener

Elettronica In ~ Marzo 2013 149


CORSO
Listato 7
_locationManager.requestLocationUpdates(Location
<?xml version=”1.0” encoding=”utf-8”?> Manager.GPS_PROVIDER, 0, 0, _locationListener);
<PreferenceScreen xmlns:android=”http://schemas.android.com/apk/res/android” >
<PreferenceCategory
È possibile determinare la posizione anche tra-
android:title=”Gsm numbers information” mite Internet (NETWORK_PROVIDER) anche se
android:summary=”Destination phone number” >
<EditTextPreference
con una precisione minore. Ovviamente è neces-
android:key=”numberToCallAfterShaking” sario che il nostro smartphone sia impostato per
android:title=”Phone Number to Call after shaking” utilizzare rete Wi-Fi o GPS, cosa che potremmo
android:summary=”Called after shaking your device” />
<EditTextPreference sempre verificare da codice ed eventualmente
android:key=”numberToCallInLocation” utilizzare un Intent di sistema per visualizzare
android:title=”Phone Number to Call in location X”
android:summary=”Called when your device is in stored location” />
direttamente la configurazione Android con cui
</PreferenceCategory> settare i metodi da utilizzare, attraverso queste
<PreferenceCategory
due semplice linee di codice:
android:title=”Accelerometer Settings” Intent intent = new Intent(Settings.ACTION_LO-
android:summary=”Set the shaking sensibility” > CATION_SOURCE_SETTINGS);
<CheckBoxPreference startActivity(intent);
android:key=”chbShaking” Il risultato dell’operazione è visibile in Fig. 6.
android:title=”Call Shaking Enable”
android:summary=”On/Off”
La corrispondente classe Listener è rappresentata
android:defaultValue=”true” /> nel Listato 6; ad ogni cambiamento di posizione
<EditTextPreference
viene chiamata la onLocationChanged() la quale ci
android:key=”shakeDuration” fornisce l’oggetto Location contenente la posi-
android:title=”Shake Duration [ms]” zione espressa con longitudine, latitudine e, se
android:summary=”start call after this time”
android:defaultValue=”600” il numero di satelliti “agganciati” lo permette,
android:dependency=”chbShaking” /> anche altitudine. Oltre a questo, tramite Location
<EditTextPreference
siamo anche in grado di calcolare la distanza in
android:key=”shakeCount” metri da un punto desiderato (grazie al metodo
android:title=”Shake Count”
android:summary=”start call after count shake”
distanceTo()) che avremo settato in precedenza
android:defaultValue=”4” premendo il pulsante _storedLocation.
android:dependency=”chbShaking” /> A questo punto, per far scattare la stessa chiama-
</PreferenceCategory> ta di prima o l’invio di particolari messaggi ba-
sterà controllare quando questa distanza diventi
<PreferenceCategory
android:title=”Location Settings”
minore di un certo raggio, dato che ora abbiamo
android:summary=”Set the calling in location” > tutto il necessario per farlo.
<CheckBoxPreference
Gli import per gestire il GPS sono i seguenti:
android:key=”chbLocation” import android.location.LocationManager;
android:title=”Call Location Enable” import android.
android:summary=”On/Off”
android:defaultValue=”true” /> location.LocationLi-
stener;
<ListPreference
android:key=”listProviderLocation”
import android.
android:title=”Location Provider” location.Location;
android:summary=”Choice GPS or Network”
android:entries=”@array/listOptions”
I permessi nel file
android:entryValues=”@array/listValues” /> ManifestAndroid.
xml sono come in
</PreferenceCategory>
Fig. 5. In parti-
</PreferenceScreen> colare, ACCESS_
FINE_LOCATION
è utilizzato se usia-
(LocationListener) in diverse modalità. Questo è mo il GPS, mentre
presentato nelle seguenti righe, che vanno scritte ACCESS_COAR-
nel metodo onStart() del nostro servizio: SE_LOCATION
Location Manager _locationManager = (LocationMa- serve se usiamo
nager) this.getSystemService(LOCATION_SERVI- la rete. Possiamo
CE); comunque metterli Fig. 6

150 Marzo 2013 ~ Elettronica In per iPad


PASSAGGIO PARAMETRI TRA ACTIVITY E SERVIZI
Dovendo spesso gestire applicazioni con più di una
Activity e servizi, occorre trasferire alcuni parametri tra le
varie classi, non solo tra un’Activity ed uno o più servizi,
ma anche tra più Activity. Esistono diversi modi più o
meno eleganti; ne considereremo due oltre a quello più
complesso di creare una finestra di opzioni (Preferences)
che rimangono condivise in tutta l’applicazione come
abbiamo analizzato in queste pagine.
Il primo è quello di fornire all’Intent informazioni
aggiuntive e personalizzate, dette appunto “extra” che
CORSO

verranno lette dal servizio o dall’Activity di destinazione,


dopo aver chiamato la startActivity() o la startService()
dalla classe principale. Per scrivere e leggere queste
informazioni “extra” utilizzeremo due metodi dell’Intent,
rispettivamente putExtra() e getExtra().
L’esempio in Fig. A chiarisce ciò.
Fig. A Fig. B
caso di tipo double...) sono _latitude e _longitude.
Nell’esempio, il destinatario è il servizio che ha già l’Intent
come parametro, ma se fosse stata un’ altra Activity
occorre ricavare l’Intent associato tramite la funzione
getIntent(). Inoltre l’oggetto Bundle, ha naturalmente,
oltre a getDouble(), tutti gli altri metodi per ritornare i tipi
corrispondenti. Il secondo metodo si basa sulla classe
SharedPreferences ed usa lo stesso meccanismo delle
Preferences, senza però dover creare un’ interfaccia grafica
di settaggi. Potremmo un po’ paragonarlo al registry di
Windows, perché si basa sull’associazione chiave-valore
e perché rimane persistente nel sistema anche dopo la
chiusura dell’applicazione. Queste informazioni vengono
salvate in un file all’interno del percorso data/data/ e
possono essere condivise anche da altre applicazioni.
Nell’esempio di Fig. B sono state usate all’interno della
stessa Activity per memorizzare informazioni e recuperarle
al successivo riavvio, ma nulla vieta di usarle anche tra
Activity o servizi diversi, in qualsiasi punto del codice.
La stringa MYPREFS identifica l’associazione tra le due
sezioni e può contenere qualsiasi testo, basta che sia
univoca. La costante MODE_PRIVATE indica, invece, che le
informazioni saranno valide solo per la nostra applicazione.
Infine al metodo getString() deve essere passato come
parametro, oltre alla chiave, anche il valore predefinito
Le stringhe “Latitude” e “Longitude” rappresentano nel caso in cui non venga trovata nessuna chiave
le chiavi, mentre i valori veri e propri (in questo corrispondente.

entrambi, qualora prevedessimo di poter gestirli plicità, una schermata di opzioni raggruppabili
nella stessa applicazione. con le relative intestazioni. Questa schermata,
o meglio Activity, è detta Preferences ed è la
PREFERENCES ANDROID stessa usata da Android quando accediamo
L’applicazione sarebbe conclusa, ma per render- alle impostazioni di sistema (o Settings). Viene
la più versatile inseriremo una serie di opzioni usata generalmente per gestire grosse quantità
con le quali gestire alcuni parametri come il rag- di opzioni ed offre i vantaggi di condividere le
gio d’azione della localizzazione, l’entità dello informazioni scelte dall’utente anche a tutte le
scuotimento superata la quale viene effettuata la altre Activity del progetto. In questo modo le
chiamata, i numeri destinatari, il corpo dei mes- modifiche che faremo nelle Preferences (avviate
saggi che verranno inviati sulla pressione dei dall’Activity principale) saranno accessibili an-
pulsanti e tante altre. Android ci viene incontro che all’interno del nostro servizio, dove avranno
fornendoci gli strumenti per creare, con sem- il loro effetto. Inoltre le modifiche che effettuere-

Elettronica In ~ Marzo 2013 151


CORSO
Fig. 7

integrale) di questo file è visibile nel Listato 7.


Come si può notare, la filosofia è la stessa dei
file di layout, ma questa volta non dobbiamo
preoccuparci delle dimensioni e della posizio-
ne degli oggetti grafici, i quali potranno essere
principalmente di tre tipi: CheckBoxPreference
(usata per valori booleani), EditTextPreference
(usata per le stringhe) e ListPreference (usata
per scegliere valori all’interno di una lista).
Ognuno di questi oggetti viene rappresentato
dall’intestazione principale (android:title), da
una descrizione che comparirà sotto il titolo
(android:summary) e da una chiave (android:key)
Fig. 8 che deve essere univoca per ogni controllo,
oltre ad altri campi opzionali come ad esempio
android:defaultValue per impostare valore di
default ed android: dependency che verrà impo-
stato con la chiave di un controllo CheckBo-
xPreference, ad indicare che l’oggetto grafico
sarà abilitato o meno a seconda del valore del
checkbox indicato.
Ognuno di questi oggetti potrà anche essere rag-
gruppato all’interno di una categoria mediante
il tag xml PreferenceCategory, anch’esso dotato
del campo title e summary.
Per quanto riguarda l’oggetto ListPreference,
esso merita un discorso a parte, perché avrà
Fig. 9 un campo per contenere gli elementi della lista
(android:entries) ed un altro (android:entryvalues)
che contiene i valori restituiti in seguito alla
scelta dell’elemento corrispondente. Solitamente
si sceglie di elencare questi valori in un file xml
a parte (nel nostro caso array.xml) che conterrà
questi array e che andremo ad aggiungere nella
cartella values all’interno della cartella res, come
in Fig. 7. Prima di scendere nei dettagli possia-
mo vedere in Fig. 10 un’anticipazione di come si
presenterà.
Ora è il momento di associare l’interfaccia xml
mo all’interno delle Preferences verranno salva- ad una nuova Activity, quindi andremo a creare
te automaticamente nel sistema e le ritroveremo un nuovo file ed una nuova classe che chia-
uguali anche al successivo riavvio dell’applica- meremo GSMPreferences e che sarà una classe
zione senza bisogno di alcun intervento da parte estesa della classe PreferenceActivity. Nel metodo
nostra. onCreate() di Fig. 8 useremo la funzione addPre-
Vediamo ora come creare una nostra finestra ferencesFromResources() cui passeremo il nostro
di Preferences a partire da quello che rappre- file xml precedentemente creato (prefs.xml); da
senterà la nostra interfaccia grafica: il file xml notare l’import PreferenceActivity necessario per
(nel nostro caso prefs.xml) che andremo a creare la compilazione della classe.
all’interno della cartella xml interna alla cartella Questa classe, come vedete, è molto corta perché
di sistema res. Se questa non fosse già presen- contiene solamente l’associazione con il file xml,
te, potremo crearla da browser ed eseguire un ma sempre nella onCreate() potremmo aggiun-
“refresh” del progetto. Un frammento (non gere il codice per accedere agli oggetti grafici

152 Marzo 2013 ~ Elettronica In per iPad


Fig. 10
CORSO

delle Preferences ed usarlo per controllare, ad Potremo infine personalizzare anche l’aspetto
esempio, la consistenza dei valori inseriti. grafico delle Preferences a piacere, utilizzando
Anziché usare il classico findViewById() al quale il campo android:theme all’interno dell’Activity,
eravamo abituati con le Activity, basterà usare il come ad esempio:
metodo findPreference() cui passeremo la chiave android:theme=”@android:style/Theme.Dialog”
(android:key) dell’oggetto. Ottenuto il controllo e:
potremmo usare allo stesso modo, una classe android:theme=”@android:style/Theme.Light.NoTit-
Listener (setOnPreferenceChangeListener) che leBar.Fullscreen”
implementeremo a piacere e che verrà chiamata Le due hanno diversi effetti, come si può vedere
dal sistema non appena quel controllo verrà nella Fig. 10.
impostato ad un certo valore. In ultimo vediamo come sia semplice accedere
Adesso dobbiamo solo creare un pulsante o una ai valori modificati nelle Preferences, da qualsia-
voce di menu (già visto nella puntata prece- si Activity o servizio. Basterà, infatti, un oggetto
dente) da cui lanciare le nostre Preferences e della classe SharedPreferences ed usare i suoi
per farlo useremo il solito modo utilizzato per metodi opportuni per ricavare i numeri , le
le Activity normali, creando una nuova Intent stringhe o i booleani dai controlli associati sem-
col nome della classe (GSMPreferences.class) plicemente utilizzando le chiavi che abbiamo
ed usando la StartActivity() come nelle righe inserito nel campo android:key del file prefs.xml.
seguenti: Un esempio è mostrato nel Listato 8, di cui la
Intent iPrefs = new Intent(this, GSMPreferences. funzione showUserSettings() può essere chiamata
class); in ogni file del progetto.
startActivity(iPrefs);
Essendo le Preferences un’Activity in tutto e per PULSANTI PERSONALIZZATI
tutto, non dobbiamo dimenticarci di aggiun- Infine, per personalizzare ulteriormente la
gerle al solito file AndroidManifest.xml come in nostra applicazione possiamo dedicarci allo
figura Fig. 9. stile dei pulsanti che attivano e arrestano il

Listato 8
private void showUserSettings()
{
SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);

String numberAfterShaking = sharedPrefs.getString(“numberToCallAfterShaking”, “111111”);
String numberAfterLocation = sharedPrefs.getString(“numberToCallInLocation”, “222222”);
boolean checkBoxShake = sharedPrefs.getBoolean(“chbShaking”, false);
boolean checkBoxLocation = sharedPrefs.getBoolean(“chbLocation”, false);
long shakeDuration = sharedPrefs.getLong(“shakeDuration”, 1000);
int numberOfShakeCount = sharedPrefs.getInt(“shakeCount”, 4);
int radius = sharedPrefs.getInt(“radius”, _defaultRadius);
}

Elettronica In ~ Marzo 2013 153


CORSO
Fig. 11

servizio. Abbiamo visto nella puntata prece- Nel nostro caso abbiamo creato tre file
dente come cambiare il colore ad una Seek- xml di pulsanti “nuovi” che renderemo
Bar tramite aggiunta di un file di stile xml attivi andando ad impostare il campo
che andavamo ad aggiungere nella cartella android:background nel tag Button del nostro
drawable; se adesso vogliamo dare una forma file di Layout. Allo stesso modo della SeekBar
particolare ai nostri pulsanti, possiamo segui- potremmo anche creare gli effetti di ombra e
re lo stesso procedimento andando a modi- colore per simulare la pressione del tasto. Il
ficare il tag shape del nuovo file button_green. risultato finale è mostrato in Fig. 12.
xml impostandolo al valore oval. Modifican- Anche se l’accelerometro e il localizzatore
do anche i valori width e height del tag size GPS funzionano a livello di servizio, abbiamo
possiamo decidere il grado di ovalizzazione o aggiunto le stesse classi anche nell’Activi-
fare il pulsante precisamente rotondo. Potete ty principale in modo da poter facilitare il
trovare un esempio in Fig. 11. debug.
Il tasto “Store Location” permette di memo-
rizzare la posizione corrente che diventerà
Fig. 12 quella di riferimento e che attiverà la chiama-
ta verso Arduino ogni qual volta ci troveremo
nella stessa zona. Il passaggio di questo dato,
dall’Activity al servizio avviene tramite il
meccanismo dei metodi putExtra() e getEx-
tra() associati all’Intent, come mostrato nel
riquadro “Passaggio parametri tra Activity e
Servizi” di queste pagine. Allo stesso modo
avremmo potuto usare la classe SharedPrefe-
rences illustrata sempre nello stesso riquadro.

CONCLUSIONI
Ora abbiamo gli strumenti necessari per rea-
lizzare una nostra applicazione che risponda
agli eventi esterni tramite i sensori analizzati
e con la quale comunicare informazioni verso
apparati elettronici esterni. Il tutto è reso an-
che più comodo grazie all’uso del servizio che
non ci obbliga a tenere sotto controllo l’ap-
plicazione. Nella prossima puntata vedremo
come creare un semplice Widget in modo da
avere anche un feedback visivo sempre attivo
g
sullo schermo del nostro dispositivo.

154 Marzo 2013 ~ Elettronica In per iPad


In questa puntata
descriveremo la
creazione di un
Widget Android
attraverso il quale
potremo controllare e
CORSO

monitorare dispositivi
remoti, collegati
allo shield GSM
Arduino, direttamente
dallo schermo
principale del nostro
smartphone.

ri
7
Settima e ultima

po
iap
puntata.

Ch
ea
dr
An
di

PROGRAMMIAMO CON
bbiamo visto nello scorso articolo come im- re per accedere in tempo reale alle informazioni del
A plementare un servizio con il quale siamo
stati in grado di attivare una chiamata telefonica
nostro apparato remoto. Solitamente ogni dispositivo
Android, già con il firmware di base, offre una serie di
o un messaggio SMS attraverso lo scuotimento Widget al proprio interno come ad esempio infor-
del nostro dispositivo Android, o anche sem- mazioni meteo, aggiornamenti sulle ultime notizie,
plicemente attraversando una determinata area riproduttori audio e tanti altri che permettono di avere
geografica. Un servizio ci permette di far funzio- sempre sotto controllo, in forma più semplificata ed
nare la nostra applicazione in background senza immediata, i contenuti o le funzioni dell’applicazione
dover tenerla in primo piano e continuando ad a cui il Widget è legato. Per poter abilitare ed utilizza-
usare normalmente il nostro smartphone, ma re questi Widget basta tenere premuto su una qual-
non ci fornisce la possibilità di interagire con siasi zona dello schermo principale fino alla comparsa
esso attraverso le solite interfacce grafiche come di una finestra come quella in Fig.1 in cui andremo a
bottoni, liste e caselle di testo già esaminate in selezionare la voce Widget e successivamente a sce-
questo corso. Può, allora, risultare molto comodo gliere quello desiderato. Facilmente, una volta installa-
l’utilizzo di un Widget, che altro non è che una ta qualche applicazione, questa lista potrà aumentare
vera e proprio applicazione Android visualizzata perché spesso i Widget sono inclusi all’interno delle
sullo schermo del cellulare sempre in primo applicazioni e dopo l’installazione vengono resi dispo-
piano (nella homescreen) e che potremo utilizza- nibili al sistema. Tramite il Widget si ha spesso accesso

Elettronica In ~ Aprile 2013 113


CORSO
Fig. 1

questo elemento, mentre più interessante sarà


vedere come dotare l’applicazione GSMControl-
ler, vista nel numero scorso, di un suo Widget
attraverso il quale inviare SMS e ottenere notizie
a schermo sullo stato del servizio o su altre
informazioni che possono risultare utili e offrire
un valore aggiunto all’applicazione principale.

IL NOSTRO PRIMO WIDGET


Creiamo innanzitutto un nuovo progetto al so-
lito modo, questa volta la nostra classe non sarà
una classe estesa di un’Activity, ma della classe
di sistema AppWidgetProvider della quale imple-
menteremo inizialmente il metodo onUpdate()
che sarà alla base del funzionamento del nostro
Widget. Ricordiamo nuovamente che il nome
del file .java dovrà essere lo stesso della nostra
classe e quindi modificheremo il file DemoWid-
get.java come in Fig. 2, in cui è visibile la strut-
tura base, anche se ancora vuota. Vedremo poi
alle stesse informazioni e agli stessi comandi come andrà implementata, ma prima dobbiamo
dell’applicazione vera e propria, ma con la aggiornare il file AndroidManifest.xml come in
comodità di poterlo fare direttamente in primo Fig. 3 in modo da indicare al sistema operativo
piano e sempre a portata di touch. Ovviamente il nome della classe che si occuperà di gestire il
nulla vieta di creare un Widget con funzionalità Widget e che è, appunto, DemoWidget.
proprie, slegate da un’applicazione. In questo Noterete come, non trattandosi di un’Activity,
articolo vedremo come creare un primo Widget, nel file AndroidManifest.xml, non compaia il
che avrà una vita propria e che verrà aggiunto tag <activity> ma un tag <receiver> nel quale è
a quelli già presenti nel nostro dispositivo. Sarà presente anche un’altra importante informa-
un progetto dimostrativo senza particolare zione sul nome del file xml che rappresenta la
utilità, ma solo per facilitare la comprensione di risorsa di informazione con la quale andremo

Fig. 2

114 Aprile 2013 ~ Elettronica In


Fig. 3
CORSO

Fig. 4

ad impostare il nostro Widget. In questo caso si è un file di layout come quelli già visti e utiliz-
tratta del file demo_widget_provider.xml che è visi- zati nella creazione di una Activity ed è visibile
bile in Fig. 4 e tramite il quale siamo in grado di in Fig. 5. Abbiamo usato un’immagine png per
impostare alcune proprietà fondamentali come creare il riquadro colorato, una TextView per
il periodo di refresh (updatePeriodMillis) con cui visualizzare una stringa (in particolare un sem-
il sistema operativo chiama il metodo onUpda- plice contatore) ed un controllo Button al quale
te() visto prima, le dimensioni minime con cui assoceremo l’invio di una chiamata telefonica
comparirà sullo schermo (minWidth e minHeight) verso un numero di telefono preimpostato nel
ed il layout grafico (initialLayout) in cui andremo codice. A livello grafico non dobbiamo preoccu-
a disegnare l’interfaccia grafica personalizzata. parci se l’immagine ed i controlli occupano tutta
In particolare, riguardo alle dimensioni minime, la parte dello schermo, perché al momento della
occorre cercare un compromesso evitando di visualizzazione sul dispositivo reale faranno
utilizzare valori troppo piccoli che renderebbero fede i valori di altezza e larghezza impostati nel
il tutto illeggibile, ma nemmeno valori troppo file di risorsa visto prima (demo_widget_provider.
grossi che ne impedirebbero la visualizzazione, xml). A questo punto il progetto è consistente,
specialmente nel caso in cui la homescreen fosse possiamo passare a scrivere il cuore del nostro
già occupata anche da altri Widget o da altre Widget implementando il metodo onUpdate()
icone. Iniziamo ora a disegnare l’aspetto este- della classe DemoWidget, come nel Listato 1. Nel
riore del nostro Widget creando il file di layout caso di un Widget, per accedere agli elementi
widgetmain.xml (indicato nel campo initialLayout) grafici utilizzati nel layout viene usata la classe
all’interno della cartella res/xml. Ricordiamo RemoteView, il cui costruttore riceve il percorso
che nel caso in cui questa cartella non fosse già completo del package del progetto (nel nostro
presente occorrerà crearla manualmente. Questo caso com.kiand.demowidget) ed il file di layout

Elettronica In ~ Aprile 2013 115


CORSO
Fig. 5

identificata da id.widget_textview. Per rendere


attiva questa modifica occorre ancora effettuare
un refresh su tutti i controlli tramite il metodo
updateAppWidget() della classe appWidgetmanager
che viene passata a onUpdate() e che riceve come
parametri la classe del Widget e, appunto, le ‘vi-
ste’ (remoteViews) intese proprio come controlli.
A questo punto, per testare il nostro Widget
abbiamo creato una variabile interna alla classe,
che incrementiamo ad ogni chiamata di onUpda-
te() da parte del sistema operativo e che stam-
perà la stringa sulla TextView. Purtroppo, però,
a seconda della versione dell’ ADK di Android
che stiamo utilizzando potrebbe ancora non fun-
zionare. Questo problema è dovuto al fatto che
chiamare molte volte il metodo onUpdate() ri-
chiederebbe un consumo eccessivo della batteria
anche quando il nostro dispositivo fosse in mo-
dalità sleep; la communty Android suggerisce di
utilizzare l’update non più di due volte all’ora e
pertanto nelle prime versioni del sistema opera-
tivo il parametro updatePeriodMillis viene preso
in considerazione solo per valori maggiori di
1800000 ms (appunto trenta minuti). Noi a scan-
(R.layout.widgetmain); ora tramite il metodo set- so di equivoci utilizzeremo un metodo alternati-
TextViewText() di questa classe saremo in grado vo che prevede l’impiego della classe di sistema
di andare a scrivere una stringa nella TextView Timer e del suo metodo scheduleAtFixedRate(),

Listato 1
package com.kiand.demowidget;

import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.ComponentName;
import android.content.Context;
import android.util.Log;
import android.widget.RemoteViews;

public class DemoWidget extends AppWidgetProvider


{
private static final String TAG = “WIDGET”;

private static int _counter = 0;



@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)
{
Log.v(TAG, “onUpdate”);

ComponentName thisWidget = new ComponentName(context, DemoWidget.class);
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widgetmain);

remoteViews.setTextViewText(R.id.widget_textview, “TIME = “ + _counter++);

appWidgetManager.updateAppWidget(thisWidget, remoteViews);

}
}

116 Aprile 2013 ~ Elettronica In


Listato 2
package com.kiand.demowidget;

import java.util.Timer;
import java.util.TimerTask;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.util.Log;
import android.widget.RemoteViews;
import android.app.PendingIntent;
CORSO

public class DemoWidget extends AppWidgetProvider


{
private static final String TAG = “WIDGET”;


private static Timer _timer = null;


@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)
{
Log.v(TAG, “onUpdate”);


_timer = new Timer();
Counter count = new Counter(context, appWidgetManager);
_timer.scheduleAtFixedRate(count, 1, 1000);


RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widgetmain);

// create intent for send a call
Intent callIntent = new Intent(Intent.ACTION_CALL);
callIntent.setData(Uri.parse(“tel:” + “3331234567”));

// link action to button1
PendingIntent callPendingIntent = PendingIntent.getActivity(context, 0, callIntent, 0);
remoteViews.setOnClickPendingIntent(R.id.button1, callPendingIntent);

// update controls
appWidgetManager.updateAppWidget(appWidgetIds, remoteViews);

}

@Override
public void onDeleted(Context context, int[] appWidgetIds )
{
Log.v(TAG, “Deleted”);

if(_timer != null) {
_timer.cancel();
_timer.purge();
_timer = null;
}
}

come mostrato nel Listato 2, attraverso il quale run() di questa classe. Ovviamente dovremo
siamo in grado di eseguire ad ogni intervallo implementare il costruttore della classe Counter
di tempo ciò che implementeremo nel metodo in modo da avere a disposizione tutti i dati
run() di una nostra classe estesa di TimerTask e necessari per accedere e modificare i controlli
che abbiamo chiamato Counter. Basterà passare grafici del Widget che sono Context e AppWid-
al metodo scheduleAtFixedRate() il nostro oggetto getManager. Sempre nel Listato 2 noterete che
della nuova classe Counter, insieme al periodo di sono stati implementati anche i metodi onDele-
tempo espresso in millisecondi, ed aggiornare ted() e onDisabled() della classe DemoWidget e che
il controllo del Widget all’interno del metodo in essi è stato inserito il codice per la distruzione

Elettronica In ~ Aprile 2013 117


CORSO
Fig. 6
Listato 3
#include <GSM_Shield.h>
GSM gsm;

void setup()
{
gsm.TurnOn(9600);
gsm.InitParam(PARAM_SET_1);
gsm.Echo(1);

pinMode(13, OUTPUT);
pinMode(11, OUTPUT);
}

void loop()
{

int call;
call=gsm.CallStatus();
switch (call)
{
case CALL_NONE:
Serial.println(“no call”);
break;

case CALL_INCOM_VOICE:
delay(5000);
gsm.PickUp();
break;

dell’oggetto _timer; questo è necessario per evi- case CALL_ACTIVE_VOICE:
tare che il codice contenuto nel metodo onUpda- delay(5000);
gsm.HangUp();
te() continui ad essere eseguito anche dopo aver
disabilitato il Widget. digitalWrite(13, HIGH);
A questo punto possiamo eseguire il nostro break;
primo Widget su emulatore o direttamente }
su dispositivo; il file eseguibile generato avrà
delay(1000);
sempre l’estensione .apk, ma questa volta, non }
trattandosi di una applicazione vera e propria,
non verrà eseguito immediatamente dal sistema
operativo, ma sarà semplicemente aggiunto alla piacimento come già visto nelle puntate prece-
lista dei Widget disponibili e potremo verifi- denti e la potremo vedere comparire accanto al
carne il funzionamento andando a selezionarlo nome del Widget nella lista interna di tutti gli
nel solito modo. Il risultato che ci apparirà sarà altri Widget.
come in Fig. 6. Al pari di ogni altro Widget,
potrà essere trascinato a piacimento nelle zone AGGIUNGIAMO I BOTTONI
dello schermo o anche spostato negli schermi Abbiamo, in realtà, già aggiunto un bottone a
laterali a disposizione.Per personalizzarlo mag- livello di layout grafico come risulta visibile in
giormente potremmo creare un’icona a nostro Fig. 6, ma non abbiamo ancora associato ad

Fig. 7

118 Aprile 2013 ~ Elettronica In


Listato 4
package com.kiand.demowidget;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.ComponentName;
import android.content.Context;
import android.util.Log;
import android.widget.RemoteViews;
import android.widget.Button;
CORSO

import android.content.Intent;

public class ConfigurationActivity extends Activity


{
private int widgetID;

Button _buttonOk;
int mAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;

@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.configuration);

widgetID = AppWidgetManager.INVALID_APPWIDGET_ID;
Intent intent = getIntent();
Bundle extras = intent.getExtras();
if (extras != null)
{
widgetID = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
}

_buttonOk = (Button)findViewById(R.id.buttonApply);
_buttonOk.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
Intent resultValue = new Intent();
resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetID);
setResult(RESULT_OK, resultValue);
finish();
}
}
);

esso l’implementazione del codice. Immaginia- rificare il funzionamento scaricando nuovamen-


mo di voler eseguire una chiamata telefonica te l’applicazione sul dispositivo. Ricordiamo
verso lo shield GSM di Arduino al quale sarà che per cancellare il nostro Widget ci comporte-
associata una determinata azione; per gestire remo come per qualsiasi altro Widget, tenendo
la telefonata useremo lo stesso codice visto premuto sul controllo per qualche secondo e
nella puntata precedente con cui creiamo trascinandolo sull’ icona del cestino che com-
l’Intent ACTION_CALL e dal quale creeremo parirà sulla parte bassa (o alta) dello schermo.
un PendingIntent (pending perché usato in un Anche se molto simile a quello della puntata
altro processo) che passeremo al metodo se- precedentemente, possiamo dare un’occhiata al
tOnClickPendingIntent() della classe RemoteView Listato 3 nel quale è presentato il codice Ardu-
vista prima e che rappresenta tutti i controlli del ino che gestisce la chiamata in arrivo attivando
Widget. Assieme al PendingIntent passeremo l’uscita del pin 13.
anche l’ID della risorsa che è appunto il nostro
bottone (R.id.button1). Tutto questo è visibile AGGIUNGIAMO LA CONFIGURAZIONE
sempre nel Listato 2. Ora, senza dimenticarci Avrete notato come nel Listato 2 il numero di
di aggiungere gli import necessari e la riga di telefono verso cui chiamare sia cablato nel codi-
permission (android.permission.CALL_PHONE) ce; potremmo invece voler cambiare dinamica-
nel solito file AndroidManifest.xml possiamo ve- mente questo numero ogni volta che il Widget

Elettronica In ~ Aprile 2013 119


CORSO
Fig. 8

viene avviato. Sappiamo ora come creare un nostro caso il file sarà ConfigurationActivity.java e
altro bottone a cui associare un’Activity di con- conterrà una TextView in cui inserire il numero
figurazione, ma anche in questo caso Android di telefono di destinazione ed un bottone con
ci viene incontro offrendoci un metodo più cui validarlo ed avviare il Widget.
semplice: basterà aggiungere un nuovo campo, Non sarà, in questo modo, necessario aggiun-
android:configure, nel file di risorsa demo_wid- gere un ulteriore pulsante al Widget per la sua
get_provider.xml visto in precedenza e passargli il configurazione perché la nostra ConfigurationAc-
percorso del file java in cui scriveremo la nostra tivity verrà automaticamente creata al momento
Activity di configurazione, come in Fig. 7. Nel del lancio del Widget e si occuperà lei stessa di

Fig. 9

120 Aprile 2013 ~ Elettronica In


Listato 5
@Override
public void onReceive(Context context, Intent intent)
{
farlo partire, sulla pressione del bottone butto- String strAction = intent.getAction();
Log.v(TAG, “onReceive:” + strAction);
nApply come mostrato nel Listato 4.
Anche in questo caso dovremo aggiornare il file if (intent.getAction().equals(ACTION_START))
{
AndroidManifest.xml informandolo sull’utilizzo Log.v(TAG, “ACTION_START”);
di questa nuova Activity e per passare l’infor- }
else if (intent.getAction().equals(ACTION_STOP))
mazione acquisita nella TextBox possiamo ri- {
correre alle SharedPreferences viste nella puntata Log.v(TAG, “ACTION_STOP”);
}
CORSO

precedente; il numero di telefono verrà memo-


else if (intent.getAction().equals(ACTION_OPTION))
rizzato nell’Activity per poi essere recuperato {
ed utilizzato nel metodo onUpdate() del Widget Log.v(TAG, “ACTION_OPTION”);
} else {
stesso. super.onReceive(context, intent);
}
}
INTEGRAZIONE CON GSMCONTROLLER
Vediamo ora come aggiungere un Widget
personalizzato alla nostra applicazione GSM- utilizzato in precedenza, aggiungendone altri
Controller. per visualizzare eventualmente altri dati con
L’obiettivo è quello di aumentare visibilità maggior dettaglio. Questa volta rinomineremo il
al nostro servizio dotandolo di un controllo file con le informazioni del Widget in gsm_wid-
maggiore senza dover necessariamente riaprire get_provider.xml, aggiorneremo opportunamente
ogni volta l’applicazione in questione. Possiamo il file AndroidManifest.xml sulla base delle modi-
ad esempio fare in modo che nel Widget vi sia fiche viste prima, ma senza utilizzare l’Activity
un bottone per attivare direttamente il servizio, di configurazione. Possiamo adesso compilare
un altro per stopparlo e un terzo per accedere tutto e scaricare sul dispositivo l’applicazione
alle opzioni. Allo stesso modo possiamo fare in che partirà normalmente, ma rispetto a pri-
modo che il servizio dell’applicazione GSMCon- ma, adesso abbiamo a disposizione un nuovo
troller invii, esso stesso, determinate informazio- Widget che verrà identificato con la stessa icona
ni al Widget, come la distanza dal punto geogra- dell’applicazione.
fico prestabilito - aggiornata di volta in volta in La parte più interessante da analizzare è invece
tempo reale – o il numero di telefono corrente al l’interazione tra il nostro Widget e l’applicazio-
quale verrà diretta la chiamata in caso di scuo- ne, perché vogliamo essere in grado di poterla
timento ripetuto del dispositivo oltre allo stato controllare tramite il Widget in primo piano, ma
del servizio ed altri dati utili. anche di permettere all’applicazione stessa di
Le modifiche da apportare sono relativamente aggiornare il Widget sul suo stato attuale.
poche, potremmo anche utilizzare parte del co- Partiamo inizialmente a modificare il file
dice già scritto per il DemoWidget visto sopra, GsmWidget.java ed in particolare il metodo
semplicemente copiando il file DemoWidget.java onUpdate() che modificheremo come in Fig. 8.
all’interno della cartella src del progetto, rinomi- Non utilizzeremo più la classe Timer per aggior-
nandolo in GsmWidget.java e aggiungendo due nare periodicamente il Widget perché in questo
bottoni al layout grafico (widgetmain.xml). caso non sarà necessario, anzi, basterà che il
Per la visualizzazione delle informazioni metodo onUpdate() sia chiamato una volta sola,
potremo usare lo stesso controllo TextView al momento del lancio del Widget e per esserne

Fig. 10

Elettronica In ~ Aprile 2013 121


CORSO
Fig. 11

il metodo onStart() del servizio GSMService


collegato all’Intent dal quale possiamo ricavare
l’azione associata tramite il metodo getAction()
ed essere in grado di capire quale pulsante
sia stato premuto. Questo è quanto succede
nella Fig. 9 in cui ci troviamo già nel codice del
servizio; in particolare se l’azione ricevuta è la
ACTION_STOP interrompiamo il servizio nel
quale già ci troviamo con la funzione di sistema
stopSelf(). Nulla ci avrebbe vietato di usare il
meccanismo di passaggio parametri dell’Intent
tramite i metodi putExtra() e getExtra() visti
nella scorsa puntata ed anzi, a seconda dei
casi, si possono utilizzare entrambe le strade,
ma l’impiego di queste Action ci offre anche il
vantaggio di gestire la scelta dei pulsanti anche
all’interno della classe Widget grazie al metodo
onReceive() (nel file GsmWIdget.java) che è visibile
nel Listato 5 in cui possiamo implementare
sicuri imposteremo il valore updatePeriodMillis ulteriore codice di controllo. Per far sì che la
a zero. Dovremo però associare ai tre bottoni le onReceive() sia in grado di ‘trappare’ queste Ac-
azioni desiderate e quindi creeremo due Intent tion occorre registrarle nel file AndroidManifest.
per attivare e stoppare il servizio GSMCon- xml come mostrato in Fig. 10, all’interno del tag
troller ed un Intent per chiamare l’Activity che <receiver> e pertanto occorre che le Action siano
gestirà le opzioni dell’applicazione principale (la dichiarate pubbliche nella classe del Widget.
PreferenceActivity GsmPreferences) già scritta ed Infine, una volta che nel servizio della nostra
analizzata nella puntata precedente. applicazione abbiamo ricavato l’oggetto _remo-
Come già fatto per il Widget di esempio, rica- teView riferito ai controlli presenti nel Widget,
viamo i PendingIntent dai corrispondenti Intent come in Fig. 9, possiamo utilizzarlo per aggior-
appena creati e li associamo ai bottoni tramite il nare il Widget con i dati che ci vengono forniti
metodo setOnClickPendingIntent(), infine aggior- nel tempo dal servizio; ad esempio se volessimo
niamo le stringhe di testo nelle due TextView visualizzare sulla TextView del Widget la loca-
tramite setTextVIewText() e i controlli per rende- zione corrente o la distanza dal punto prefissato
re valide le modifiche con updateAppWidget(). in cui partirà la chiamata verso lo Shield GSM
Da notare come tutti gli Intent vengano creati di Arduino, potremmo farlo tranquillamente
passando il nome della classe corrispondente al solito modo, usando il metodo setTextView-
(GSMService per il servizio e GSMPreferences Text() seguito da updateAppWidget() ogni qual
per l‘Activity) mentre per gli Intent che gesti- volta venga chiamata la _locationListener che è
scono lo start e lo stop del servizio si debba in attesa di una notifica dal sistema operativo di
usare PendingIntent.getService() e per l’Activity cambiamento di locazione. Il risultato finale sarà
occorra usare PendingIntent.getActivty(). Infine, come in Fig. 11.
non dimentichiamo di usare un requestcode (il
secondo paramentro di getService e getActivity) CONCLUSIONI
diverso per la creazione dei vari PendingIntent, Con questa puntata concludiamo il corso di pro-
altrimenti tutti i bottoni avranno una stessa azio- grammazione di Android, consapevoli di non
ne associata. Noterete anche come ogni Intent aver approfondito alcuni argomenti di carattere
abbia associata una stringa diversa e personaliz- prettamente informatico, talvolta senza scen-
zabile, tramite il metodo setAction(); nel nostro dere troppo nei dettagli, per prediligere il lato
caso sono ACTION_START, ACTION_STOP e “hardware” dell’opera; speriamo vivamente di
ACTION_OPTION e servono per identificare aver fornito comunque le principali basi e qual-
l’Intent corrispondente. Ogni volta che viene che spunto per i vostri futuri progetti elettronici
g
premuto uno dei tre pulsanti, viene chiamato nel mondo Android.

122 Aprile 2013 ~ Elettronica In

Potrebbero piacerti anche