2.2: Part 2 - Sending and Receiving SMS Messages
Contents:
- Task 3: Receive SMS messages with a broadcast receiver
- Coding challenge
- Summary
- Related concept
- Learn more
Task 3. Receive SMS messages with a broadcast receiver
To receive SMS messages, use the onReceive() method of the BroadcastReceiver class. The Android framework sends out system broadcasts of events such as receiving an SMS message, containing intents that are meant to be received using a BroadcastReceiver. You need to add the RECEIVE_SMS
permission to your app's AndroidManifest.xml file.
3.1 Add permission and create a broadcast receiver
To add RECEIVE_SMS
permission and create a broadcast receiver, follow these steps:
Open the AndroidManifest.xml file and add the
android.permission.RECEIVE_SMS
permission below the other permission for SMS use:<uses-permission android:name="android.permission.SEND_SMS" /> <uses-permission android:name="android.permission.RECEIVE_SMS" />
Receiving an SMS message is permission-protected. Your app can't receive SMS messages without the
RECEIVE_SMS
permission line in AndroidManifest.xml.- Select the package name in the Project:Android: view and choose File > New > Other > Broadcast Receiver.
Name the class "MySmsReceiver" and make sure "Exported" and "Enabled" are checked.
The "Exported" option allows your app to respond to outside broadcasts, while "Enabled" allows it to be instantiated by the system.
- Open the AndroidManifest.xml file again. Note that Android Studio automatically generates a
<receiver>
tag with your chosen options as attributes:<receiver android:name= "com.example.android.smsmessaging.MySmsReceiver" android:enabled="true" android:exported="true"> </receiver>
3.2 Register the broadcast receiver
In order to receive any broadcasts, you must register for specific broadcast intents. In the Intent documentation, under "Standard Broadcast Actions", you can find some of the common broadcast intents sent by the system. In this app, you use the android.provider.Telephony.SMS_RECEIVED
intent.
Add the following inside the <receiver>
tags to register your receiver:
<receiver
android:name="com.example.android.smsmessaging.MySmsReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.provider.Telephony.SMS_RECEIVED"/>
</intent-filter>
</receiver>
3.3 Implement the onReceive() method
Once the BroadcastReceiver intercepts a broadcast for which it is registered (SMS_RECEIVED
), the intent is delivered to the receiver's onReceive()
method, along with the context in which the receiver is running.
- Open MySmsReceiver and add under the class declaration a string constant
TAG
for log messages and a string constantpdu_type
for identifying PDUs in a bundle:public class MySmsReceiver extends BroadcastReceiver { private static final String TAG = MySmsReceiver.class.getSimpleName(); public static final String pdu_type = "pdus"; ...
- Delete the default implementation inside the supplied
onReceive()
method. In the blank
onReceive()
method:Add the
@TargetAPI
annotation for the method, because it performs a different action depending on the build version.Retrieve a map of extended data from the intent to a
bundle
.Define the
msgs
array andstrMessage
string.Get the
format
for the message from thebundle
.@TargetApi(Build.VERSION_CODES.M) @Override public void onReceive(Context context, Intent intent) { // Get the SMS message. Bundle bundle = intent.getExtras(); SmsMessage[] msgs; String strMessage = ""; String format = bundle.getString("format"); ...
As you enter
SmsMessage[]
, Android Studio automatically importsandroid.telephony.SmsMessage
.- Retrieve from the bundle one or more pieces of data in the protocol data unit (PDU) format, which is the industry-standard format for an SMS message:
... // Retrieve the SMS message received. Object[] pdus = (Object[]) bundle.get(pdu_type); ...
- If there are messages (
pdus
), check for Android version 6.0 (Marshmallow) and newer versions. You will use this boolean to check if your app needs the deprecated signature createFromPdu(byte[] pdu) for earlier versions of Android:... if (pdus != null) { // Check the Android version. boolean isVersionM = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M); ...
Initialize the
msgs
array, and use its length in thefor
loop:... // Fill the msgs array. msgs = new SmsMessage[pdus.length]; for (int i = 0; i < msgs.length; i++) { // Check Android version and use appropriate createFromPdu. if (isVersionM) { // If Android version M or newer: msgs[i] = SmsMessage.createFromPdu((byte[]) pdus[i], format); } else { // If Android version L or older: msgs[i] = SmsMessage.createFromPdu((byte[]) pdus[i]); } ...
Use createFromPdu(byte[] pdu, String format) to fill the
msgs
array for Android version 6.0 (Marshmallow) and newer versions. For earlier versions of Android, use the deprecated signature createFromPdu(byte[] pdu).Build the
strMessage
to show in a toast message:Get the originating address using the
getOriginatingAddress()
method.Get the message body using the
getMessageBody()
method.Add an ending character for an end-of-line.
... // Build the message to show. strMessage += "SMS from " + msgs[i].getOriginatingAddress(); strMessage += " :" + msgs[i].getMessageBody() + "\n"; ...
- Log the resulting
strMessage
and display a toast with it:... // Log and display the SMS message. Log.d(TAG, "onReceive: " + strMessage); Toast.makeText(context, strMessage, Toast.LENGTH_LONG).show(); ...
The complete onReceive()
method is shown below:
@TargetApi(Build.VERSION_CODES.M)
@Override
public void onReceive(Context context, Intent intent) {
// Get the SMS message.
Bundle bundle = intent.getExtras();
SmsMessage[] msgs;
String strMessage = "";
String format = bundle.getString("format");
// Retrieve the SMS message received.
Object[] pdus = (Object[]) bundle.get(pdu_type);
if (pdus != null) {
// Check the Android version.
boolean isVersionM =
(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M);
// Fill the msgs array.
msgs = new SmsMessage[pdus.length];
for (int i = 0; i < msgs.length; i++) {
// Check Android version and use appropriate createFromPdu.
if (isVersionM) {
// If Android version M or newer:
msgs[i] = SmsMessage.createFromPdu((byte[]) pdus[i], format);
} else {
// If Android version L or older:
msgs[i] = SmsMessage.createFromPdu((byte[]) pdus[i]);
}
// Build the message to show.
strMessage += "SMS from " + msgs[i].getOriginatingAddress();
strMessage += " :" + msgs[i].getMessageBody() + "\n";
// Log and display the SMS message.
Log.d(TAG, "onReceive: " + strMessage);
Toast.makeText(context, strMessage, Toast.LENGTH_LONG).show();
}
}
}
3.4 Run the app and send a message
Run the app on a device. If possible, have someone send you an SMS message from a different device.
You can also receive an SMS text message when testing on an emulator. Follow these steps:
- Run the app on an emulator.
Click the … (More) icon at the bottom of the emulator's toolbar on the right side, as shown in the figure below:
- The extended controls for the emulator appear. Click Phone in the left column to see the extended phone controls:
- You can now enter a message (or use the default "marshmallows" message) and click Send Message to have the emulator send an SMS message to itself.
- The emulator responds with a notification about receiving an SMS message. The app should also display a toast message showing the message and its originating address, as shown below:
Solution Code
Android Studio project: SmsMessaging
Coding challenge
Challenge: Create a simple app with one button, Choose Picture and Send, that enables the user to select an image from the Gallery and send it as a Multimedia Messaging Service (MMS) message. After tapping the button, a choice of apps may appear, including the Messenger app. The user can select the Messenger app, and select an existing conversation or create a new conversation, and then send the image as a message.
The following are hints:
- To access and share an image from the Gallery, you need the following permission in the AndroidManifest.xml file:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
- To enable the above permission, follow the model shown previously in this chapter to check for the
READ_EXTERNAL_STORAGE
permission, and request permission if necessary. - Use the following intent for picking an image:
Intent galleryIntent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI); startActivityForResult(galleryIntent, IMAGE_PICK);
- Override the onActivityResult method to retrieve the intent result, and use
getData()
to get the Uri of the image in the result:protected void onActivityResult (int requestCode, int resultCode, Intent imageReturnedIntent) { ... Uri mSelectedImage = imageReturnedIntent.getData(); }
- Set the image's Uri, and use an intent with
ACTION_SEND
,putExtra()
, andsetType()
:Intent smsIntent = new Intent(Intent.ACTION_SEND); smsIntent.putExtra(Intent.EXTRA_STREAM, mSelectedImage); smsIntent.setType("image/*");
- Android Studio emulators can't pass MMS messages to and from each other. You must test this app on real Android devices.
- For more information about sending multimedia messages, see Sending MMS with Android.
Android Studio project: MMSChallenge
Summary
- To send an intent to an SMS messaging app with a phone number, your app needs to prepare a URI for the phone number as a string prefixed by "smsto:" (as in
smsto:14155551212
). - Use an implicit intent with
ACTION_SENDTO
to launch an SMS app, and set the phone number and message for the intent withsetData()
andputExtra
. - To send SMS messages from within your app, add the
"android.permission.SEND_SMS"
permission to the AndroidManifest.xml file: - Use the sendTextMessage() method of the SmsManager class to send the message, which takes the following parameters:
destinationAddress
: The string for the phone number to receive the message.scAddress
: A string for the service center address, ornull
to use the current default Short Message Service Center (SMSC).smsMessage
: A string for the body of the message to send.sentIntent
: APendingIntent
. If notnull
, this is broadcast when the message is successfully sent or if the message failed.deliveryIntent
: APendingIntent
. If notnull
, this is broadcast when the message is delivered to the recipient.
- Use checkSelfPermission() to determine whether your app has been granted a particular permission by the user. If permission has not been granted, use the requestPermissions() method to display a standard dialog for the user to grant permission.
- Create a broadcast receiver to receive SMS messages using the onReceive() method of the BroadcastReceiver class.
- Add the
"android.provider.Telephony.SMS_RECEIVED"
intent filter between the<receiver>
tags in AndroidManifest.xml to register your receiver for SMS messages. - Use
getExtras()
to get the message from the intent:Bundle bundle = intent.getExtras();
- Retrieve the messages from the PDU format:
Object[] pdus = (Object[]) bundle.get("pdus");
- Use the following
createFromPdu()
signature for Android version 6.0 (Marshmallow) and newer versions:msgs[i] = SmsMessage.createFromPdu((byte[]) pdus[i], format);
- Use the following
createFromPdu()
signature for versions older than Android version 6.0:msgs[i] = SmsMessage.createFromPdu((byte[]) pdus[i]);
- To send an SMS message to an app running in an emulator, click the … (More) icon at the bottom of the emulator's toolbar on the right side, choose Phone, enter a message (or use the default "marshmallows" message), and click Send Message.
Related concept
Learn more
- Android Developer Reference:
- Stack Overflow: Simulating incoming call or sms in Android Studio
- Android blog: Getting Your SMS Apps Ready for KitKat