Monday, 1 July 2013

Receive SMS using Android BroadcastReceiver inside an Activity


Receive SMS using Android BroadcastReceiver inside an activity



Today I am going to share you a very usefull code.
   In one of my project I have a situation where I need to verify the user Mobile number with the SMS verification code coming from server to mobile number.

   So I need to read SMS for verification  mobile number, and  there was another point is We just wait only for 30 sec to receive SMS if SMS comes and then check validation code else say Authentication fails. or time expires .





A SMS in Android is intercepted by using BroadcastReceiver which then can be used to launch an activity, but in our case we want the BroadcastReceiver to send theSMS to our Activity when it is in the foreground rather than launching a new activity everytime.
We start by creating a BroadcastReceiver  – SmsReceiver.java




import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.telephony.SmsMessage;
import android.widget.Toast;

public class SmsReceiver extends BroadcastReceiver {
       @Override
       public void onReceive(Context context, Intent intent) {

              Bundle extras = intent.getExtras();
              if (extras == null)
                     return;

              // To display a Toast whenever there is an SMS.
              // Toast.makeText(context,"Recieved",Toast.LENGTH_LONG).show();

              Object[] pdus = (Object[]) extras.get("pdus");
              for (int i = 0; i < pdus.length; i++) {
                     SmsMessage SMessage = SmsMessage.createFromPdu((byte[]) pdus[i]);
                     String sender = SMessage.getOriginatingAddress();
                     String body = SMessage.getMessageBody().toString();

                     // A custom Intent that will used as another Broadcast
                     Intent in = new Intent("SmsMessage.intent.MAIN").putExtra(
                                  "get_msg", sender + ":" + body);

                     // You can place your check conditions here(on the SMS or the
                     // sender)
                     // and then send another broadcast
                     context.sendBroadcast(in);

                     // This is used to abort the broadcast and can be used to silently
                     // process incoming message and prevent it from further being
                     // broadcasted. Avoid this, as this is not the way to program an
                     // app.
                     // this.abortBroadcast();
              }
       }
}


We create our own BroadcastReceiver that will intercept the SMS and retrieve the body & the sender. Then it broadcasts another Intent that will is handled by our app in the foreground.
Lets register the BroadcastReceiver(SmsReceiver) in the android-manifest.xmlfile (inside

<receiver android:name="com.itdeveloper.khurram.SmsReceiver" >
            <intent-filter>
                <action android:name="android.provider.Telephony.SMS_RECEIVED" />
            </intent-filter>
        </receiver>


You can set the priority of the receiver to 999 , if you wish to intercept the message and not let it pass through. (Not a good thing to do)
Now lets add the permission -
<uses-permission android:name="android.permission.RECEIVE_SMS" />

Your manifest xml look like
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.itdeveloper.khurram"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="17" />

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

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.itdeveloper.khurram.SignInWaitingActivity"
            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="com.itdeveloper.khurram.SmsReceiver" >
            <intent-filter>
                <action android:name="android.provider.Telephony.SMS_RECEIVED" />
            </intent-filter>
        </receiver>
    </application>

</manifest>





Now lets create our Activity that will display the SMS when received –
SignInWaitingActivity

package com.itdeveloper.khurram;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.preference.PreferenceManager;
import android.util.Log;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

public class SignInWaitingActivity extends Activity {
       private BroadcastReceiver mIntentReceiver;
       TextView timerTv;
       TextView mobNoVeryfyTv;

       private ProgressBar progressBar;
       static Boolean timeOut = true;

       @Override
       protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.activity_sign_in_waiting);
             
              mobNoVeryfyTv = (TextView) findViewById(R.id.SW_MobNoVeryfyDesctxt);

              timerTv = (TextView) findViewById(R.id.SW_TimeRemainigTv);
              progressBar = (ProgressBar) findViewById(R.id.SW_progressBar);
              // show 30 second time count down
              new CountDownTimer(30000, 1000) {

                     public void onTick(long millisUntilFinished) {
                           timerTv.setText("Seconds Remaining : " + millisUntilFinished
                                         / 1000);
                     }

                     public void onFinish() {
                           timerTv.setText("Time Over");
                     //     SignInWaitingActivity.this.finish();
                     }
              }.start();

       }

       @Override
       protected void onResume() {
              super.onResume();

              IntentFilter intentFilter = new IntentFilter("SmsMessage.intent.MAIN");
              mIntentReceiver = new BroadcastReceiver() {
                     @Override
                     public void onReceive(Context context, Intent intent) {
                           String msg = intent.getStringExtra("get_msg");

                           // Process the sms format and extract body &amp; phoneNumber
                           msg = msg.replace("\n", "");
                           String body = msg.substring(msg.lastIndexOf(":") + 1,
                                         msg.length());
                           String pNumber = msg.substring(0, msg.lastIndexOf(":"));

                           // Add it to the list or do whatever you wish to
                           Log.e("onResume", "" + msg + body + pNumber);

                           Toast.makeText(getApplicationContext(), body, 1).show();

                           // check body content with your validation code mine is
                           // success123

                           if (body.equalsIgnoreCase("success123")) {

                                  Toast.makeText(getApplicationContext(),
                                                "Authentication Success.", 1).show();
                                  mobNoVeryfyTv.setText("Authentication Success.");

                           } else {

                                  // if message is contains some invalide code
                                  mobNoVeryfyTv.setText("Authentication Fails.");

                           //     SignInWaitingActivity.this.finish();

                           }

                     }
              };
              this.registerReceiver(mIntentReceiver, intentFilter);
       }

       @Override
       protected void onPause() {

              super.onPause();
              this.unregisterReceiver(this.mIntentReceiver);
       }

}


and your xml as
activity_sign_in_waiting.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/imagesbg" >

    <TextView
        android:id="@+id/SW_MobNoVeryfyDesctxt"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="60dp"
        android:text="Waiting for verification sms"
        android:textColor="@android:color/white"
        android:textSize="14sp" />

    <TextView
        android:id="@+id/SW_TimeRemainigTv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/SW_progressBar"
        android:layout_centerHorizontal="true"
        android:text="00:00"
        android:textColor="@android:color/white" />

    <ProgressBar
        android:id="@+id/SW_progressBar"
        style="?android:attr/progressBarStyleLarge"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/SW_MobNoVeryfyDesctxt"
        android:layout_centerHorizontal="true" />

</RelativeLayout>


We declare a BroadcastReciever in the class itself rather than the manifest in theonResume() method so that when the app returns in the foreground , it should receive the SMS.
Note – As per Honeycomb 3.0+ it is necessary to launch the activity once, in order to register the BroadcastReceiver defined in the manifest.
The onRecieve() method of the BroadcastReceiver uses the same Intent using the corresponding intent filter “SmsMessage.intent.MAIN”  defined inSmsReceiver.
You can use the incoming message body/number and filter out the required messages for controlling or updating the UI of the app.




Note that SmsReceiver (BroadcastReceiver) will keep on intercepting the SMS in the background but you can only see these messages when the app is in the foreground. You can save these messages in the database for further user (or use inbox to retrieve them later).
I used this technique to create a Spring MVC & Android (SMS) based quiz , where questions appear with options A,B,C,D in the browser and you can answer these questions by messaging it to the app. The app is synced with Spring MVC web app using HTTP POST & GET request.

you can download complete example from here 

Download 


Happy Codddddding :)