time

Sending and receiving SMS Message in Android

Sending and receiving SMS Message in Android

In Android, we can send SMS from an application in two ways. If we use SmsManager api, it will directly send SMS from our application. In case if we use Intent with proper action (ACTION_VIEW), it will invoke built-in SMS app to send SMS from our application.

Any SMS sending requires add android.permission.SEND_SMS permission to the manifest:

<uses-permission android:name="android.permission.SEND_SMS"/>

Check if device can send SMS

We can use TelephonyManager to check if device can send SMS

public boolean isSimExists() {
    TelephonyManager telephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
    int SIM_STATE = telephonyManager.getSimState();

    if (SIM_STATE == TelephonyManager.SIM_STATE_READY)
        return true;
    else {
        // we can inform user about sim state
        switch (SIM_STATE) {
            case TelephonyManager.SIM_STATE_ABSENT: 
            case TelephonyManager.SIM_STATE_NETWORK_LOCKED: 
            case TelephonyManager.SIM_STATE_PIN_REQUIRED: 
            case TelephonyManager.SIM_STATE_PUK_REQUIRED: 
            case TelephonyManager.SIM_STATE_UNKNOWN: 
            break;
        }
        return false;
    }
}

Sending SMS by invoking Built-in SMS application

To send SMS using Intent object, we need to write the code like as shown below.

String phone = "+38091234567";
String message = "Hello";

Intent sendIntent = new Intent(Intent.ACTION_VIEW);
sendIntent.putExtra("address", phone);
sendIntent.putExtra("sms_body", message); 
sendIntent.setType("vnd.android-dir/mms-sms");
startActivity(sendIntent);

Android has built-in support to add phone number and text message to send an SMS as follows

String phones = "+38091234567;+38091234568";

smsIntent.putExtra("address", phones);
smsIntent.putExtra("sms_body", message);

Here address and sms_body are case sensitive and should be specified in small characters only. You can specify more than one number in single string but separated by semi-colon (;).

Sending SMS using SmsManager API

To send SMS programmatically in Android, we need to get an instance of the SmsManager class using the static getDefault() method.

String phone = "+38091234567";
String message = "Hello";

try {
    SmsManager smgr = SmsManager.getDefault();
    smgr.sendTextMessage(phone, null, message, null, null);
    Toast.makeText(MainActivity.this, "SMS Sent Successfully", Toast.LENGTH_SHORT).show();
}
catch (Exception e){
    Toast.makeText(MainActivity.this, "SMS Failed to Send, Please try again", Toast.LENGTH_SHORT).show();
}

The first argument passed to sendTextMessage() is the destination address to which the message has to be sent and the second argument is the SMSC address (it’s also like a phone number generally) to which if you pass null, the default service center of the device’s carrier will be used. Third argument is the text message to be sent in the SMS. The fourth and fifth arguments if not null must be pending intents performing broadcasts when the message is successfully sent (or failed) and delivered to the recipient.

Let’s see an example where we set broadcast receivers for when the message is sent successfully and is also delivered (or not).

String phone = "+38091234567";
String message = "Hello";

String SMS_SENT = "SMS_SENT";
String SMS_DELIVERED = "SMS_DELIVERED";

// for when the SMS has been sent

registerReceiver(new BroadcastReceiver() {
    @Override
    public void onRecive(Context context, Intent intent) {
        String state;
        switch (getResultCode()) {
            case Activity.RESULT_OK:
                state = "SMS sent successfully";
                break;
            case SmsManager.RESULT_ERROR_GENERIC_FAILURE:
                state = "Generic failure cause";
                break;
            case SmsManager.RESULT_ERROR_NO_SERVICE:
                state = "Service is currently unavailable";
                break;
            case SmsManager.RESULT_ERROR_NULL_PDU:
                state = "No PDU provided";
                break;
            case SmsManager.RESULT_ERROR_RADIO_OFF:
                state = "Radio was explicitly turned off";
                break;
        }
        Toast.makeText(context, state, Toast.LENGTH_SHORT).show();
    }
}, new IntentFilter(SMS_SENT));

// for when the SMS has been delivered

registerReceiver(new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        String state;
        switch (getResultCode()) {
            case Activity.RESULT_OK:
                state = "SMS delivered";
                break;
            case Activity.RESULT_CANCELED:
                state = "SMS not delivered";
                break;
        }
        Toast.makeText(context, state, Toast.LENGTH_SHORT).show();
    }
}, new IntentFilter(SMS_DELIVERED));

// unregisterReceiver()

// get the default instance of SmsManager
SmsManager smsManager = SmsManager.getDefault();

PendingIntent sentPendingIntent = PendingIntent.getBroadcast(this, 0, new Intent(SMS_SENT), 0);
PendingIntent deliveredPendingIntent = PendingIntent.getBroadcast(this, 0, new Intent(SMS_DELIVERED), 0);

// send a text based SMS
smsManager.sendTextMessage(phone, null, message, sentPendingIntent, deliveredPendingIntent);

Following is the MainActivity with full code. For permission check I use EasyPermissions.

public class MainActivity extends AppCompatActivity implements EasyPermissions.PermissionCallbacks {
    private static final int RC_PERM_SMS = 125;
    private static final int RC_SETTINGS = 126;

