Android adding SMS Verification Like WhatsApp – Part 2

In the 1st part of this article we have learned how to create the required REST API for this app. In this part we are going to see how to build the android app that interact with the API to receive the SMS and get it verified. As this project uses volley to make http calls, I suggest you go through my Volley tutorial to know the usage of volley. Also you need to have basic knowledge on android services and broadcast receivers.

We are going to use SMS broadcast receiver to read the sms whenever device receives it and an Intent Service to make the http calls which sends the OTP to server to get it verified.

android-sms-verification-like-whatsapp

Below is the screenshot of the app we are going to build now.

android-sms-verification-like-whatsapp-viber-2

6. Creating the Android App

This app contains two activities. One with a ViewPager with two pages. One page is to enter the mobile number and other page is to enter the OTP. The second activity is to display the logged in user profile information.

1. In Android Studio, create a new project by navigating to File ⇒ New Project and fill all the required details. When it prompts to select a default activity, select Blank Activity and proceed.

2. Open build.gradle located under app folder and add volley library dependency by adding com.mcxiaoke.volley:library-aar:1.0.0.

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile "com.android.support:appcompat-v7:22.1.1"
    compile 'com.mcxiaoke.volley:library-aar:1.0.0'
}

6.1 Making the App Material

This step is optional but I recommend you go through it as it improves your knowledge on material design. Follow my Material Design tutorial to get to know how to make your android app material design ready.

3. Open strings.xml located under res ⇒ values and add below string values.

<resources>
    <string name="app_name">SMS Verification</string>

    <string name="hello_world">Hello world!</string>
    <string name="action_settings">Settings</string>
    <string name="title_activity_sms">SmsActivity</string>
    <string name="action_logout">Logout</string>
    <string name="msg_enter_mobile">Enter your mobile number to get started!</string>
    <string name="lbl_name">Name</string>
    <string name="lbl_email">Email</string>
    <string name="lbl_mobile">Mobile</string>
    <string name="lbl_next">NEXT</string>
    <string name="msg_sit_back">Sit back &amp; Relax! while we verify your mobile number</string>
    <string name="msg_manual_otp">(Enter the OTP below in case if we fail to detect the SMS automatically)</string>
    <string name="lbl_enter_otp">Enter OTP</string>
    <string name="lbl_submit">SUBMIT</string>
</resources>

4. Open colors.xml located under res ⇒ values and add below color values.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorPrimary">#3b5bb3</color>
    <color name="colorPrimaryDark">#303F9F</color>
    <color name="textColorPrimary">#FFFFFF</color>
    <color name="windowBackground">#FFFFFF</color>
    <color name="navigationBarColor">#000000</color>
    <color name="colorAccent">#ea5d88</color>

    <color name="bg_view_sms">#ffd423</color>
    <color name="bg_view_otp">#fc6d38</color>
</resources>

4. Now open styles.xml located under res ⇒ values and add below styles.

<resources>

    <style name="MyMaterialTheme" parent="MyMaterialTheme.Base">

    </style>

    <style name="MyMaterialTheme.Base" parent="Theme.AppCompat.Light.DarkActionBar">
        <item name="windowNoTitle">true</item>
        <item name="windowActionBar">false</item>
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>

</resources>

5. Under res directory, create a folder named values-v21. In this folder create another styles.xml and add below code.

<resources>

    <style name="MyMaterialTheme" parent="MyMaterialTheme.Base">
        <item name="android:windowContentTransitions">true</item>
        <item name="android:windowAllowEnterTransitionOverlap">true</item>
        <item name="android:windowAllowReturnTransitionOverlap">true</item>
        <item name="android:windowSharedElementEnterTransition">@android:transition/move</item>
        <item name="android:windowSharedElementExitTransition">@android:transition/move</item>
    </style>

</resources>

7. Finally open the AndroidManifest.xml and add the MyMaterialTheme to <application> tag.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="info.androidhive.smsverification">

    <application
        android:theme="@style/MyMaterialTheme">
        .
        .
    </application>

</manifest>

Now if you run the app, you can see the top notification bar color changed which means the material design theme is applied.

3. Now create five packages named activity, app, helper, receiver and service. These packages helps in keeping the project organized.

Below is screenshot of the final project of this tutorial.

android-sms-verification-project-structure

4. Under app package, create a class named Config.java. This class contains very important app configuration information.

