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.