    private String[] wantedPerm = {Manifest.permission.SEND_SMS, Manifest.permission.RECEIVE_SMS};

    private Activity activity = MainActivity.this;
    private String TAG = MainActivity.class.getSimpleName();
    private TextView tv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        TextView tv = findViewById(R.id.tv);

        if (isSimExists()) {
            tv.setText("You can send SMS!");
            if (EasyPermissions.hasPermissions(activity, wantedPerm)) {
                Log.d(TAG, "Already have permission, do the thing");
                sendSms();
            } else {
                EasyPermissions.requestPermissions(activity, "This app needs access to send SMS.", RC_PERM_SMS, wantedPerm);
            }
        } else {
            tv.setText("You can't send SMS!");
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, activity);
    }

    @Override
    public void onPermissionsGranted(int requestCode, List<String> perms) {
        Log.d(TAG, "onPermissionsGranted:" + requestCode + ":" + perms.size());
        sendSms();
    }

    @Override
    public void onPermissionsDenied(int requestCode, List<String> perms) {
        Log.d(TAG, "onPermissionsDenied:" + requestCode + ":" + perms.size());
        if (EasyPermissions.somePermissionPermanentlyDenied(activity, perms)) {
            new AppSettingsDialog.Builder(activity)
                    .setTitle("Permissions Required")
                    .setPositiveButton("Settings")
                    .setNegativeButton("Cancel")
                    .setRequestCode(RC_SETTINGS)
                    .build()
                    .show();
        }
    }

    @AfterPermissionGranted(RC_PERM_SMS)
    private void methodRequireLocationPermission() {
        if (EasyPermissions.hasPermissions(this, wantedPerm)) {
            Log.d(TAG, "Already have permission, do the thing");
        } else {
            Log.d(TAG, "Do not have permission, request them now");
            EasyPermissions.requestPermissions(activity, "This app needs access to send SMS.", RC_PERM_SMS, wantedPerm);
        }
    }

    private void sendSms() {
        String phone = "+380971234567";
        String message = "Hello";
        SmsManager smsManager = SmsManager.getDefault();
        smsManager.sendTextMessage(phone, null, message, null, null);
        Log.d(TAG, "sendSms: SENT");
    }

    public boolean isSimExists() {
        TelephonyManager telephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
        int SIM_STATE = telephonyManager.getSimState();

        if (SIM_STATE == TelephonyManager.SIM_STATE_READY)
            return true;
        else {
            // we can inform user about sim state
            switch (SIM_STATE) {
                case TelephonyManager.SIM_STATE_ABSENT:
                case TelephonyManager.SIM_STATE_NETWORK_LOCKED:
                case TelephonyManager.SIM_STATE_PIN_REQUIRED:
                case TelephonyManager.SIM_STATE_PUK_REQUIRED:
                case TelephonyManager.SIM_STATE_UNKNOWN:
                    break;
            }
            return false;
        }
    }

}

Divide and Send Multipart Text Messages

Generally an SMS is restricted to 160 (7 bit) characters or 140 (8 bit) characters. So if your message is longer than that, then you’ll have to divide it into multiple parts using divideMessage() and then send with the sendMultipartTextMessage() method. It’s fairly simple, let’s see an example.

ArrayList<String> parts = smsManager.divideMessage(smsMessage);
smsManager.sendMultipartTextMessage(phone, null, parts, null, null);

Receive SMS Text Messages

Now that we know how to send SMS messages, it’s time to see how to receive them using broadcast receivers as soon as the recipient device receives it.

Firstly, you’ll need the RECEIVE_SMS permission, so put this in your manifest:

<uses-permission android:name="android.permission.RECEIVE_SMS" />

Next we’ll create a broadcast receiver class called SmsReceiver.java to listen for any incoming SMS. So create the file and put this code into it:

public class SmsReceiver extends BroadcastReceiver {
    private String TAG = SmsReceiver.class.getSimpleName();

    public SmsReceiver() {}

    @Override
    public void onReceive(Context context, Intent intent) {
        // Get the data (SMS data) bound to intent
        Bundle bundle = intent.getExtras();

        if (bundle != null) {
            // Retrieve the SMS Messages received

            Object[] sms = (Object[]) bundle.get("pdus");

            // For every SMS message received
            for (int i=0; i < sms.length; i++) {
                // Convert Object array
                SmsMessage smsMessage = SmsMessage.createFromPdu((byte[]) sms[i]);

                String phone = smsMessage.getOriginatingAddress();
                String message = smsMessage.getMessageBody().toString();

                Toast.makeText(context, phone + ": " + message, Toast.LENGTH_SHORT).show();
            }
        }
    }
}

It’ll loop through all the PDUs (one for each message in a multipart text message) and form a big string with the sender phone number and the messages. Finally it logs the entire string formed. This reception technique will work for both single and multipart messages. You’ll also need create an entry for the receiver in the manifest file with an SMS_RECEIVED_ACTION intent filter.

<receiver
    android:name=".SmsReceiver"
    android:enabled="true"
    android:exported="true">
    <intent-filter android:priority="10" >
        <action android:name="android.provider.Telephony.SMS_RECEIVED" />
    </intent-filter>
</receiver>

We registered our broadcast receiver in the manifest file (statically). Alternatively, we can also dynamically register it in the code itself.