> URL_REQUEST_SMS and URL_VERIFY_OTP should be correct. The ip address should match with your localhost PC.

> SMS_ORIGIN should match the value in your PHP project’s Config.php.

> OTP_DELIMITER should also match the value in your PHP project’s Config.php.

package info.androidhive.smsverification.app;

/**
 * Created by Ravi on 08/07/15.
 */
public class Config {
    // server URL configuration
    public static final String URL_REQUEST_SMS = "http://192.168.0.101/android_sms/msg91/request_sms.php";
    public static final String URL_VERIFY_OTP = "http://192.168.0.101/android_sms/msg91/verify_otp.php";

    // SMS provider identification
    // It should match with your SMS gateway origin
    // You can use  MSGIND, TESTER and ALERTS as sender ID
    // If you want custom sender Id, approve MSG91 to get one
    public static final String SMS_ORIGIN = "ANHIVE";

    // special character to prefix the otp. Make sure this character appears only once in the sms
    public static final String OTP_DELIMITER = ":";
}

5. Under app package, create a class named MyApplication.java. This class initiates the volley core objects. This class extends from Application class which should be added in your AndroidManifest.xml <application> tag.

package info.androidhive.smsverification.app;

import android.app.Application;
import android.text.TextUtils;

import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.toolbox.Volley;

/**
 * Created by Ravi on 13/05/15.
 */

public class MyApplication extends Application {

    public static final String TAG = MyApplication.class
            .getSimpleName();

    private RequestQueue mRequestQueue;

    private static MyApplication mInstance;

    @Override
    public void onCreate() {
        super.onCreate();
        mInstance = this;
    }

    public static synchronized MyApplication getInstance() {
        return mInstance;
    }

    public RequestQueue getRequestQueue() {
        if (mRequestQueue == null) {
            mRequestQueue = Volley.newRequestQueue(getApplicationContext());
        }

        return mRequestQueue;
    }

    public <T> void addToRequestQueue(Request<T> req, String tag) {
        req.setTag(TextUtils.isEmpty(tag) ? TAG : tag);
        getRequestQueue().add(req);
    }

    public <T> void addToRequestQueue(Request<T> req) {
        req.setTag(TAG);
        getRequestQueue().add(req);
    }

    public void cancelPendingRequests(Object tag) {
        if (mRequestQueue != null) {
            mRequestQueue.cancelAll(tag);
        }
    }
}

6. Open your AndroidManifest.xml and add the MyApplication to <application> tag.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="info.androidhive.smsverification">

    <application
        android:name=".app.MyApplication" ..>
        .
        .

    </application>

</manifest>

7. Under helper package, create a class named MyViewPager.java. This is a custom ViewPager class where we disable the swipe functionality of it.

package info.androidhive.smsverification.helper;

import android.content.Context;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.MotionEvent;

/**
 * Created by Ravi on 08/07/15.
 */
public class MyViewPager extends ViewPager {

    public MyViewPager(Context context) {
        super(context);
    }

    public MyViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        // Never allow swiping to switch between pages
        return false;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        // Never allow swiping to switch between pages
        return false;
    }
}

8. Create a class named PrefManager.java under helper package. This class contains methods to store user information in Shared Preferences.

package info.androidhive.smsverification.helper;

import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;

import java.util.HashMap;

/**
 * Created by Ravi on 08/07/15.
 */
public class PrefManager {
    // Shared Preferences
    SharedPreferences pref;

    // Editor for Shared preferences
    Editor editor;

    // Context
    Context _context;

    // Shared pref mode
    int PRIVATE_MODE = 0;

    // Shared preferences file name
    private static final String PREF_NAME = "AndroidHive";

    // All Shared Preferences Keys
    private static final String KEY_IS_WAITING_FOR_SMS = "IsWaitingForSms";
    private static final String KEY_MOBILE_NUMBER = "mobile_number";
    private static final String KEY_IS_LOGGED_IN = "isLoggedIn";
    private static final String KEY_NAME = "name";
    private static final String KEY_EMAIL = "email";
    private static final String KEY_MOBILE = "mobile";

    public PrefManager(Context context) {
        this._context = context;
        pref = _context.getSharedPreferences(PREF_NAME, PRIVATE_MODE);
        editor = pref.edit();
    }

