Yalantis: iOS, Android And Web App Development Company

What I Learned Developing SMS/MMS Messenger for Android

SMS and MMS are well-known mechanisms for transmitting short messages and media files. Even though Internet-based communication technology is now ubiquitous for all sorts of communication from sending an instant message to placing phone calls, SMS and MMS are still there, and you can absolutely use them in your mobile app development. In this article I’ll talk about how to write a custom SMS and MMS messenger based on my experience developing this app. 

SMS/MMS messenger

Sending SMS

You might be wondering why somebody would want to build an SMS/MMS messenger in the first place. We all have standard messengers in our smartphones, and we even use them from time to time, especially when we need to send an urgent message and there is no internet connection. But there is one problem with standard messengers – there is nothing fun about them because they are standard.

Developing a custom messenger with some interesting features will let you breathe a new life into a standard, boring, and old communication tool!

Enough motivation. Let’s get to the building point.

In order to write SMS implementation for your app, you need to declare all the necessary permits in the Android Manifest:

<uses-permission android:name="android.permission.SEND_SMS"/>
<uses-permission android:name="android.permission.RECEIVE_SMS"/>
SmsManager smsManager = SmsManager.getDefault();
smsManager.sendTextMessage(phoneNumber, null, message, sentPendingIntent,
deliverPendingIntent);

I bet you know what this all means, but I still want to explain:

  • phoneNumber is a phone number you want to send a message to.
  • message is some text. Mind you, the number of characters in the text is limited.
  • sentPendingIntent is an intent that notifies you once a message has been sent.
  • deliverPendingIntent is an intent that notifies you once a message has been delivered.

You can actually survive without sentPendingIntent and deliverPendingIntent because the method doesn’t require them. However, we’re used to a message status being displayed in our phones, so you'd better not disappoint the users and implement it.

Message status implementation

String SENT = "sent";
String DELIVERED = "delivered";

Intent sentIntent = new Intent(SENT);
/*Create Pending Intents*/
PendingIntent sentPI = PendingIntent.getBroadcast(
getApplicationContext(), 0, sentIntent,
PendingIntent.FLAG_UPDATE_CURRENT);

Intent deliveryIntent = new Intent(DELIVERED);

PendingIntent deliverPI = PendingIntent.getBroadcast(
getApplicationContext(), 0, deliveryIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
/* Register for SMS send action */
registerReceiver(new BroadcastReceiver() {

@Override
public void onReceive(Context context, Intent intent) {
String result = "";

switch (getResultCode()) {

case Activity.RESULT_OK:
result = "Transmission successful";
break;
case SmsManager.RESULT_ERROR_GENERIC_FAILURE:
result = "Transmission failed";
break;
case SmsManager.RESULT_ERROR_RADIO_OFF:
result = "Radio off";
break;
case SmsManager.RESULT_ERROR_NULL_PDU:
result = "No PDU defined";
break;
case SmsManager.RESULT_ERROR_NO_SERVICE:
result = "No service";
break;
}

Toast.makeText(getApplicationContext(), result,
Toast.LENGTH_LONG).show();
}

}, new IntentFilter(SENT));
/* Register for Delivery event */
registerReceiver(new BroadcastReceiver() {

@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(getApplicationContext(), "Deliverd",
Toast.LENGTH_LONG).show();
}

}, new IntentFilter(DELIVERED));

Now you don’t just send an SMS but know its current status as well.

Message history implementation

If you take upon SMS and MMS messenger development you need to know user message history. ContentProvider can handle that. But since different devices store SMS and MMS differently, you’ll have to find a single solution to fit all of them.

You should use your own database to store SMS and MMS messages, and synchronize it with a device’s storage at the first launch. This will help you further work with data in the app.

Here is a method for SMS:

private void getAllSmsMessages() {
 Cursor cursor = context.getContentResolver().query(Telephony.Sms.CONTENT_URI, null, mSelectionClause, mSelectionArgs, null);
 try {
 if (cursor.moveToFirst()) {
 do {
 int threadId = cursor.getInt(cursor.getColumnIndex(Telephony.Sms.THREAD_ID));
 String messageId = cursor.getString(cursor.getColumnIndex(Telephony.Sms._ID));
 String message = cursor.getString(cursor.getColumnIndex(Telephony.Sms.BODY));
 int type = cursor.getInt(cursor.getColumnIndex(Telephony.Sms.TYPE));
 long date = cursor.getLong(cursor.getColumnIndex(Telephony.Sms.DATE));
 App.getDataBaseManager().saveMessage(new SmsMmsMessage(threadId, messageId,
 message, type, date, null, null));
 } while (cursor.moveToNext());
 }
 } finally {
 cursor.close();
 }
 }

