Gli SMS sono la data application più utilizzata al mondo. Per chi si occupa di sviluppo software su piattaforme mobile è essenziale saper utilizzare i servizi a disposizione per la gestione degli SMS: questo articolo ha lo scopo di introdurre i concetti e i meccanismi fondamentali con i quali possiamo gestire gli SMS all'interno delle nostre applicazioni Android.
Lo scopo di queste lezioni è quello di approfondire la gestione degli SMS in Android: come nell’articolo sulla gestione del database, tutto il codice e gli esempi che vedremo saranno specifici e incentrati su questo determinato argomento. Rimandiamo alla guida allo sviluppo di app per Android per le argomentazioni introduttive, come la configurazione dell'ambiente di sviluppo e la creazione di un progetto.
Come vedremo più avanti in questo articolo, gli strumenti che abbiamo a disposizione permettono di tracciare in maniera completa e puntuale l'intero ciclo di vita dell'SMS: questo ci fornisce la possibilità di controllare quanto sta avvenendo ed assegnare così un comportamento alla nostra app coerente con la situazione (ad esempio possiamo fornire un feedback preciso all'utente sull'effettivo inoltro e ricezione dell'SMS da parte del destinatario).
Testare la nostra app con l’emulatore
Rimandando alla lezione specifica della nostra guida l’approfondimento dei software da utilizzare per lo sviluppo, soffermiamoci per un istante sull’emulatore dell’SDK. Quando trattiamo la gestione degli SMS l'emulatore diventa un utile strumento per testare il comportamento della nostra applicazione sia per quanto riguarda gli SMS inviati sia per quanto riguarda quelli in ricezione.
Prima di approfondire il discorso tecnico vediamo dunque come sfruttare l'emulatore per simulare l'invio e la ricezione di un SMS.
Per simulare l'invio di SMS abbiamo chiaramente bisogno di almeno 2 emulatori, uno per inviare l'SMS (emulatore A) ed uno per riceverlo (emulatore B): è l'Android Debugging Bridge (adb) che supporta l'invio di messaggi SMS tra istanze multiple di emulatori. L'adb è un potente tool che si utilizza da linea di comando e che permette di comunicare sia con gli emulatori sia con il nostro device Android (purché connesso al computer).
L’importanza dell’Android Debugging Bridge
L'adb è un programma client-server composto da 3 elementi:
- un client
- un server daemon adb
- un daemon
L'adb è un tool che viene rilasciato insieme all'SDK (Software Development Kit) di Android, e lo possiamo trovare sotto la directory <sdk>/platform-tools/. Per chi volesse approfondire la conoscenza e lo studio dell'Android Debug Bridge è disponibile sul sito ufficiale degli sviluppatori Android una pagina di supporto nella quale possiamo approfondire il funzionamento del tool e studiarne i comandi principali a disposizione. Consiglio vivamente di dare almeno un'occhiata veloce, perchè l'adb è uno strumento davvero potente che spesso viene sottovaluto o ignorato.
Giusto per avere un'idea dell'importanza di questo tool e delle funzionalità che ci mette a disposizione possiamo riassumere brevemente alcune tra le caratteristiche più interessanti:
- permette di effettuare query
- permette di esaminare da shell
- fermare il server dell'adb killare adb
Simulare l’invio degli SMS
Ma torniamo alla nostra app. Supponiamo dunque di voler spedire un messaggio SMS dall'emulatore A all'emulatore B: tutto ciò che dobbiamo fare è specificare il numero di porta dell'emulatore di destinazione, proprio come facciamo compilando il campo “destinatario” quando inviamo un nuovo messaggio SMS. Android si occuperà di inoltrare automaticamente il nostro messaggio all'istanza di destinazione dell'emulatore, messaggio che a questo punto sarà gestito come un normale SMS.
Il numero di porta, supponendo che all'emulatore A (sender) e all'emulatore B (receiver) siano state assegnate rispettivamente le porte 5556 e 5554, lo troviamo nel titolo della finestra in cui viene visualizzato l'emulatore. La figura 1.1 mostra l'emulatore B, il destinatario dell'SMS:

Figura 1.1: il titolo dell'emulatore con la rispettiva porta di comunicazione
Come mostrato in figura, nell'angolo in alto a sinistra possiamo vedere la porta in cui sta “girando” l'emulatore (5554), pertanto sarà sufficiente trattare il numero di porta “5554” come se fosse il numero di telefono del destinarlo.
Android fornisce principalmente due metodi per inviare SMS da una nostra applicazione: la prima metodologia sfrutta gli Intents mentre la seconda si affida alle funzionalità dell'SMSManager.
Invio degli SMS con gli Intent e con il client nativo
Gli Intent sono descrizioni astratte di operazione che possono essere svolte (si dia un’occhiata alla reference). Tecnicamente gli Intents sono messaggi asincroni che permettono ai componenti Android di richiamare e richiedere funzionalità gestite da altri componenti Android.
L'esempio più comune di utilizzo è quello che permette ad una Activity di richiedere al sistema Android di far partire un'altra Activity: questa è una tipologia di chiamata che eseguiamo praticamente dal primo progetto Android che sviluppiamo, pertanto gli Intent sono così comodi che in alcuni casi non ci accorgiamo nemmeno di usarli.
Gli Intent sono quindi una componente molto importante della piattaforma Android, perché permettono di combinare insieme diverse operazioni in modo molto trasparente, faciltando notelvolmente lo svolgere di alcuni processi.
Gli Intent possono anche essere utilizzati per segnalare al sistema Android l'accadere di determinati eventi, ed altri componenti possono essere “registrati” a tali eventi per raccogliere particolari notifiche e svolgere così un compito specifico.
La potenza degli Intent non si ferma qui. Questi infatti possono anche contenere informazioni e dati che possono essere condivisi e utilizzati dal componente ricevente: ad esempio la nostra applicazione può richiamare attraverso un Intent il componente browser passandogli un URI (Universal Resource Identifiers) da aprire.
La piattaforma Android mette a disposizione due tipologie diverse di Intent, quelli espliciti e quelli impliciti. Li vedremo nella seconda parte di questo articolo in pubblicazione la prossima settimana.
Intent espliciti
Gli Intent espliciti specificano il preciso componente che deve essere chiamato dalla piattaforma Android utilizzando come identificatore la classe Java.
Come specificato poc'anzi, uno degli Intent che utilizzeremo di più durante lo sviluppo delle nostre applicazioni Android è quello che permette ad una Activity
di "chiamare e far partire" una seconda Activity
. Questo è un buon esempio di Intent esplicito, il cui codice potrebbe essere simile al seguente:
Intent intent = new Intent(ActivityA.this, ActivityB.class);
startActivity(intent);
codice 1.1
Come mostrato nell'esempio, il codice è molto semplice e praticamente autoesplicativo: creiamo un oggetto Intent passandogli come primo parametro una reference
Activity
ActivityA
reference
Activity
ActivityB
startActivity()
Activity
Activity
Una caratteristica importante degli Intent è la facilità con la quale permettono il passaggio di parametri e la condivisione di dati ed informazioni. Grazie al metodo "putExtra()
ActivityA
ActivityB
Intent intent = new Intent(ActivityA.this, ActivityB.class);
intent.putExtra("nomeValore1", "Valore");
intent.putExtra("nomeValore2", "Valore");
startActivity(intent);
codice 1.2
Come mostrato nel codice 1.1 e 1.2, grazie ai meccanismi degli Intent possiamo facilmente richiamare Activity
Intent impliciti
Gli Intent impliciti non specificano direttamente quali componenti Android devono essere richiamati per svolgere un determinato compito: essi specificano esclusivamente l'azione che deve essere svolta e opzionalmente un URI che eventualmente è utile per svolgere l'azione richiesta.
L'esempio tipico di Intent implico è quello che viene richiamato quando vogliamo visualizzare una pagina web:
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.marcolecce.com/blog/"));
codice 1.3
Il codice 1.3 comunica alla piattaforma Android che vogliamo visualizzare (Intent.ACTION _VIEW
http://www.marcolecce.com/blog/
Se tale ricerca restituisce un solo componente, allora è il sistema stesso che automaticamente lo esegue, altrimenti se la ricerca identifica più componenti registrati per l'azione richiesta allora il sistema visualizzerà all'utente un dialog
Utilizzare gli intent per spedire SMS
Ora dovremmo avere un'idea un pò più chiara di cosa sono e come funzionano gli Intent della piattaforma Android: possiamo allora proseguire il discorso principale e vedere come utilizzare gli Intent per inviare i nostri SMS.
Il codice che analizziamo è il seguente:
Intent smsIntent = new Intent(Intent.ACTION_SENDTO, Uri.parse("sms:123456789"));
smsIntent.putExtra("sms_body", "Un saluto SMS da Marco Lecce");
startActivity(smsIntent);
codice 1.4
Come dimostra chiaramente il codice 1.4, inviare SMS è in realtà molto semplice: la prima cosa da fare è creare un oggetto Intent (smsIntent
Intent.ACTION_SENDTO
Il passo successivo è quello di utilizzare un parametro "sms_body" per passare il testo dell'SMS all'Intent: a questo punto non ci rimane che richiamare il metodo startActivity
Ovviamente il codice 1.4 presenta uno dei casi più semplici di invio di SMS: per non annoiarci troppo possiamo aggiungere un pochetto di complessità e compiere un piccolo passo in avanti, analizzando come sia possibile allegare dei file al nostro SMS creando quello che a tutti gli effetti diventa un MMS (Multimedia Messaging Service).
Vediamo il codice:
// URI del contenuto da allegare
Uri attached_Uri = Uri.parse("content://media/external/images/media/myphoto");
// Creiamo un nuovo Intent per l'MMS
Intent mmsIntent = new Intent(Intent.ACTION_SEND, attached_Uri);
mmsIntent.putExtra("sms_body", "Un saluto MMS da Marco Lecce");
mmsIntent.putExtra("address", "123456789");
mmsIntent.putExtra(Intent.EXTRA_STREAM, attached_Uri);
mmsIntent.setType("image/png");
startActivity(mmsIntent);
codice 1.5
La prima cosa da fare è definire una URI per la risorsa che vogliamo allegare al nostro MMS (attached_Uri
Utilizziamo il metodo putExtra()
extra stream
Intent
setType()
A questo punto possiamo richiamare il metodo startActivity()
Invio degli SMS utilizzando l'SMSManager
La seconda metodologia messa a disposizione dalla piattaforma Android per l'invio di SMS è quella che si basa sull'utilizzo dell'SMSManager
.
L'SMSManager
mette a disposizione dello sviluppatore un insieme di funzionalità che permettono di sostituire in toto
quelle messe a disposizione dall'applicazione nativa della piattaforma Android: in altre parole utilizzando l'SMSManager
possiamo rimpiazzare l'applicazione nativa degli SMS per l'invio dei messaggi, per gestirne la ricezione o ancora per impletare un layer
di trasporto dati.
Come ormai siamo abituati con altri componenti della piattaforma Android, l'utilizzo dell'SMSManger
è molto intuitivo, e con poche righe di codice possiamo raggiungere pienamente il nostro obiettivo; anche in questo caso insomma il codice è sostanzialmente autoesplicativo:
SmsManager smsManager = SmsManager.getDefault();
String sendTo = "0123456789";
String message = "Un saluto da Marco Lecce con l'SMS Manager! :)";
smsManager.sendTextMessage(sendTo, null, message, null, null);
codice 1.6
La prima coda da fare è ottenere una reference
SMSManager
SmsManger.getDefault()
sendTo
message
A questo punto possiamo procedere all'invio dell'SMS utilizzando il metodo sendTextMessage()
service center
null
service center
sendIntent
deliveryIntent
Gli ultimi due parametri ci permettono di specificare gli Intent per tracciare la trasmissione e il successo della consegna dell'SMS: vedremo tra poco come sfruttare questi strumenti per tracciare e controllare l'invio dell'SMS.
Per chi volesse approfondire o chiarirsi ulteriormente le idee sul metodo sendTextMessage()
documentazione ufficiale
Per poter inviare i messaggi SMS la nostra applicazione deve richiederne esplicitamente i permessi. Questo significa che dobbiamo aggiungere al file manifest dell'applicativo la richiesta per utilizzare i permessi SEND_SMS:
<uses-permission android:name="android.permission.SEND_SMS"/>
A questo punto possiamo testare l'invio degli SMS dal nostro applicativo: come abbiamo visto nella parte introduttiva di questo articolo possiamo utilizzare più istanze di emulatori per simulare l'invio e la ricezione degli SMS e osservare così il comportamente della nostra app.
Tracciare l'invio e l'avvenuta ricezione degli SMS
Come abbiamo introdotto nella sezione precedente, l'SMSManager
utilizza il metodo sentTextMessage()
per gestire l'invio di SMS, ma in realtà questo metodo è decisamente più potente di quello che potrebbe sembrare ad una prima occhiata.
Infatti possiamo creare dei Pending Intent
per gestire azioni specifiche e utilizzare i Broadcast Receiver
per "ascoltare" le richieste generate da questi Intent: ora, se passiamo gli Intent in questione al metodo sentTextMessage()
l'applicativo, opportunamente "registrato" ai Broadcast Receiver
specifici, sarà in grado di tenere traccia di quanto sta accadendo al nostro SMS. Per comodità riportiamo nuovamente la signature
del metodo sentTextMessage()
:
sendTextMessage(sendTo, null, message, null, null);
Gli ultimi due parametri ci permettono di specificare i Pending
Intent
Pending Intent
Broadcast
Receiver
result code
- Activity.RESULT_OK
- SmsManager.RESULT_ERROR_GENERIC_FAILURE
- SmsManager.RESULT_ERROR_RADIO_OFF
- SmsManager.RESULT_ERROR_NULL_PDU
Activity.RESULT_OK
result code
SmsManager.RESULT_ERROR_GENERIC_FAILURE
SmsManager.RESULT_ERROR_RADIO_OFF
SmsManager.RESULT_ERROR_NULL_PDU
Protocol Description Unit
Il secondo Pending Intent
Pending
sendTextMessage()
PendingIntent sentPI = PendingIntent.getBroadcast(getApplicationContext(), 0, new Intent("SENT_SMS_ACTION"), 0);
PendingIntent deliverPI = PendingIntent.getBroadcast(getApplicationContext(), 0, new Intent("DELIVERED_SMS_ACTION"), 0);
registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context _context, Intent _intent) {
switch (getResultCode()) {
case Activity.RESULT_OK:
// invio avvenuto con successo
break;
case SmsManager.RESULT_ERROR_GENERIC_FAILURE:
//gestione dell'errore generico
break;
case SmsManager.RESULT_ERROR_RADIO_OFF:
//gestione dell'errore di segnale assente
break;
case SmsManager.RESULT_ERROR_NULL_PDU:
//gestione dell'errore sulla PDU
break;
}
}
},
new IntentFilter("SENT_SMS_ACTION"));
registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context _context, Intent _intent) {
//ricezione avvenuta correttamente: posso fornire un feedback //appriopriato all'utente
}
},
new IntentFilter("DELIVERED_SMS_ACTION"));
smsManager.sendTextMessage(sendTo, null, myMessage, sentPI, deliverPI);
codice 1.8
Il codice 1.8 mostra un esempio di quanto abbiamo introdotto poc'anzi: la prima cosa che facciamo è definire i due Pending Intent
sentPI
deliverPI
Successivamente registriamo due BroadcastReceiver
receiver
IntentFilter
Il passo successivo è definire il comportamento della nostra applicazione in fase di invio e ricezione dell'SMS. Vediamo il primo caso.
Nel receiver
BroadcastReceiver
override
onReceive()
BroadcastReceiver
Intent broadcast
.
A questo punto abbiamo "insegnato" alla nostra app come comportarsi quando l'invio dell'SMS è avvenuto correttamente e quando arriva la conferma della ricezione da parte del destinatario.
Normalmente questo metodo è chiamato all'interno del thread
thread
registerReceiver()
Quando il metodo onReceive()
thread
Nella pratica la sostanza di quanto abbiamo visto non cambia per la configurazione del receiver
onReceive()
Dialog
feedback
label
Come sappiamo i messaggi SMS normalmente hanno un limite massimo di caratteri, pertanto i messaggi con un numero di caratteri maggiore del limite consetito (in genere 160 caratteri) devono essere opportunamente sezionati in più messaggi.
Ovviamente Android mette a disposizione uno strumento davvero facile ed immediato per gestire questa problematica: il metodo divideMessage()
SMSManger
divideMessage
ArrayList
ArrayList
divideMessage()
ArrayList
Una volta sezionato opportunamente il nostro SMS possiamo utilizzare il metodo sendMultipartTextMessage()
SMSManager
array
String message = […];
ArrayList<String> messageArray = smsManager.divideMessage(message);
ArrayList<PendingIntent> sentIntents = new ArrayList<PendingIntent>();
for (int i = 0; i < messageArray.size(); i++) {
sentIntents.add(sentPI);
}
smsManager.sendMultipartTextMessage(sendTo, null, messageArray, sentIntents, null);
codice 1.9
Il codice 1.9 mostra un esempio di utilizzo del metodo sentMultipartTextMessage()
divideMessage()
message
ArrayList
Nell'esempio ho inserito un semplice ciclo for
Pending Intent
item
ArrayList
sentIntent
deliveryIntent
sendMultipartTextMessage()
ArrayList
Pending Intent
Bene, ora siamo in grado di inviare messaggi SMS e gestirne il ciclo di vita. Lo scopo dell'articolo era quello di analizzare i meccanismi e le tecniche messe a disposizione dalla piattaforma Android per fornire alla nostra applicazione la possibilità di gestire e controllare l'invio degli SMS.
Come abbiamo visto negli esempi il codice richiesto per implementare queste funzionalità è chiaro, di facile lettura e sicuramente composto da poche righe: questo dimosta ancora una volta la potenza e l'accurata progettazione che caratterizzano la piattaforma Android, la quale mette a disposizione degli sviluppatori strumenti tali da permettere di realizzare in poco tempo applicazioni praticamente di ogni genere.