La comunicazione via SMS potrebbe apparire un po' old-style in questi tempi così “social”. Eppure si tratta della forma di messaggistica più diffusa al
mondo, utilizzata con disinvoltura da persone appartenenti ad ogni fascia di età e disponibile su ogni dispositivo che disponga di funzionalità telefoniche.
Relativamente al mondo Android, l'utilità in campo professionale dell'interazione via SMS è ancora ampia. Per fare un esempio, esistono molti dispositivi
per la domotica, come caldaie, sistemi antifurto, etc. che notificano lo stato dell'impianto o il verificarsi di situazioni particolari via SMS.
Ecco: in un caso del genere un'app Android potrebbe rimanere in attesa di tali messaggi, ed utilizzarli interagendo con l'utente.
In questo capitolo, si affronterà proprio l'invio e la ricezione di SMS in un'app Android.
BroadcastReceiver
Delle quattro componenti che costituiscono un'applicazione Android, finora ne abbiamo viste tre: Activity, ContentProvider e Service. Qui introdurremo la
quarta: il BroadcastReceiver.
Si tratta di un oggetto che si registra presso il sistema operativo, per essere allertato non appena si verifica una determinata circostanza.
L'attivazione avviene mediante il classico meccanismo degli Intent, includendo altri elementi già visti come il PendingIntent e gli IntentFilters.
Un BroadcastReceiver - utile in tantissime circostanze, non solo per gli SMS – viene creato estendendo la classe omonima ed implementando il metodo
onReceive
. Tale metodo riceve in input due parametri: il Context per l'interazione con il sistema e un Intent. Quest'ultimo contiene tutte le informazioni riguardanti l'evento.
Affinchè funzioni, il BroadcastReceiver deve essere registrato nel sistema e ciò può essere fatto in due modi:
- via XML, nell'AndroidManifest, mediante il tag
<receiver>
; - in Java, usando il metodo
registerReceiver
.
In questo capitolo ci avvarremo di entrambe le modalità di registrazione.
Invio di SMS
Per prima cosa impareremo ad inviare SMS da un'app. Per fare ciò, utilizzeremo una classe di sistema denominata SmsManager, adoperandola per due operazioni significative:
- ci faremo resituire un riferimento all'SmsManager;
- invieremo per suo tramite un messaggio di testo, con il numero del destinatario ed il testo.
Da non dimenticare che è necessario dichiarare l'apposita permission:
<uses-permission android:name="android.permission.SEND_SMS"/>
Per completezza, queste due operazioni saranno integrate con l'uso di BroadcastReceiver per notificare il successo nell'invio.
String numero = "3301234567"; // di pura fantasia
String testo = "Ciao, come stai?";
SmsManager smsManager = SmsManager.getDefault();
PendingIntent inviato = PendingIntent.getBroadcast(getApplicationContext(), 0, new Intent("SMS_INVIATO"), 0);
PendingIntent consegnato = PendingIntent.getBroadcast(getApplicationContext(), 0, new Intent("SMS_CONSEGNATO"), 0);
suInvio=new BroadcastReceiver()
{
@Override
public void onReceive(Context arg0, Intent arg1)
{
if (getResultCode()==Activity.RESULT_OK)
Toast.makeText(arg0, "SMS inviato correttamente", Toast.LENGTH_LONG).show();
else
Toast.makeText(arg0, "Errore in invio", Toast.LENGTH_LONG).show();
}
};
suConsegna=new BroadcastReceiver()
{
@Override
public void onReceive(Context arg0, Intent arg1)
{
if (getResultCode()==Activity.RESULT_OK)
Toast.makeText(arg0, "SMS consegnato", Toast.LENGTH_LONG).show();
else
Toast.makeText(arg0, "Errore", Toast.LENGTH_LONG).show();
}
};
registerReceiver(suInvio, new IntentFilter("SMS_INVIATO"));
registerReceiver(suConsegna, new IntentFilter("SMS_CONSEGNATO"));
smsManager.sendTextMessage(numero, null, testo, inviato, consegnato);
Il codice proposto è una modalità completa per gestire l'invio di un SMS. Il numero del destinatario ed il testo sono contenuti in due stringhe, che abbiamo chiamato rispettivamente numero
e testo
. Le due righe di codice essenziali sono:
SmsManager smsManager = SmsManager.getDefault();
smsManager.sendTextMessage(numero, null, testo, inviato, consegnato);
La prima riga individua l'SmsManager e la seconda si occupa dell'invio. Tutto ciò che si trova tra loro serve a gestire la notifica di invio e consegna.
Ognuna di queste due fasi viene gestita con:
- un PendingIntent che congela un Intent relativo ad azioni personalizzate (
SMS_INVIATO
per l'invio eSMS_CONSEGNATO
per la consegna), che sarà inviato al
momento del completamento di ognuna delle due fasi; - un BroadcastReceiver che si registrerà per essere informato del lancio del PendingIntent corrispondente.
Nell'Activity sono stati definiti due membri di classe BroadcastReceiver:
private BroadcastReceiver suInvio = null;
private BroadcastReceiver suConsegna = null;
La loro inizializzazione vera e propria è stata effettuata in fase di invio. Il metodo onReceive
in questo caso apre un Toast di
notifica.
Affinchè il BroadcastReceiver sia attivo è necessario che venga registrato. Visto che ci sono due coppie BroadcastReceiver-PendingIntent, avverranno due
registrazioni, ognuna delle quali assocerà un Receiver con il corrispondente IntentFilter relativo all'azione richiesta:
registerReceiver(suInvio, new IntentFilter("SMS_INVIATO"));
registerReceiver(suConsegna, new IntentFilter("SMS_CONSEGNATO"));
Infine possiamo richiedere che nel metodo onPause
vengano cancellate le registrazioni dei BroadcastReceiver, nel seguente modo:
@Override
protected void onPause()
{
super.onPause();
unregisterReceiver(suInvio);
unregisterReceiver(suConsegna);
}
Ricezione
Per quanto riguarda la ricezione di SMS, non aggiungeremo concetti nuovi, dal momento che utilizzeremo nuovamente la classe BroadcastReceiver.
Prima di tutto, specifichiamo le permission nel Manifest; questa volta occorrono le seguenti:
<uses-permission android:name="android.permission.RECEIVE_SMS"/>
<uses-permission android:name="android.permission.READ_SMS" />
Poi creiamo una classe Java che eredita da BroadcastReceiver:
public class IncomingSMS extends BroadcastReceiver
{
@Override
public void onReceive(Context context, Intent intent)
{
Bundle extras = intent.getExtras();
if ( extras != null )
{
Object[] smsExtra = (Object[]) extras.get( "pdus");
for ( int i = 0; i < smsExtra.length; ++i )
{
SmsMessage sms = SmsMessage.createFromPdu((byte[])smsExtra[i]);
String testo = sms.getMessageBody().toString();
String numero = sms.getOriginatingAddress();
/* Svolgimento di operazioni
sul messaggio*/
}
}
}
}
Infine registriamo presso il sistema il BroadcastReceiver, inserendo un apposito tag nel manifest:
<receiver android:name=".IncomingSMS">
<intent-filter>
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
</receiver>
L'azione inclusa nell'IntentFilter, questa volta, non è definita da noi come nel caso dell'invio. Rimaniamo in attesa dell'evento SMS_RECEIVED definito nel
package Telephony.
Per quanto riguarda più in dettaglio il codice scritto in Java, tra gli Extras dell'Intent ce n'è uno con etichetta pdus. Dobbiamo trasformare le varie PDU
arrivate – in pratica gli SMS grezzi - in oggetti di classe SmsMessage. Questo permetterà anche di poterli leggere in maniera agevole con metodi ad hoc.
Dopo la conversione, infatti, per ottenere il testo del messaggio è sufficiente invocare il metodo getMessageBody
, mentre per il
numero del mittente si ricorre a getOriginatingAddress
.