Now we have all messages on the device. But they are SMS. Time to get all MMS messages too. ContentProvider can again help us:

private void getAllMmsMessages(int messageType) {
 Cursor cursor = context.getContentResolver().query(Telephony.Mms.Sent.CONTENT_URI, null, null, null, null);
 if (messageType == MESSAGE_TYPE_INBOX) {
 cursor = context.getContentResolver().query(Telephony.Mms.Inbox.CONTENT_URI, null, null, null, null);
 }
 try {
 if (cursor.moveToFirst()) {
 do {
 int id = cursor.getInt(cursor.getColumnIndex(Telephony.Mms._ID));
 String mmsId = cursor.getString(cursor.getColumnIndex(Telephony.Mms.MESSAGE_ID));
 int threadId = cursor.getInt(cursor.getColumnIndex(Telephony.Mms.THREAD_ID));
 long date = cursor.getLong(cursor.getColumnIndex(Telephony.Mms.DATE));
 String part = getPartOfMMS(id);
 String message = getMmsText(id);
 String type = getMmsType(id);
 App.getDataBaseManager().saveMessage(new SmsMmsMessage(threadId, String.valueOf(date), message, messageType, date, part, type));
 } while (cursor.moveToNext());
 }
 } finally {
 cursor.close();
 }
 }

Done! Now we need to get MMS content. It’s accessible through media files in the Uri format.

private String getPartOfMMS(int mmsID) {
 String selectionPart = "mid=" + mmsID;
 Uri uri = Uri.parse("content://mms/part");
 Cursor cursor = context.getContentResolver().query(uri, null,
 selectionPart, null, null);
 try {
 if (cursor.moveToFirst()) {
 do {
 String path = cursor.getString(cursor.getColumnIndex(Telephony.Mms.Part._DATA));
 if (path != null) {
 return path;
 }
 } while (cursor.moveToNext());
 }
 return null;
 } finally {
 cursor.close();
 }
 
 }
 
 
 private String getMmsText(int id) {
 String selectionPart = "mid=" + id;
 Uri uri = Uri.parse("content://mms/part");
 Cursor cursor = context.getContentResolver().query(uri, null,
 selectionPart, null, null);
 try {
 if (cursor.moveToFirst()) {
 do {
 String type = cursor.getString(cursor.getColumnIndex(Telephony.Mms.Part.CONTENT_TYPE));
 if ("text/plain".equals(type)) {
 String path = cursor.getString(cursor.getColumnIndex(Telephony.Mms.Part.TEXT));
 if (path != null) {
 return path;
 }
 }
 } while (cursor.moveToNext());
 }
 } finally {
 cursor.close();
 }
 return null;
 }
 
 private String getMmsType(int id) {
 String selectionPart = "mid=" + id;
 Uri uri = Uri.parse("content://mms/part");
 Cursor cursor = context.getContentResolver().query(uri, null,
 selectionPart, null, null);
 try {
 if (cursor.moveToFirst()) {
 do {
 String type = cursor.getString(cursor.getColumnIndex(Telephony.Mms.Part.CONTENT_TYPE));
 if (!type.equals("application/smil")) {
 return type;
 }
 } while (cursor.moveToNext());
 }
 } finally {
 cursor.close();
 }
 return null;
 }

Now we have the content and can actually show MMS to a user with all the sounds, videos, and images.

how to build an SMS/MMS messenger

Sending MMS

In order to send MMS you need a package com.android.mms (on the devices older than API level 21). Everything seems pretty clear, except one thing – there is no such package in the SDK. I was really baffled that Android SDK doesn’t provide the possibility to send MMS. But we’ve got what we’ve got.

To solve this issue, you need to get so-called APN settings, and then add the following resolution to Android Manifest:

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

“No permission to write APN” and how to handle that

Everything seems to work on Nexus, but if you take any other device, the app will crash with a note “No permission to write APN.”

Here is when you need to hack:

