SMS is a hugely popular and important means of communication for mobile devices.
SMS is used to send simple text messages and small amounts of data. Android includes a built-in SMS application that allows users to view received SMS messages and send mes- sages (including replying to received messages). Along with the built-in user-facing sup- port and the related ContentProvider for interacting with the built-in system, the SDK provides APIs for developers to be able to send and receive messages programmatically.
Listing 7.6 Catching and aborting an outgoing call
B
Create broadcast receiver
C Define constant for NEW_OUTGOING_CALL
Define constant for D
PHONE_NUMBER
E Override onReceive F
Filter Intent for action
F
Get Intent extras data
H
Show quick message
I
Abort Intent
To explore this support we are going to look at both sides of the coin, sending and receiving.
The unadorned screen in figure 7.4 shows the SMS-related Activity we will build in the Tele- phonyExplorer application.
To get started working with SMS, we will send SMS messages using the support provided by the SmsManager.
7.4.1 Sending SMS messages
The android.telephony.gsm subpackage con- tains the SmsManager and SmsMessage classes.
These are our SMS friends. The SmsManager is used to define many important SMS-related constants, and it contains the sendData- Message, sendMultipartTextMessage, and sendTextMessage methods.
In listing 7.7 we have an example from our TelephonyExplorer application of using the SMS manager to send a simple text message.
// . . . start of class omitted for brevity private Button smsSend;
private SmsManager smsManager;
@Override
public void onCreate(Bundle icicle) { super.onCreate(icicle);
this.setContentView(R.layout.smsexample);
// . . . other onCreate view item inflation omitted for brevity this.smsSend = (Button) findViewById(R.id.smssend_button);
this.smsManager = SmsManager.getDefault();
final PendingIntent sentIntent = PendingIntent.getActivity(
this, 0, new Intent(this, SmsSendCheck.class), 0);
this.smsSend.setOnClickListener(new OnClickListener() { public void onClick(View v) {
String dest = smsInputDest.getText().toString();
if (PhoneNumberUtils.
isWellFormedSmsAddress(dest)) { smsManager.sendTextMessage(
smsInputDest.getText().toString, null, smsInputText.getText().toString(),
Listing 7.7 Using the SmsManager to send SMS messages
B Get SmsManager handle
Create PendingIntent for post action
C
D Check destination is valid
Figure 7.4 An Activity that sends SMS messages and an example of an alert based on a received SMS message
sentIntent, null);
Toast.makeText(SmsExample.this, "SMS message sent",
Toast.LENGTH_LONG).show();
} else {
Toast.makeText(SmsExample.this, "SMS destination invalid - try again", Toast.LENGTH_LONG).show();
} } });
}
The first thing we need to do in regard to working with SMS messages is obtain an instance of the SmsManager, which is done with the static getDefault method B. The manager will be used later to send the message. Before we can do that, though, we need to create a PendingIntent (which will be used as a parameter in the send method coming up).
A PendingIntent can specify an Activity, Broadcast, or Service that it requires. In our case we are using the getActivity method, which denotes an Activity, and then we are specifying the context, request code (which is unused), the Intent, and addi- tional flags C. The flags indicate whether or not a new instance of the referenced Activity (or Broadcast or Service) should be created if one does not already exist.
Once we have a PendingIntent, we check that the destination address is valid for SMS (using another method from PhoneNumberUtils) D, and we send the message using the manager’s sendTextMessage method E.
This send method takes in several parameters, one of which can be confusing. The signature of this method is as follows:
sendDataMessage(String destinationAddress, String scAddress, short destinationPort, byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent)
The destinationAddress is simple; this is the phone number you want to send the message to. The scAddress is the tricky one. This is not meant to be the source address, but rather it indicates the internal service center address on the network; this
Send message
E
What is a PendingIntent?
A PendingIntent is a specification of a future intent. It is basically a way for you to pass a future Intent to another application and allow that application to execute that Intent as if it had the same permissions as your application, whether or not your application is still around when the Intent is eventually invoked. Remember the Ac- tivity lifecycle and the separate process logic that the platform uses. A Pendin- gIntent provides a means for applications to, in essence, work “beyond the grave”
for a particular Intent. Even after an owning application that creates a PendingIn- tent has been killed, that Intent can still be run later.
should be left null in most cases (which uses the default). The destinationPort is also simple; it’s the port. The data is the payload of the message. Finally, the sent- Intent and deliveryIntent are separate PendingIntent instances that are fired when the message is successfully sent and received, respectively.
Much like the permissions we listed in table 7.1 in reference to phone permissions, SMS-related tasks also require manifest permissions. The SMS-related permissions are shown in table 7.2.
Along with sending text and data messages using this basic pattern, you can create an SMS-related BroadcastReceiver to receive incoming SMS messages.
7.4.2 Receiving SMS messages
Receiving an SMS message programmatically is done through receiving a broadcast on the Android platform. To demonstrate this with our TelephonyExplorer application, we are again going to implement a receiver, as shown in listing 7.8.
public class SmsReceiver extends BroadcastReceiver { public static final String SMSRECEIVED = "SMSR";
private static final String SMS_REC_ACTION =
"android.provider.Telephony.SMS_RECEIVED";
@Override
public void onReceive(fContext context, Intent intent) { if (intent.getAction().
equals(SmsReceiver.SMS_REC_ACTION)) { StringBuilder sb = new StringBuilder();
Bundle bundle = intent.getExtras();
if (bundle != null) {
Object[] pdus = (Object[]) bundle.get("pdus");
for (Object pdu : pdus) { SmsMessage smsMessage =
SmsMessage.createFromPdu((byte[]) pdu);
sb.append("body - "
+ smsMessage.getDisplayMessageBody());
} }
Table 7.2 SMS-related manifest permissions and their purpose
Phone-related permission Purpose
android.permission.RECEIVE_SMS Allow application to monitor incoming SMS messages android.permission.READ_SMS Allow application to read SMS messages
android.permission.SEND_SMS Allow application to send SMS messages
android.permission.WRITE_SMS Write SMS messages to the built-in SMS provider (not related to sending messages directly)
Listing 7.8 Creating an SMS-related BroadcastReceiver
B Extend BroadcastReceiver
C
Define constant SMS_RECEIVED action
D Filter for action in receiver
Get pdus from Intent Bundle
E
Create SmsMessage from pdus
F
G Get message body for display
Toast.makeText(context, "SMS RECEIVED - "
+ sb.toString(), Toast.LENGTH_LONG).show();
} } }
To react to an incoming SMS message we once again are creating a BroadcastReceiver by extending that class B. Our receiver defines a local constant for the Intent action it wants to catch, in this case android.provider.Telephony.SMS_RECEIVEDC.
Once the class setup is ready, we filter for the action we want in the onReceive method D, and we get the SMS data from the Intent “extras” Bundle using the key pdus E. PDU, or Protocol Data Unit, is the term that describes the data packet in SMS messages. In this case the platform is using the String key pdus (we discovered this by trial and error, by getting the key Set from the Bundle and iterating it). For every pdu Object we then construct an SmsMessage by casting the data to a byte array F. Once this is in SmsMessage form, we can work with the methods on that class, such as get- DisplayMessageBodyG.
Sending and receiving messages in SMS form completes our exploration of the telephony APIs.