    public void setIsWaitingForSms(boolean isWaiting) {
        editor.putBoolean(KEY_IS_WAITING_FOR_SMS, isWaiting);
        editor.commit();
    }

    public boolean isWaitingForSms() {
        return pref.getBoolean(KEY_IS_WAITING_FOR_SMS, false);
    }

    public void setMobileNumber(String mobileNumber) {
        editor.putString(KEY_MOBILE_NUMBER, mobileNumber);
        editor.commit();
    }

    public String getMobileNumber() {
        return pref.getString(KEY_MOBILE_NUMBER, null);
    }

    public void createLogin(String name, String email, String mobile) {
        editor.putString(KEY_NAME, name);
        editor.putString(KEY_EMAIL, email);
        editor.putString(KEY_MOBILE, mobile);
        editor.putBoolean(KEY_IS_LOGGED_IN, true);
        editor.commit();
    }

    public boolean isLoggedIn() {
        return pref.getBoolean(KEY_IS_LOGGED_IN, false);
    }

    public void clearSession() {
        editor.clear();
        editor.commit();
    }

    public HashMap<String, String> getUserDetails() {
        HashMap<String, String> profile = new HashMap<>();
        profile.put("name", pref.getString(KEY_NAME, null));
        profile.put("email", pref.getString(KEY_EMAIL, null));
        profile.put("mobile", pref.getString(KEY_MOBILE, null));
        return profile;
    }
}

6.1 Creating SMS Receiver

Now we’ll see how to add a receiver which will be triggered whenever the device receives an SMS. Also we’ll add an Intent Service to make the http calls when the app is not running.

9. Under service package, create a class named HttpService.java and extend this class from IntentService. This service is useful to make the HTTP calls when the app is in background or killed. We’ll use this Intent Service to send the OTP to our server if the app is killed before receiving the SMS.

package info.androidhive.smsverification.service;

import android.app.IntentService;
import android.content.Intent;
import android.util.Log;
import android.widget.Toast;

import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.StringRequest;

import org.json.JSONException;
import org.json.JSONObject;

import java.util.HashMap;
import java.util.Map;

import info.androidhive.smsverification.activity.MainActivity;
import info.androidhive.smsverification.app.Config;
import info.androidhive.smsverification.app.MyApplication;
import info.androidhive.smsverification.helper.PrefManager;

/**
 * Created by Ravi on 04/04/15.
 */
public class HttpService extends IntentService {

    private static String TAG = HttpService.class.getSimpleName();

    public HttpService() {
        super(HttpService.class.getSimpleName());
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        if (intent != null) {
            String otp = intent.getStringExtra("otp");
            verifyOtp(otp);
        }
    }

    /**
     * Posting the OTP to server and activating the user
     *
     * @param otp otp received in the SMS
     */
    private void verifyOtp(final String otp) {
        StringRequest strReq = new StringRequest(Request.Method.POST,
                Config.URL_VERIFY_OTP, new Response.Listener<String>() {

            @Override
            public void onResponse(String response) {
                Log.d(TAG, response.toString());

                try {

                    JSONObject responseObj = new JSONObject(response);

                    // Parsing json object response
                    // response will be a json object
                    boolean error = responseObj.getBoolean("error");
                    String message = responseObj.getString("message");

                    if (!error) {
                        // parsing the user profile information
                        JSONObject profileObj = responseObj.getJSONObject("profile");

                        String name = profileObj.getString("name");
                        String email = profileObj.getString("email");
                        String mobile = profileObj.getString("mobile");

                        PrefManager pref = new PrefManager(getApplicationContext());
                        pref.createLogin(name, email, mobile);

                        Intent intent = new Intent(HttpService.this, MainActivity.class);
                        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                        startActivity(intent);

                        Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show();

                    } else {
                        Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show();
                    }

                } catch (JSONException e) {
                    Toast.makeText(getApplicationContext(),
                            "Error: " + e.getMessage(),
                            Toast.LENGTH_LONG).show();
                }

            }
        }, new Response.ErrorListener() {

            @Override
            public void onErrorResponse(VolleyError error) {
                Log.e(TAG, "Error: " + error.getMessage());
                Toast.makeText(getApplicationContext(),
                        error.getMessage(), Toast.LENGTH_SHORT).show();
            }
        }) {

            @Override
            protected Map<String, String> getParams() {
                Map<String, String> params = new HashMap<String, String>();
                params.put("otp", otp);

                Log.e(TAG, "Posting params: " + params.toString());
                return params;
            }

        };

        // Adding request to request queue
        MyApplication.getInstance().addToRequestQueue(strReq);
    }

}

