In continuation with my previous blog Adding SMS Verification Like WhatsApp to Android Application Part-I, here I continue with the implementation details for automatic SMS OTP verification. This part shows the BroadcastReceiver, IntentService, layouts, manifest changes and the registration screen.
TL;DR
- The article contains the core runnable code for: HttpService (network call to verify OTP), SmsReceiver (reads incoming SMS and extracts OTP), SmsActivity (UI to request SMS and enter OTP manually), and MainActivity (post-login screen).
- Important: For modern Android you should read the "Android versions & security" section below before copying the code: runtime permissions, SMS Retriever/User Consent APIs and background execution limits are not covered by the original code and must be added for Android 6+ and Android 8+.
1. HttpService.java (service directory)
This IntentService receives an OTP string via intent extras and posts it to the server to verify and activate the user. The code uses Volley for network requests and PrefManager to store the user profile on success.
Code (copy into service/HttpService.java):
package marutsoft.com.smsverify.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 marutsoft.com.smsverify.activity.MainActivity;
import marutsoft.com.smsverify.app.Config;
import marutsoft.com.smsverify.app.MyApplication;
import marutsoft.com.smsverify.helper.PrefManager;
/**
* Created by Karjol G T on 01-02-2016.
*/
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);
Toast.makeText(getApplicationContext(), responseObj.toString(), Toast.LENGTH_LONG).show();
// 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);
}
}
Note: avoid logging or Toasting the OTP in production (this code uses them for debugging). Also consider replacing IntentService with JobIntentService / WorkManager for Android O+ background restrictions (see "Android versions & security" below).
2. SmsReceiver.java (receiver directory)
This BroadcastReceiver listens for incoming SMS messages, filters by origin, extracts the OTP using a delimiter, and starts HttpService with the OTP.
Code (copy into receiver/SmsReceiver.java):
package marutsoft.com.smsverify.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 marutsoft.com.smsverify.app.Config;
import marutsoft.com.smsverify.service.HttpService;
/**
* Created by Karjol G T on 01-02-2016.
*/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;
}
}
Important robustness and API notes (you should apply these to the above code):
- Use
Telephony.Sms.Intents.getMessagesFromIntent(intent)to get SmsMessage[] which handles multipart PDUs and format differences. - For API >= 23 use
SmsMessage.createFromPdu(byte[] pdu, String format)if you must call createFromPdu. - Validate bounds before substringing the OTP: ensure
start + length <= message.length(). - Avoid logging or Toasting the OTP (sensitive information).
3. AndroidManifest.xml changes
Add required permissions and register the receiver and service. The original manifest snippets:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.READ_SMS" />
And inside <application> (excerpt):
<application android:name=".app.MyApplication" android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme">
<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" />
<!-- 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>
Notes & cautions:
- Runtime permissions:
RECEIVE_SMSandREAD_SMSrequire requesting the permission at runtime on Android 6+. Add code in your activity to request and handle user response. - Very high receiver priority is discouraged; it can block other SMS handlers. Consider lowering priority or using SMS Retriever / User Consent to avoid requiring
READ_SMS. - On Android 8+ you cannot reliably start background services from a receiver; consider JobIntentService or WorkManager.
4. activity_sms.xml (res/layout)
Create the registration / verification screens. The original layout is long; include it as given:
<!-- activity_sms.xml content -->
<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="marutsoft.com.smsverify.activity.SmsActivity">
<marutsoft.com.smsverify.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>
</marutsoft.com.smsverify.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>
Add content descriptions (android:contentDescription) for images and ensure labels are present for accessibility.
5. SmsActivity.java (activity directory)
This activity shows the registration screen, requests an OTP from the server, and allows manual OTP entry. It also starts HttpService with the entered OTP.
Code (copy into activity/SmsActivity.java):
package marutsoft.com.smsverify.activity;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
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 marutsoft.com.smsverify.R;
import marutsoft.com.smsverify.app.Config;
import marutsoft.com.smsverify.app.MyApplication;
import marutsoft.com.smsverify.helper.PrefManager;
import marutsoft.com.smsverify.service.HttpService;
/**
* Created by Karjol G T on 02-02-2016.
*/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.setOnP
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 * */
@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);
}
}
}
Notes: Add runtime permission request flow before attempting to read SMS or register the receiver.
6. Toolbar layout (res/layout/toolbar.xml)
Toolbar XML used by MainActivity:
<?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" />
7. activity_main.xml (layout for the logged in screen)
Original code for the main activity layout:
<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>
8. menu_main.xml (menu)
Menu contains a logout action:
<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>
9. MainActivity.java (showing stored profile and logout)
Code (copy into activity/MainActivity.java):
package marutsoft.com.smsverify.activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;
import java.util.HashMap;
import marutsoft.com.smsverify.R;
import marutsoft.com.smsverify.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);
}
}
10. Run and test
With proper configuration in Config.java (URLs for URL_REQUEST_SMS and URL_VERIFY_OTP) and a server endpoint that sends OTP via SMS to the supplied mobile number, you can run the application and test sign‑up and OTP verification.
That's it. Happy Coding
Android versions & security (must read before copying the code)
- Android 6 (Marshmallow) and above: READSMS and RECEIVESMS are runtime permissions. You must request them in the Activity and handle the user's decision. If the user denies, fall back to manual OTP entry.
- Android 8 (Oreo) and above: apps cannot freely start background services from a BroadcastReceiver. Replace IntentService with JobIntentService or schedule work with WorkManager for network calls triggered from a receiver.
- SMS Retriever API / SMS User Consent API: to avoid asking for SMS reading permissions and to comply with Play policy, prefer the SMS Retriever API or the User Consent API. These allow auto‑retrieval of OTP without READ_SMS.
- Privacy & security: never log or display the OTP in Toasts or logs in production. Always use HTTPS for server requests and verify OTP server‑side. Add rate limiting and expiry on server side.
If you want, I can produce a small patch for the SmsReceiver (using Telephony.Sms.Intents.getMessagesFromIntent) and a JobIntentService replacement for HttpService as an example.
References and further reading (recommended):
- Part I of this tutorial: http://karjol-gt.blogspot.in/2016/02/adding-sms-verification-like-whatsapp.html
- Android SMS Retriever API and User Consent API documentation (official Android docs)
- Android runtime permissions (official Android docs)
No comments:
Post a Comment