Saturday, November 22, 2014

Android: SMS app: receiving

In this tutorial we will learn how to handle incoming messages, how to parse them to find a pattern, and how to send an SMS.

Step: As always update to newest version of your IDE and tools (SDK manager)


Step: Create a new Android app module.




Step: Select cool icon for your SMS app



Step: Select Blank Activity


Step: Name your MainActivity




Step: Add PERMISSIONS

Step: Add receiver class declaration

Step: Add intent-filter for SMS_RECEIVED


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.chicagoandroid.android.app.sms">
    <application
            android:allowBackup="true"
            android:icon="@drawable/ic_launcher"
            android:label="@string/app_name"
            android:theme="@style/AppTheme">
        <activity
                android:name=".MainActivity"
                android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
        <receiver android:name=".SmsReceiver">
            <intent-filter>
                <action android:name="android.provider.Telephony.SMS_RECEIVED"/>
            </intent-filter>
        </receiver>


    </application>

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

</manifest>

Step: Create a new class SmsReceiver

package com.chicagoandroid.android.app.sms;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.provider.Telephony;
import android.telephony.SmsMessage;
import android.util.Log;
//deprecated import android.telephony.gsm.SmsMessage;
/**
 * Created by uki on 11/22/14.
 */
public class SmsReceiver extends BroadcastReceiver {
      private static final String TAG = SmsReceiver.class.getSimpleName();
      private static final String SMS_RECEIVED = "android.provider.Telephony.SMS_RECEIVED";
      @Override
      public void onReceive(Context context, Intent intent) {
            final String tag = TAG + ".onReceive";
            Bundle bundle = intent.getExtras();
            if (bundle == null) {
                  Log.w(tag, "BroadcastReceiver failed, no intent data to process.");
                  return;
            }
            if (intent.getAction().equals(SMS_RECEIVED)) {
                  Log.d(tag, "SMS_RECEIVED");
                  String smsOriginatingAddress, smsDisplayMessage;
                  /**
                   * You have to CHOOSE which code snippet to use NEW (KitKat+), or legacy
                   * Please comment out the for{} you don't want to use.
                   */
                  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                        Log.d(tag, "KitKat or newer + API " + Build.VERSION.SDK_INT);
                        // API level 19 (KitKat 4.4) getMessagesFromIntent
                        for (SmsMessage message : Telephony.Sms.Intents.getMessagesFromIntent(intent)) {
                              if (message == null) {
                                    Log.e(tag, "SMS message is null -- ABORT");
                                    break;
                              }
                              smsOriginatingAddress = message.getDisplayOriginatingAddress();
                              smsDisplayMessage = message.getDisplayMessageBody(); //see getMessageBody();
                              SmsParser.processReceivedSms(smsOriginatingAddress, smsDisplayMessage);
                        }
                  }
                  else { // BELOW KITKAT
                        Log.d(tag, "legacy SMS implementation (before KitKat) API " + Build.VERSION.SDK_INT);
                        // Processing SMS messages the OLD way, before KitKat, this WILL work on KitKat or newer Android
                        // PDU is a “protocol data unit”, which is the industry format for an SMS message
                        Object[] data = (Object[]) bundle.get("pdus");
                        for (Object pdu : data) {
                              SmsMessage message = SmsMessage.createFromPdu((byte[]) pdu);
                              if (message == null) {
                                    Log.e(tag, "SMS message is null -- ABORT");
                                    break;
                              }
                              smsOriginatingAddress = message.getDisplayOriginatingAddress();
                              smsDisplayMessage = message.getDisplayMessageBody(); // see getMessageBody();
                              SmsParser.processReceivedSms(smsOriginatingAddress, smsDisplayMessage);
                        }
                  }
            } // onReceive method
      }
}





StockOverflow: http://stackoverflow.com/a/27076215/3566154

Step: Simple SMS parser

package com.chicagoandroid.android.app.sms;

import android.util.Log;

/**
 * Created by uki on 11/22/14.
 */
public class SmsParser {
   private static final String TAG = SmsParser.class.getSimpleName();
   public static void processReceivedSms(String smsOriginatingAddress, String smsDisplayMessage) {
      final String tag = TAG + ".processReceivedSms";
      Log.i(tag, "SMS from  " + smsOriginatingAddress);
      Log.i(tag, "SMS body  " + smsDisplayMessage);
      String keyword = "Info?";
      if (doesSmsStartWith(smsDisplayMessage, keyword)) {
         Log.i(tag, "SMS does start with " + keyword);
      }
      else
         Log.e(TAG + ".onReceive", "SMS does not start with " + keyword);
   }
   /**
    * this is a very simple method to detect if
    * the SMS message starts with a given character sequence
    */
   private static boolean doesSmsStartWith(String smsMessage, String txt) {
      return smsMessage.trim().toLowerCase().startsWith(txt.trim().toLowerCase());
   }
}


Step: run this app and test receiving SMS in LogCat


11-22 04:55:13.783  10317-10317/com.chicagoandroid.android.app.sms D/SmsReceiver.onReceive﹕ SMS_RECEIVED
11-22 04:55:13.803  10317-10317/com.chicagoandroid.android.app.sms D/SmsReceiver.onReceive﹕ KitKat or newer
11-22 04:55:13.803  10317-10317/com.chicagoandroid.android.app.sms I/SmsParser.processReceivedSms﹕ SMS from  +1650815xxxx // privacy
11-22 04:55:13.803  10317-10317/com.chicagoandroid.android.app.sms I/SmsParser.processReceivedSms﹕ SMS body  Test 6
11-22 04:55:13.803  10317-10317/com.chicagoandroid.android.app.sms E/SmsParser.onReceive﹕ SMS does not start with Info?
11-22 04:55:13.803  10317-10317/com.chicagoandroid.android.app.sms D/SmsReceiver.onReceive﹕ legacy SMS implementation (before KitKat)
11-22 04:55:13.803  10317-10317/com.chicagoandroid.android.app.sms I/SmsParser.processReceivedSms﹕ SMS from  +16508156603
11-22 04:55:13.803  10317-10317/com.chicagoandroid.android.app.sms I/SmsParser.processReceivedSms﹕ SMS body  Test 6
11-22 04:55:13.803  10317-10317/com.chicagoandroid.android.app.sms E/SmsParser.onReceive﹕ SMS does not start with Info?

Step: create basic UI