private boolean getSettingsFromApnsFile(Context context, String apnName) {
 FileReader reader = null;
 boolean sawValidApn = false;
 
 try {
 reader = new FileReader("/etc/apns-conf.xml");
 BufferedReader bufferedReader = new BufferedReader(reader);
 
 appendLog("Start read from apn file");
 appendLog("Getting String:");
 String line = null;
 
 XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
 factory.setNamespaceAware(true);
 XmlPullParser xpp = factory.newPullParser();
 xpp.setInput(reader);
 
 TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
 String simOperator = telephonyManager.getSimOperator();
 if (TextUtils.isEmpty(simOperator)) {
 return false;
 }
 
 int eventType = xpp.getEventType();
 while (eventType != XmlPullParser.END_DOCUMENT) {
 if (eventType == XmlPullParser.START_TAG && xpp.getName().equals("apn")) {
 HashMap<String, String> attributes = new HashMap<String, String>();
 for (int i = 0; i < xpp.getAttributeCount(); i++) {
 attributes.put(xpp.getAttributeName(i), xpp.getAttributeValue(i));
 appendLog("put into attrs");
 }
 if (attributes.containsKey("mcc") && attributes.containsKey("mnc") && simOperator.equals(attributes.get("mcc") + attributes.get("mnc"))) {
 appendLog("second if working");
 if (!TextUtils.isEmpty(apnName) && !apnName.trim().equals(attributes.get("apn"))) {
 appendLog("now we will get apns");
 eventType = xpp.next();
 Logit.d(getClass(), xpp.getAttributeValue(1));
 Logit.d(getClass(), xpp.getNamespace());
 apn = new APN();
 apn.MMSCenterUrl = xpp.getAttributeValue(4);
 apn.MMSProxy = xpp.getAttributeValue(5);
 apn.MMSPort = xpp.getAttributeValue(6);
 apns.add(apn);
 continue;
 }
 }
 }
 eventType = xpp.next();
 }
 } catch (Exception e) {
 e.printStackTrace();
 } finally {
 if (reader != null) {
 try {
 reader.close();
 } catch (Exception e) {
 e.printStackTrace();
 }
 }
 }
 return sawValidApn;
 }

Every Android device has an XML file with all possible APN settings, so we simply get them there, and finally send a message. Don’t forget about permits!

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

SendReq sendRequest = new SendReq();
EncodedStringValue[] phoneNumber = EncodedStringValue.extract(recipient);
sendRequest.addTo(phoneNumber);
PduPart partPdu = new PduPart();
partPdu.setName(part.Name.getBytes());
partPdu.setContentType(part.MimeType.getBytes());
partPdu.setData(part.Data);
pduBody.addPart(partPdu);
sendRequest.setBody(pduBody);
PduComposer composer = new PduComposer(context, sendRequest);
byte[] bytesToSend = composer.make();
transaction.sendPdu(bytesToSend);

Bingo! The message is sent!

As you can see, there is nothing impossible. I also found a great library Android-SMSMMS on GitHub which can handle sending messages just as fine. 

Final words

The flexibility of Android platform allows users to make a custom messaging app as default for their device (we also listed this peculiarity among reasons why you should port iPhone app to Android). However, Android allows to use only one messenger as default for sending and receiving messages. This means that we have to let our app become default. Otherwise, a user won’t be able to send MMS or SMS. Here is what you can do:

Intent intent = new Intent(context, Sms.Intents.ACTION_CHANGE_DEFAULT);
intent.putExtra(Sms.Intents.EXTRA_PACKAGE_NAME, defaultSmsApp);
startActivity(intent);

It’s important to mention that since your app is now a default messenger, it should handle all notifications and store messages on the device. If you ignore that, a user will never receive any messages.

That’s it about MMS and SMS.

We have some good news, though. In Lolipop and later Android versions Google added the ability to send MMS via Android SDK. It’s very simple:

sendMultimediaMessage(Context context, Uri contentUri, String locationUrl, Bundle configOverrides, PendingIntent sentIntent)

Now you know all about MMS and SMS messenger development. I hope this article helps you build the best messaging app!

 
Insights

How Much Does It Cost to Develop WhatsApp?

Tech

Customizable Constructor for Designers of Android Wear

Design

How We Developed the Guillotine Menu Animation for Android

See what else we can do

Check out our knowledge and capabilities

Let's talk code