2.2: Part 2 - Sending and Receiving SMS Messages

Contents:

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:

  1. 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.

  2. Select the package name in the Project:Android: view and choose File > New > Other > Broadcast Receiver.
  3. 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.

  4. 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.

  1. Open MySmsReceiver and add under the class declaration a string constant TAG for log messages and a string constant pdu_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";
       ...
    
  2. Delete the default implementation inside the supplied onReceive() method.
  3. In the blank onReceive() method:

    1. Add the @TargetAPI annotation for the method, because it performs a different action depending on the build version.

    2. Retrieve a map of extended data from the intent to a bundle.

    3. Define the msgs array and strMessage string.

    4. Get the format for the message from the bundle.

      @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 imports android.telephony.SmsMessage.

  4. 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);
    ...
    
  5. 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);
       ...
    
  6. Initialize the msgs array, and use its length in the for 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).

  7. Build the strMessage to show in a toast message:

    1. Get the originating address using the getOriginatingAddress() method.

    2. Get the message body using the getMessageBody() method.

    3. 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";
      ...
      
  8. 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:

  1. Run the app on an emulator.
  2. Click the (More) icon at the bottom of the emulator's toolbar on the right side, as shown in the figure below:

    Click the … (More) icon for more emulator controls

  3. The extended controls for the emulator appear. Click Phone in the left column to see the extended phone controls: Click Phone for the extra phone controls including Send Message
  4. 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.
  5. 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: The app shows the receiving message

Solution Code

Android Studio project: SmsMessaging

Coding challenge

Note: All coding challenges are optional.

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(), and setType():
    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.

After the user taps the button (1)

In Messenger

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 with setData() and putExtra.
  • 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, or null to use the current default Short Message Service Center (SMSC).
    • smsMessage: A string for the body of the message to send.
    • sentIntent: A PendingIntent. If not null, this is broadcast when the message is successfully sent or if the message failed.
    • deliveryIntent: A PendingIntent. If not null, 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.

Learn more

results matching ""

    No results matching ""