9. Now under receiver package, create a class named SmsReceiver.java and extend the class from BroadcastReceiver. This is a broadcast receiver class which will be triggered whenever user device receives the SMS.

package info.androidhive.smsverification.receiver;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.telephony.SmsMessage;
import android.util.Log;

import info.androidhive.smsverification.app.Config;
import info.androidhive.smsverification.service.HttpService;

/**
 * Created by Ravi on 09/07/15.
 */
public class SmsReceiver extends BroadcastReceiver {
    private static final String TAG = SmsReceiver.class.getSimpleName();

    @Override
    public void onReceive(Context context, Intent intent) {

        final Bundle bundle = intent.getExtras();
        try {
            if (bundle != null) {
                Object[] pdusObj = (Object[]) bundle.get("pdus");
                for (Object aPdusObj : pdusObj) {
                    SmsMessage currentMessage = SmsMessage.createFromPdu((byte[]) aPdusObj);
                    String senderAddress = currentMessage.getDisplayOriginatingAddress();
                    String message = currentMessage.getDisplayMessageBody();

                    Log.e(TAG, "Received SMS: " + message + ", Sender: " + senderAddress);

                    // if the SMS is not from our gateway, ignore the message
                    if (!senderAddress.toLowerCase().contains(Config.SMS_ORIGIN.toLowerCase())) {
                        return;
                    }

                    // verification code from sms
                    String verificationCode = getVerificationCode(message);

                    Log.e(TAG, "OTP received: " + verificationCode);

                    Intent hhtpIntent = new Intent(context, HttpService.class);
                    hhtpIntent.putExtra("otp", verificationCode);
                    context.startService(hhtpIntent);
                }
            }
        } catch (Exception e) {
            Log.e(TAG, "Exception: " + e.getMessage());
        }
    }

    /**
     * Getting the OTP from sms message body
     * ':' is the separator of OTP from the message
     *
     * @param message
     * @return
     */
    private String getVerificationCode(String message) {
        String code = null;
        int index = message.indexOf(Config.OTP_DELIMITER);

        if (index != -1) {
            int start = index + 2;
            int length = 6;
            code = message.substring(start, start + length);
            return code;
        }

        return code;
    }
}

10. To make the service and receiver working, open the AndroidManifest.xml and do the below changes.

> Add INTERNET, RECEIVE_SMS and READ_SMS permissions.

> Add MyApplication to <application> tag.

> Make SmsActivity as launcher activity. (We’ll create this activity shortly)

> Add SmsReceiver class using <receiver> tag.

> Add HttpService using <service> tag.

This is how your manifest file should look like.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="info.androidhive.smsverification">

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

    <application
        android:name=".app.MyApplication"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/MyMaterialTheme">

        <activity
            android:name=".activity.SmsActivity"
            android:label="@string/title_activity_sms">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <activity
            android:name=".activity.MainActivity"
            android:label="@string/app_name"
            android:windowSoftInputMode="adjustResize">

        </activity>

        <!-- SMS Receiver -->
        <receiver android:name=".receiver.SmsReceiver">
            <intent-filter android:priority="99999">
                <action android:name="android.provider.Telephony.SMS_RECEIVED" />
            </intent-filter>
        </receiver>


        <!-- Intent service -->
        <service
            android:name=".service.HttpService"
            android:exported="false" />

    </application>

</manifest>

6.2 Creating Mobile Login Screen

Now we have all the core logic ready. Let’s add the first activity to enter the mobile number and OTP.

