Auro Auro - 3 months ago 15
Java Question

Matcher won't match the Pattern in Android

I am trying to develop an app that automatically reads a One Time Password from an incoming SMS. I have a class called

SMS
where I'm storing the currently read messages and a class called
SMSRule
that contains information about the possible OTP Senders.

The
SMSRule
also contains a
Pattern
variable that should be matched with the message to see if it really is the correct sender of the OTP.

Everything is going smooth except for the OTP extraction from the message. My SMS is containing the message, but when I try to match the
Pattern
against the message, my
match.find()
returns false.

Following are my files:

OTPAutoRead.java

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.telephony.SmsMessage;
import android.util.Log;
import android.widget.Toast;

import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import auro.widget.OTP.SMS.SMS;
import auro.widget.OTP.SMS.SMSRule;

/**
* Created on 26/8/16.
* @author Auro
* @since 1.0
*/

public class OTPAutoRead extends BroadcastReceiver {

private static final String LOG_TAG = OTPAutoRead.class.getCanonicalName();

private static final String mReadPermission = "android.permission.READ_SMS";
private static final String mReceivePermission = "android.permission.RECEIVE_SMS";

private List<SMSRule> smsRules;
private Context context;
private String OTP;

public OTPAutoRead() {throw new InstantiationError("Empty Constructor");}

public OTPAutoRead(@NonNull Context context, final List<SMSRule> smsRules) {

if (smsRules == null || smsRules.size() == 0) {

throw new AuroMissingRulesException("No SMS Rules Found");
}

this.smsRules = smsRules;
this.context = context;
CheckSMSReadPermission();

IntentFilter itf = new IntentFilter();
itf.addAction("android.provider.Telephony.SMS_RECEIVED");
itf.setPriority(999);
context.registerReceiver(this,itf);

}


private void CheckSMSReadPermission() {

PackageManager packageManager = context.getPackageManager();
String packageName = context.getPackageName();

int readPermission = packageManager.checkPermission(mReadPermission,packageName);
int receivePermission = packageManager.checkPermission(mReceivePermission, packageName);

boolean canRead = (readPermission == PackageManager.PERMISSION_GRANTED);

if (!canRead)
Toast.makeText(context,"Please enable SMS read permission for auto OTP read", Toast.LENGTH_SHORT).show();
}


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

try {
if (intent.getAction().equals("android.provider.Telephony.SMS_RECEIVED"))
tryReceiveMessage(intent);
} catch (Exception e) {
Log.i(LOG_TAG, "Failed to read SMS", e);
}
}


private void tryReceiveMessage(Intent intent) {

Bundle bundle = intent.getExtras();

SmsMessage[] messages = null;

if (bundle != null) {

Object[] pdus = (Object[]) bundle.get("pdus");

if (pdus != null) {
messages = new SmsMessage[pdus.length];

for (int i = 0; i < messages.length; i++) {

messages[i] = SmsMessage.createFromPdu((byte[]) pdus[i]);
String messageFrom = messages[i].getOriginatingAddress().toUpperCase();
String messageBody = messages[i].getMessageBody().toUpperCase();

Log.d(LOG_TAG, "Message is from: " + messageFrom + " and its content is: " + messageBody);

SMS sms = new SMS();
sms.setMessage(messageBody).setAddress(messageFrom);

processOTP(sms);
}
}
}
}


private void processOTP(SMS sms) {

int i;

for (i = 0; i < smsRules.size(); i ++) {

if (sms.getAddress().toUpperCase().contains(smsRules.get(i).getSender().toUpperCase())) {

Pattern pattern = smsRules.get(i).getOTPPattern();

System.out.println(pattern.pattern());
System.out.println(sms.getMessage());

Matcher matcher = pattern.matcher(sms.getMessage().toUpperCase());

if (matcher.find()) {

OTP = matcher.group(1);
Log.i(LOG_TAG,"Extracted OTP is: " + OTP);
Toast.makeText(context,"OTP RECEIVED IS: " + OTP,Toast.LENGTH_SHORT).show();
return;
} else
Log.d(LOG_TAG,"Failed to extract OTP");
}
}
}

public String getOTP() {
return OTP;
}

}


SMS.java

import android.support.annotation.NonNull;

/**
* Created on 26/8/16.
* @author Auro
* @since 1.0
*/
public class SMS {

private String mID;
private String mAddress;
private String mMessage;
private boolean isRead = false;
private String mTime;

public String getID() {

return mID;
}

public SMS setID(@NonNull final String ID) {

mID = ID;
return this;
}

public String getAddress() {

return mAddress;
}

public SMS setAddress(@NonNull final String Address) {

mAddress = Address;
return this;
}

public String getMessage() {
return mMessage;
}

public SMS setMessage(@NonNull final String Message) {

mMessage = Message;
return this;
}

public boolean isRead() {

return isRead;
}

public SMS setReadState(boolean ReadState) {

isRead = ReadState;
return this;
}

public String getTime() {

return mTime;
}

public SMS setTime(@NonNull String Time) {

mTime = Time;
return this;
}
}


SMSRule.java

import java.util.regex.Pattern;

/**
* Created on 26/8/16.
* @author Auro
* @since 1.0
*/
public class SMSRule {

private String mSender;
private String mGroupID;
private Pattern mOTPPattern;


private SMS sms;

public String getSender() {

return mSender;
}

public SMSRule setSender(final String sender) {

mSender = sender;
return this;
}

public String getGroupID() {

return mGroupID;
}

public SMSRule setGroupID(final String groupID) {

mGroupID = groupID;
return this;

}


public Pattern getOTPPattern() {

return mOTPPattern;
}

public SMSRule setOTPPattern(final Pattern otpPattern) {

mOTPPattern = otpPattern;
return this;
}

}


MainActivity.java

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.EditText;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

import auro.widget.OTP.OTPAutoRead;
import auro.juspay.widget.OTP.SMS.SMSRule;


public class MainActivity extends AppCompatActivity {


private EditText editText;

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

editText = (EditText) findViewById(R.id.editText);

SMSRule rule = new SMSRule();
Pattern pattern = Pattern.compile("One Time Password is (\\d{6})".toUpperCase());

rule.setSender("BANK-XYZ");
rule.setOTPPattern(pattern);

List<SMSRule> smsRules = new ArrayList<>();
smsRules.add(rule);

OTPAutoRead otpAutoRead = new OTPAutoRead(this,smsRules);

String OTP = otpAutoRead.getOTP();

if (OTP != null)
{
editText.setText(OTP);
}
}

}


Problem is, when I run this in
Debug
mode, I get
matchFound = false
. I also tried changing the
regex
to

Pattern pattern = Pattern.compile("One Time Password is \d{6}".toUpperCase());


It just wouldn't work

Following is a screenshot: -

enter image description here

Answer

When you convert the pattern string to uppercase in Pattern.compile("One Time Password is (\\d{6})".toUpperCase()), \d (matching a digit) turns into \D (matching a non-digit).

Use [0-9] instead of the \d so that the pattern means the same, or just remove toUpperCase() and write the exact pattern, or use a pattern case insensitive flag.