12. Under res ⇒ layout, create an xml layout named activity_sms.xml and add below code. This layout contains a ViewPager with two pages. In one page we ask the user to enter his mobile number. In the second page we prompt the user to enter the OTP if the automatic sms verification fails.

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/viewContainer"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="info.androidhive.smsverification.activity.SmsActivity">

    <info.androidhive.smsverification.helper.MyViewPager
        android:id="@+id/viewPagerVertical"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent">

        <LinearLayout
            android:id="@+id/layout_sms"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:background="@color/colorPrimary"
            android:gravity="center_horizontal"
            android:orientation="vertical">

            <ImageView
                android:layout_width="60dp"
                android:layout_height="60dp"
                android:layout_gravity="center_horizontal"
                android:layout_marginBottom="25dp"
                android:layout_marginTop="100dp"
                android:src="@mipmap/ic_launcher" />

            <TextView
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:layout_marginBottom="25dp"
                android:gravity="center_horizontal"
                android:inputType="textCapWords"
                android:paddingLeft="40dp"
                android:paddingRight="40dp"
                android:text="@string/msg_enter_mobile"
                android:textColor="@android:color/white"
                android:textSize="14dp" />

            <LinearLayout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:orientation="vertical">

                <EditText
                    android:id="@+id/inputName"
                    android:layout_width="fill_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginBottom="15dp"
                    android:background="@android:color/white"
                    android:fontFamily="sans-serif-light"
                    android:hint="@string/lbl_name"
                    android:padding="5dp"
                    android:singleLine="true"
                    android:textColor="@color/colorPrimary"
                    android:textSize="18dp" />

                <EditText
                    android:id="@+id/inputEmail"
                    android:layout_width="fill_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginBottom="15dp"
                    android:background="@android:color/white"
                    android:fontFamily="sans-serif-light"
                    android:hint="@string/lbl_email"
                    android:inputType="textEmailAddress"
                    android:padding="5dp"
                    android:textColor="@color/colorPrimary"
                    android:textSize="18dp" />

                <EditText
                    android:id="@+id/inputMobile"
                    android:layout_width="240dp"
                    android:layout_height="wrap_content"
                    android:background="@android:color/white"
                    android:fontFamily="sans-serif-light"
                    android:hint="@string/lbl_mobile"
                    android:inputType="phone"
                    android:maxLength="10"
                    android:padding="5dp"
                    android:textColor="@color/colorPrimary"
                    android:textCursorDrawable="@null"
                    android:textSize="18dp" />

                <Button
                    android:id="@+id/btn_request_sms"
                    android:layout_width="fill_parent"
                    android:layout_height="wrap_content"
                    android:layout_gravity="center_horizontal"
                    android:layout_marginTop="25dp"
                    android:background="@color/colorPrimaryDark"
                    android:text="@string/lbl_next"
                    android:textColor="@android:color/white"
                    android:textSize="14dp" />
            </LinearLayout>


        </LinearLayout>

        <LinearLayout
            android:id="@+id/layout_otp"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:background="@color/colorPrimary"
            android:gravity="center_horizontal"
            android:orientation="vertical">

            <ImageView
                android:layout_width="60dp"
                android:layout_height="60dp"
                android:layout_gravity="center_horizontal"
                android:layout_marginBottom="25dp"
                android:layout_marginTop="100dp"
                android:src="@mipmap/ic_launcher" />

            <TextView
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:layout_marginBottom="15dp"
                android:gravity="center_horizontal"
                android:paddingLeft="40dp"
                android:paddingRight="40dp"
                android:text="@string/msg_sit_back"
                android:textColor="@android:color/white"
                android:textSize="16dp" />

            <TextView
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:layout_marginBottom="25dp"
                android:gravity="center_horizontal"
                android:paddingLeft="40dp"
                android:paddingRight="40dp"
                android:text="@string/msg_manual_otp"
                android:textColor="@android:color/white"
                android:textSize="12dp" />

            <EditText
                android:id="@+id/inputOtp"
                android:layout_width="120dp"
                android:layout_height="wrap_content"
                android:background="@android:color/white"
                android:fontFamily="sans-serif-light"
                android:gravity="center_horizontal"
                android:hint="@string/lbl_enter_otp"
                android:inputType="number"
                android:maxLength="6"
                android:padding="10dp"
                android:textCursorDrawable="@null"
                android:textSize="18dp" />

            <Button
                android:id="@+id/btn_verify_otp"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_horizontal"
                android:layout_marginTop="25dp"
                android:background="@color/colorPrimaryDark"
                android:paddingLeft="20dp"
                android:paddingRight="20dp"
                android:text="@string/lbl_submit"
                android:textColor="@android:color/white"
                android:textSize="14dp" />

        </LinearLayout>

    </info.androidhive.smsverification.helper.MyViewPager>


    <ProgressBar
        android:id="@+id/progressBar"
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_gravity="center"
        android:layout_marginBottom="60dp"
        android:indeterminateTint="@color/colorAccent"
        android:indeterminateTintMode="src_atop"
        android:visibility="gone" />

    <LinearLayout
        android:id="@+id/layout_edit_mobile"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_marginBottom="50dp"
        android:gravity="center"
        android:orientation="horizontal">

        <TextView
            android:id="@+id/txt_edit_mobile"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textColor="@android:color/white"
            android:textSize="16dp" />

        <ImageButton
            android:id="@+id/btn_edit_mobile"
            android:layout_width="40dp"
            android:layout_height="40dp"
            android:layout_marginLeft="10dp"
            android:background="@null"
            android:src="@drawable/ic_edit_mobile" />
    </LinearLayout>

</RelativeLayout>

13. Create an activity class named SmsActivity.java under activity package. In the below code

> requestForSMS() method make a call to server by passing name, email and mobile requesting for sms.

> verifyOtp() methods passes the otp received in the SMS to server to verify it.

package info.androidhive.smsverification.activity;

import android.content.Intent;
import android.os.Bundle;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.StringRequest;

import org.json.JSONException;
import org.json.JSONObject;

import java.util.HashMap;
import java.util.Map;

import info.androidhive.smsverification.R;
import info.androidhive.smsverification.app.Config;
import info.androidhive.smsverification.app.MyApplication;
import info.androidhive.smsverification.helper.PrefManager;
import info.androidhive.smsverification.service.HttpService;

public class SmsActivity extends AppCompatActivity implements View.OnClickListener {

    private static String TAG = SmsActivity.class.getSimpleName();

    private ViewPager viewPager;
    private ViewPagerAdapter adapter;
    private Button btnRequestSms, btnVerifyOtp;
    private EditText inputName, inputEmail, inputMobile, inputOtp;
    private ProgressBar progressBar;
    private PrefManager pref;
    private ImageButton btnEditMobile;
    private TextView txtEditMobile;
    private LinearLayout layoutEditMobile;


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

        viewPager = (ViewPager) findViewById(R.id.viewPagerVertical);
        inputName = (EditText) findViewById(R.id.inputName);
        inputEmail = (EditText) findViewById(R.id.inputEmail);
        inputMobile = (EditText) findViewById(R.id.inputMobile);
        inputOtp = (EditText) findViewById(R.id.inputOtp);
        btnRequestSms = (Button) findViewById(R.id.btn_request_sms);
        btnVerifyOtp = (Button) findViewById(R.id.btn_verify_otp);
        progressBar = (ProgressBar) findViewById(R.id.progressBar);
        btnEditMobile = (ImageButton) findViewById(R.id.btn_edit_mobile);
        txtEditMobile = (TextView) findViewById(R.id.txt_edit_mobile);
        layoutEditMobile = (LinearLayout) findViewById(R.id.layout_edit_mobile);

        // view click listeners
        btnEditMobile.setOnClickListener(this);
        btnRequestSms.setOnClickListener(this);
        btnVerifyOtp.setOnClickListener(this);

        // hiding the edit mobile number
        layoutEditMobile.setVisibility(View.GONE);

        pref = new PrefManager(this);

        // Checking for user session
        // if user is already logged in, take him to main activity
        if (pref.isLoggedIn()) {
            Intent intent = new Intent(SmsActivity.this, MainActivity.class);
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
            startActivity(intent);

            finish();
        }

        adapter = new ViewPagerAdapter();
        viewPager.setAdapter(adapter);
        viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            }

            @Override
            public void onPageSelected(int position) {
            }

            @Override
            public void onPageScrollStateChanged(int state) {

            }
        });


        /**
         * Checking if the device is waiting for sms
         * showing the user OTP screen
         */
        if (pref.isWaitingForSms()) {
            viewPager.setCurrentItem(1);
            layoutEditMobile.setVisibility(View.VISIBLE);
        }
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.btn_request_sms:
                validateForm();
                break;

            case R.id.btn_verify_otp:
                verifyOtp();
                break;

            case R.id.btn_edit_mobile:
                viewPager.setCurrentItem(0);
                layoutEditMobile.setVisibility(View.GONE);
                pref.setIsWaitingForSms(false);
                break;
        }
    }

    /**
     * Validating user details form
     */
    private void validateForm() {
        String name = inputName.getText().toString().trim();
        String email = inputEmail.getText().toString().trim();
        String mobile = inputMobile.getText().toString().trim();

        // validating empty name and email
        if (name.length() == 0 || email.length() == 0) {
            Toast.makeText(getApplicationContext(), "Please enter your details", Toast.LENGTH_SHORT).show();
            return;
        }

        // validating mobile number
        // it should be of 10 digits length
        if (isValidPhoneNumber(mobile)) {

            // request for sms
            progressBar.setVisibility(View.VISIBLE);

            // saving the mobile number in shared preferences
            pref.setMobileNumber(mobile);

            // requesting for sms
            requestForSMS(name, email, mobile);

        } else {
            Toast.makeText(getApplicationContext(), "Please enter valid mobile number", Toast.LENGTH_SHORT).show();
        }
    }

    /**
     * Method initiates the SMS request on the server
     *
     * @param name   user name
     * @param email  user email address
     * @param mobile user valid mobile number
     */
    private void requestForSMS(final String name, final String email, final String mobile) {
        StringRequest strReq = new StringRequest(Request.Method.POST,
                Config.URL_REQUEST_SMS, new Response.Listener<String>() {

            @Override
            public void onResponse(String response) {
                Log.d(TAG, response.toString());

                try {
                    JSONObject responseObj = new JSONObject(response);

                    // Parsing json object response
                    // response will be a json object
                    boolean error = responseObj.getBoolean("error");
                    String message = responseObj.getString("message");

                    // checking for error, if not error SMS is initiated
                    // device should receive it shortly
                    if (!error) {
                        // boolean flag saying device is waiting for sms
                        pref.setIsWaitingForSms(true);

                        // moving the screen to next pager item i.e otp screen
                        viewPager.setCurrentItem(1);
                        txtEditMobile.setText(pref.getMobileNumber());
                        layoutEditMobile.setVisibility(View.VISIBLE);

                        Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show();

                    } else {
                        Toast.makeText(getApplicationContext(),
                                "Error: " + message,
                                Toast.LENGTH_LONG).show();
                    }

                    // hiding the progress bar
                    progressBar.setVisibility(View.GONE);

                } catch (JSONException e) {
                    Toast.makeText(getApplicationContext(),
                            "Error: " + e.getMessage(),
                            Toast.LENGTH_LONG).show();

                    progressBar.setVisibility(View.GONE);
                }

            }
        }, new Response.ErrorListener() {

            @Override
            public void onErrorResponse(VolleyError error) {
                Log.e(TAG, "Error: " + error.getMessage());
                Toast.makeText(getApplicationContext(),
                        error.getMessage(), Toast.LENGTH_SHORT).show();
                progressBar.setVisibility(View.GONE);
            }
        }) {

            /**
             * Passing user parameters to our server
             * @return
             */
            @Override
            protected Map<String, String> getParams() {
                Map<String, String> params = new HashMap<String, String>();
                params.put("name", name);
                params.put("email", email);
                params.put("mobile", mobile);

                Log.e(TAG, "Posting params: " + params.toString());

                return params;
            }

        };

        // Adding request to request queue
        MyApplication.getInstance().addToRequestQueue(strReq);
    }

    /**
     * sending the OTP to server and activating the user
     */
    private void verifyOtp() {
        String otp = inputOtp.getText().toString().trim();

        if (!otp.isEmpty()) {
            Intent grapprIntent = new Intent(getApplicationContext(), HttpService.class);
            grapprIntent.putExtra("otp", otp);
            startService(grapprIntent);
        } else {
            Toast.makeText(getApplicationContext(), "Please enter the OTP", Toast.LENGTH_SHORT).show();
        }
    }

    /**
     * Regex to validate the mobile number
     * mobile number should be of 10 digits length
     *
     * @param mobile
     * @return
     */
    private static boolean isValidPhoneNumber(String mobile) {
        String regEx = "^[0-9]{10}$";
        return mobile.matches(regEx);
    }


    class ViewPagerAdapter extends PagerAdapter {

        @Override
        public int getCount() {
            return 2;
        }

        @Override
        public boolean isViewFromObject(View view, Object object) {
            return view == ((View) object);
        }

        public Object instantiateItem(View collection, int position) {

            int resId = 0;
            switch (position) {
                case 0:
                    resId = R.id.layout_sms;
                    break;
                case 1:
                    resId = R.id.layout_otp;
                    break;
            }
            return findViewById(resId);
        }
    }

}

Now run the app and test it once. Make sure that you have correct ip address of your localhost in Config.java

android-sms-verification-like-whatsapp-viber
android-sms-verification-like-whatsapp-viber-otp

6.2 Displaying logged in User Profile

Displaying the logged in user information is pretty straight forward. Previously the user information is store in Shared Preferences in HttpService.class. In this activity we have to read the information from shared preferences and display it on the screen.

14. Create an xml layout named toolbar.xml under res ⇒ layout.

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:local="http://schemas.android.com/apk/res-auto"
    android:id="@+id/toolbar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:minHeight="?attr/actionBarSize"
    android:background="?attr/colorPrimary"
    local:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
    local:popupTheme="@style/ThemeOverlay.AppCompat.Light" />

15. Open the layout file your main activity (activity_main.xml) and do the below changes. This layout contains few TextViews to display the logged in user information.

<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"
    tools:context=".MainActivity">

    <LinearLayout
        android:id="@+id/layout_toolbar"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:orientation="vertical">

        <include
            android:id="@+id/toolbar"
            layout="@layout/toolbar" />
    </LinearLayout>

    <LinearLayout
        android:id="@+id/layout_mobile"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:orientation="vertical">

        <TextView
            android:id="@+id/name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:padding="10dp"
            android:textSize="18dp" />

        <TextView
            android:id="@+id/email"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:padding="10dp"
            android:textSize="18dp" />

        <TextView
            android:id="@+id/mobile"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:padding="10dp"
            android:textSize="18dp" />

    </LinearLayout>
</RelativeLayout>

17. Open menu_main.xml located under res ⇒ menu and add an action item to provide the logout option.

<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context=".MainActivity">
    <item
        android:id="@+id/action_logout"
        android:orderInCategory="100"
        android:title="@string/action_logout"
        app:showAsAction="always" />
</menu>

16. Open MainActivity.java and do the below changes. In this activity we just read the user information stored in shared preferences and display it on the screen.

package info.androidhive.smsverification.activity;

import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;

import java.util.HashMap;

import info.androidhive.smsverification.R;
import info.androidhive.smsverification.helper.PrefManager;


public class MainActivity extends AppCompatActivity {

    private Toolbar toolbar;
    private PrefManager pref;
    private TextView name, email, mobile;

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

        toolbar = (Toolbar) findViewById(R.id.toolbar);
        name = (TextView) findViewById(R.id.name);
        email = (TextView) findViewById(R.id.email);
        mobile = (TextView) findViewById(R.id.mobile);

        // enabling toolbar
        setSupportActionBar(toolbar);
        getSupportActionBar().setDisplayShowHomeEnabled(true);

        pref = new PrefManager(getApplicationContext());


        // Checking if user session
        // if not logged in, take user to sms screen
        if (!pref.isLoggedIn()) {
            logout();
        }

        // Displaying user information from shared preferences
        HashMap<String, String> profile = pref.getUserDetails();
        name.setText("Name: " + profile.get("name"));
        email.setText("Email: " + profile.get("email"));
        mobile.setText("Mobile: " + profile.get("mobile"));
    }

    /**
     * Logging out user
     * will clear all user shared preferences and navigate to
     * sms activation screen
     */
    private void logout() {
        pref.clearSession();

        Intent intent = new Intent(MainActivity.this, SmsActivity.class);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);

        startActivity(intent);

        finish();
    }


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_logout) {
            logout();
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}

android-sms-verification-like-whatsapp-viber-1

7. Testing the App

To test the app, carefully follow the below steps.

> Make sure that your device and the PC running the php project are under the same wifi network.

> Keep correct username, password and database name in Config.php

> Give the correct MSG91_AUTH_KEY Key in Config.php

> Make sure that MSG91_SENDER_ID in Config.java and SMS_ORIGIN in Config.java are equal.

> Keep the correct ip address of your localhost in Config.java. On windows run ipconfig in command prompt to get the ip address.

I know running this app is little tricky for a beginner. But the following the above steps correctly will help you a lot. If you are still facing the problem in running this app, please do comment below.

Ravi is hardcore Android programmer and Android programming has been his passion since he compiled his first hello-world program. Solving real problems of Android developers through tutorials has always been interesting part for him.