com.money.manager.ex.notifications.SmsReceiverTransactions.java Source code

Java tutorial

Introduction

Here is the source code for com.money.manager.ex.notifications.SmsReceiverTransactions.java

Source

/*
 * Copyright (C) 2012-2018 The Android Money Manager Ex Project Team
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 3
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/************** Change Logs *****************
 * Created by velmuruganc on 11/24/2017.
 *
 * Modification:
 * 2017/12/12 - velmuruganc :
 */

package com.money.manager.ex.notifications;

import java.util.Date;
import java.util.regex.*;

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.NotificationCompat;
import android.telephony.SmsMessage;
import android.widget.Toast;

import com.money.manager.ex.Constants;
import com.money.manager.ex.R;
import com.money.manager.ex.core.Core;
import com.money.manager.ex.core.TransactionTypes;
import com.money.manager.ex.currency.CurrencyService;
import com.money.manager.ex.database.ITransactionEntity;
import com.money.manager.ex.database.MmxOpenHelper;
import com.money.manager.ex.datalayer.AccountRepository;
import com.money.manager.ex.datalayer.AccountTransactionRepository;
import com.money.manager.ex.domainmodel.AccountTransaction;
import com.money.manager.ex.settings.BehaviourSettings;
import com.money.manager.ex.settings.GeneralSettings;
import com.money.manager.ex.settings.AppSettings;
import com.money.manager.ex.settings.PreferenceConstants;
import com.money.manager.ex.transactions.CheckingTransactionEditActivity;
import com.money.manager.ex.transactions.EditTransactionActivityConstants;
import com.money.manager.ex.transactions.EditTransactionCommonFunctions;
import com.money.manager.ex.utils.MmxDate;
import com.squareup.sqlbrite.BriteDatabase;

import javax.inject.Inject;

import info.javaperformance.money.MoneyFactory;
import timber.log.Timber;

import static android.support.v4.content.ContextCompat.startActivity;
import static java.lang.Integer.*;

public class SmsReceiverTransactions extends BroadcastReceiver {

    private Context mContext;

    @Inject
    BriteDatabase database;

    private EditTransactionCommonFunctions mCommon;

    /// Db setup
    public static MmxOpenHelper MmxHelper;
    public static SQLiteDatabase db;

    static String[] fromAccountDetails;
    static String[] toAccountDetails;

    @Override
    public void onReceive(Context context, Intent intent) {
        mContext = context.getApplicationContext();

        final BehaviourSettings behav_settings = new BehaviourSettings(mContext);
        final GeneralSettings gen_settings = new GeneralSettings(mContext);
        final AppSettings app_settings = new AppSettings(mContext);
        final PreferenceConstants prf_const = new PreferenceConstants();

        //App Settings
        int baseCurencyID, fromCurrencyID, toCurrencyID;
        int baseAccountID, fromAccountID, toAccountID;

        String baseCurrencySymbl, fromAccCurrencySymbl, toAccCurrencySymbl;
        String baseAccountName, fromAccountName, toAccountName;

        Boolean autoTransactionStatus = false;
        Boolean skipSaveTrans = false;

        try {
            //------- if settings enabled the parse the sms and create trans ---------------
            if (behav_settings.getBankSmsTrans() == true) {

                //---get the SMS message passed in---
                Bundle bundle = intent.getExtras();
                SmsMessage[] msgs = null;
                String msgBody = "";
                String msgSender = "";

                if (bundle != null) { //---retrieve the SMS message received---

                    Object[] pdus = (Object[]) bundle.get("pdus");
                    msgs = new SmsMessage[pdus.length];

                    for (int i = 0; i < msgs.length; i++) {
                        msgs[i] = SmsMessage.createFromPdu((byte[]) pdus[i]);
                        msgSender = msgs[i].getOriginatingAddress();
                        msgBody += msgs[i].getMessageBody().toString();
                    }

                    //msgSender = "AT-SIBSMS";

                    if (isTransactionSms(msgSender)) {
                        // Transaction Sms sender will have format like this AT-SIBSMS,
                        // Promotional sms will have sender like AT-012345
                        // Not sure how this format will be in out side of India. May I need to update if i get sample

                        ITransactionEntity model = AccountTransaction.create();
                        mCommon = new EditTransactionCommonFunctions(null, model, database);

                        // find out the trans type using reg ex
                        String[] key_credit_search = { "(credited)", "(received)", "(added)", "(reloaded)",
                                "(deposited)", "(refunded)", "(debited)(.*?)(towards)(\\s)",
                                "(\\s)(received)(.*?)(in(\\s)your)(\\s)", "(sent)(.*?)(to)(\\s)",
                                "(debited)(.*?)(to)(\\s)", "(credited)(.*?)(in)(\\s)", "(credited)(.*?)(to)(\\s)" };

                        String[] key_debit_search = { "(made)", "(debited)", "(using)", "(paid)", "(purchase)",
                                "(withdrawn)", "(credited)(.*?)(from)(\\s)", "(sent)(.*?)(from)(\\s)",
                                "(\\s)(received)(.*?)(from)(\\s)" };

                        String transType = "";

                        Boolean isDeposit = validateTransType(key_credit_search, msgBody.toLowerCase());
                        Boolean isWithdrawal = validateTransType(key_debit_search, msgBody.toLowerCase());

                        if (isDeposit == true) {
                            if (isWithdrawal == true) {
                                transType = "Transfer";
                                String[] transCategory = getCategoryOrSubCategoryByName("Transfer");

                                if (!transCategory[0].isEmpty()) {
                                    mCommon.transactionEntity.setCategoryId(parseInt(transCategory[0]));
                                }

                                if (!transCategory[1].isEmpty()) {
                                    mCommon.transactionEntity.setSubcategoryId(parseInt(transCategory[1]));
                                }

                                mCommon.transactionEntity.setTransactionType(TransactionTypes.Transfer);

                            } else {
                                transType = "Deposit";
                                String[] incomeCategory = getCategoryOrSubCategoryByName("Income");

                                if (!incomeCategory[0].isEmpty()) {
                                    mCommon.transactionEntity.setCategoryId(parseInt(incomeCategory[0]));
                                }

                                if (!incomeCategory[1].isEmpty()) {
                                    mCommon.transactionEntity.setSubcategoryId(parseInt(incomeCategory[1]));
                                }

                                mCommon.transactionEntity.setTransactionType(TransactionTypes.Deposit);
                            }

                        } else if (isWithdrawal == true) {
                            transType = "Withdrawal";
                            mCommon.transactionEntity.setTransactionType(TransactionTypes.Withdrawal);
                        }

                        mCommon.transactionEntity.setStatus("");
                        mCommon.payeeName = "";

                        if (transType != "" && msgBody.toLowerCase().contains("otp") == false) { // if not from blank, then nothing to do with sms

                            //Create the intent thatll fire when the user taps the notification//
                            Intent t_intent = new Intent(mContext, CheckingTransactionEditActivity.class);

                            // Db setup
                            MmxHelper = new MmxOpenHelper(mContext,
                                    app_settings.getDatabaseSettings().getDatabasePath());
                            db = MmxHelper.getReadableDatabase();

                            baseCurencyID = gen_settings.getBaseCurrencytId();
                            baseAccountID = gen_settings.getDefaultAccountId();
                            baseAccountName = "";
                            fromAccountID = -1;
                            fromCurrencyID = -1;
                            fromAccountName = "";

                            //if default account id selected
                            if (baseAccountID > 0) {
                                fromAccountID = baseAccountID;
                                fromAccountName = baseAccountName;
                                fromCurrencyID = baseCurencyID;
                            }

                            //Get the base currency sysmbl
                            baseCurrencySymbl = getCurrencySymbl(baseCurencyID);
                            fromAccCurrencySymbl = baseCurrencySymbl;

                            //get te from acount details
                            extractAccountDetails(msgBody, transType);

                            if (!fromAccountDetails[0].isEmpty()) {
                                fromAccountID = parseInt(fromAccountDetails[0]);
                                fromAccountName = fromAccountDetails[1];
                                fromCurrencyID = parseInt(fromAccountDetails[2]);
                                fromAccCurrencySymbl = fromAccountDetails[3];
                                mCommon.transactionEntity.setAccountId(fromAccountID);
                            }

                            mCommon.transactionEntity.setNotes(msgBody);
                            mCommon.transactionEntity.setDate(new MmxDate().toDate());

                            //get the trans amount
                            String transAmount = extractTransAmount(msgBody, fromAccCurrencySymbl);
                            String[] transPayee = extractTransPayee(msgBody);

                            //If there is no account no. or payee in the msg & no amt, then this is not valid sms to do transaction
                            if ((!fromAccountDetails[6].isEmpty() || !toAccountDetails[6].isEmpty()
                                    || !transPayee[0].isEmpty()) && !transAmount.isEmpty()) {

                                mCommon.transactionEntity.setAmount(MoneyFactory.fromString(transAmount));

                                String transRefNo = extractTransRefNo(msgBody);

                                //set the ref no. if exists
                                if (!transRefNo.isEmpty()) {
                                    mCommon.transactionEntity.setTransactionNumber(transRefNo);
                                }

                                int txnId = getTxnId(transRefNo.trim(), mCommon.transactionEntity.getDateString());

                                switch (txnId) {
                                case 0: //add new trnsaction

                                    if (transType == "Transfer") //if it is transfer
                                    {
                                        if (!toAccountDetails[0].isEmpty()) // if id exists then considering as account transfer
                                        {
                                            toAccountID = parseInt(toAccountDetails[0]);
                                            toAccountName = toAccountDetails[1];
                                            toCurrencyID = parseInt(toAccountDetails[2]);
                                            toAccCurrencySymbl = toAccountDetails[3];

                                            mCommon.transactionEntity.setAccountToId(toAccountID);

                                            //convert the to amount from the both currency details
                                            CurrencyService currencyService = new CurrencyService(mContext);
                                            mCommon.transactionEntity
                                                    .setAmountTo(currencyService.doCurrencyExchange(fromCurrencyID,
                                                            mCommon.transactionEntity.getAmount(), toCurrencyID));

                                            mCommon.transactionEntity.setPayeeId(Constants.NOT_SET);

                                        } else { // if not, then IMPS transfer tp 3rd party

                                            transType = "Withdrawal";
                                            mCommon.transactionEntity
                                                    .setTransactionType(TransactionTypes.Withdrawal);
                                            mCommon.transactionEntity.setAccountToId(Constants.NOT_SET);
                                            mCommon.transactionEntity
                                                    .setAmountTo(MoneyFactory.fromString(transAmount));

                                            //if there is no to account found from mmex db, then check for payee
                                            //This will helps me to handle 3rd party transfer thru IMPS
                                            if (!toAccountDetails[6].isEmpty() && transPayee[0].isEmpty()) {
                                                transPayee = getPayeeDetails(toAccountDetails[6].trim());
                                            }
                                        }
                                    } else {
                                        mCommon.transactionEntity.setAccountToId(Constants.NOT_SET);
                                        mCommon.transactionEntity.setAmountTo(MoneyFactory.fromString(transAmount));
                                    }

                                    if (!transPayee[0].isEmpty()) {

                                        mCommon.transactionEntity.setPayeeId(parseInt(transPayee[0]));
                                        mCommon.payeeName = transPayee[1];
                                        mCommon.transactionEntity.setCategoryId(parseInt(transPayee[2]));
                                        mCommon.transactionEntity.setSubcategoryId(parseInt(transPayee[3]));
                                    }

                                    t_intent.setAction(Intent.ACTION_INSERT); //Set the action

                                    break;

                                default: //Update existing transaction

                                    transType = "Transfer";

                                    AccountTransactionRepository repo = new AccountTransactionRepository(mContext);
                                    AccountTransaction txn = repo.load(txnId);

                                    if (txn != null) {

                                        if (txn.getTransactionType() != TransactionTypes.Transfer) {

                                            AccountRepository accountRepository = new AccountRepository(mContext);

                                            if (txn.getTransactionType() == TransactionTypes.Deposit) {
                                                toAccountID = txn.getAccountId();
                                                toCurrencyID = accountRepository
                                                        .loadCurrencyIdFor(txn.getAccountId());
                                            } else {
                                                toAccountID = fromAccountID;
                                                toCurrencyID = fromCurrencyID;
                                                fromCurrencyID = accountRepository
                                                        .loadCurrencyIdFor(txn.getAccountId());
                                            }

                                            mCommon.transactionEntity = txn;
                                            mCommon.transactionEntity.setTransactionType(TransactionTypes.Transfer);
                                            mCommon.transactionEntity.setAccountId(fromAccountID);
                                            mCommon.transactionEntity.setAccountToId(toAccountID);

                                            //convert the to amount from the both currency details
                                            CurrencyService currencyService = new CurrencyService(mContext);
                                            mCommon.transactionEntity
                                                    .setAmountTo(currencyService.doCurrencyExchange(fromCurrencyID,
                                                            mCommon.transactionEntity.getAmount(), toCurrencyID));

                                            mCommon.transactionEntity.setPayeeId(Constants.NOT_SET);

                                            String[] transCategory = getCategoryOrSubCategoryByName("Transfer");
                                            if (!transCategory[0].isEmpty()) {
                                                mCommon.transactionEntity.setCategoryId(parseInt(transCategory[0]));
                                                mCommon.transactionEntity
                                                        .setSubcategoryId(parseInt(transCategory[1]));
                                            }

                                            mCommon.transactionEntity.setNotes(
                                                    mCommon.transactionEntity.getNotes() + "\n\n" + msgBody);

                                            t_intent.setAction(Intent.ACTION_EDIT); //Set the action
                                        } else //if transfer already exists, then do nothing
                                        {
                                            skipSaveTrans = true;
                                        }

                                    }

                                }

                                // Capture the details the for Toast
                                String strExtracted = "Account = " + fromAccountName + "-" + fromAccountDetails[6]
                                        + "\n" + "Trans Amt = " + fromAccCurrencySymbl + " " + transAmount + ",\n"
                                        + "Payyee Name= " + transPayee[1] + "\n" + "Category ID = " + transPayee[2]
                                        + "\n" + "Sub Category ID = " + transPayee[3] + "\n" + "Trans Ref No. = "
                                        + transRefNo + "\n" + "Trans Type = " + transType + "\n";

                                //Must be commented for released version
                                //mCommon.transactionEntity.setNotes(strExtracted);

                                // Set the content for a transaction);
                                t_intent.putExtra(EditTransactionActivityConstants.KEY_TRANS_SOURCE,
                                        "SmsReceiverTransactions.java");
                                t_intent.putExtra(EditTransactionActivityConstants.KEY_TRANS_ID,
                                        mCommon.transactionEntity.getId());
                                t_intent.putExtra(EditTransactionActivityConstants.KEY_ACCOUNT_ID,
                                        String.valueOf(mCommon.transactionEntity.getAccountId()));
                                t_intent.putExtra(EditTransactionActivityConstants.KEY_TO_ACCOUNT_ID,
                                        String.valueOf(mCommon.transactionEntity.getAccountToId()));
                                t_intent.putExtra(EditTransactionActivityConstants.KEY_TRANS_CODE,
                                        mCommon.getTransactionType());
                                t_intent.putExtra(EditTransactionActivityConstants.KEY_PAYEE_ID,
                                        String.valueOf(mCommon.transactionEntity.getPayeeId()));
                                t_intent.putExtra(EditTransactionActivityConstants.KEY_PAYEE_NAME,
                                        mCommon.payeeName);
                                t_intent.putExtra(EditTransactionActivityConstants.KEY_CATEGORY_ID,
                                        String.valueOf(mCommon.transactionEntity.getCategoryId()));
                                t_intent.putExtra(EditTransactionActivityConstants.KEY_SUBCATEGORY_ID,
                                        String.valueOf(mCommon.transactionEntity.getSubcategoryId()));
                                t_intent.putExtra(EditTransactionActivityConstants.KEY_TRANS_AMOUNT,
                                        String.valueOf(mCommon.transactionEntity.getAmount()));
                                t_intent.putExtra(EditTransactionActivityConstants.KEY_NOTES,
                                        mCommon.transactionEntity.getNotes());
                                t_intent.putExtra(EditTransactionActivityConstants.KEY_TRANS_DATE,
                                        new MmxDate().toDate());
                                t_intent.putExtra(EditTransactionActivityConstants.KEY_TRANS_NUMBER,
                                        mCommon.transactionEntity.getTransactionNumber());

                                // validate and save the transaction
                                if (skipSaveTrans == false) {
                                    if (validateData()) {
                                        if (saveTransaction()) {
                                            Toast.makeText(context,
                                                    "MMEX: Bank Transaction Processed for: \n\n" + strExtracted,
                                                    Toast.LENGTH_LONG).show();
                                            autoTransactionStatus = true;
                                        }
                                    }

                                    //if transaction is not created automatically, then invoke notification or activity screen
                                    if (autoTransactionStatus == false) {
                                        startActivity(mContext, t_intent, null);
                                        //showNotification(t_intent, strExtracted);
                                    }
                                } else {
                                    Toast.makeText(context,
                                            "MMEX: Skiping Bank Transaction updates SMS, because transaction exists with ref. no. "
                                                    + transRefNo,
                                            Toast.LENGTH_LONG).show();
                                }

                                //reset the value
                                msgBody = "";
                                msgSender = "";
                                bundle = null;
                                msgs = null;
                                mCommon = null;
                                skipSaveTrans = false;

                            }
                        }
                    }
                }
            }
        } catch (Exception e) {
            Timber.e(e, "MMEX: Bank Transaction Process EXCEPTION");
        }
    }

    private static String getCurrencySymbl(int currencyID) {
        //Get the currency sysmbl
        String currencySymbl = "";
        String reqCurrFields[] = { "CURRENCYID", "DECIMAL_POINT", "GROUP_SEPARATOR", "CURRENCY_SYMBOL" };

        try {
            Cursor currencyCursor = db.query("CURRENCYFORMATS_V1", reqCurrFields, "CURRENCYID = ?",
                    new String[] { String.valueOf(currencyID) }, null, null, null);

            if (currencyCursor.moveToFirst()) {
                currencySymbl = currencyCursor.getString(currencyCursor.getColumnIndex("CURRENCY_SYMBOL"));
            }

            currencyCursor.close();
        } catch (Exception e) {
            Timber.e(e, "getCurrencySymbl");
        }

        return currencySymbl;

    }

    private static boolean isTransactionSms(String smsSender) {
        boolean reqMatch = false;

        try {
            Pattern p = Pattern.compile("(-[a-zA-Z]+)");
            Matcher m = p.matcher(smsSender);

            if (m != null) {
                while (m.find()) {
                    reqMatch = true;
                    break;
                }
            }
        } catch (Exception e) {
            Timber.e(e, "isTransactionSms");
        }

        return reqMatch;
    }

    private static boolean validateTransType(String[] keySearch, String smsMsg) {
        boolean reqMatch = false;

        try {
            for (int i = 0; i <= keySearch.length - 1; i++) {
                Pattern p = Pattern.compile(keySearch[i]);
                Matcher m = p.matcher(smsMsg);

                if (m != null && reqMatch == false) {
                    while (m.find()) {
                        reqMatch = true;
                        break;
                    }
                }
            }
        } catch (Exception e) {
            Timber.e(e, "validateTransType");
        }

        return reqMatch;
    }

    private static void extractAccountDetails(String smsMsg, String transType) {
        String[] reqMatch = new String[] { "", "" };

        fromAccountDetails = new String[] { "", "", "", "", "", "", "" };
        toAccountDetails = new String[] { "", "", "", "", "", "", "" };

        int[] mIndx;

        if (transType == "Transfer") {
            mIndx = new int[] { 1, 2 };
        } else {
            mIndx = new int[] { 1 };
        }

        try {
            //find the match for UPI transfer which has "from" or "to" string
            boolean isUPI = smsMsg.contains("@");

            switch (String.valueOf(isUPI)) {
            case "false": //find the match for non UPI transfer or credit or debit

                for (int j = 0; j <= mIndx.length - 1; j++) {
                    reqMatch[j] = searchForAccountNum(smsMsg, mIndx[j]);
                }
                break;

            case "true": //UPI transfer based on from and to account

                String fromString = " from";
                String toString = " to";
                String nonUPIMsg = smsMsg;

                int fromIndex = smsMsg.indexOf(fromString);
                int toIndex = smsMsg.indexOf(toString);

                // sometime str "to" not exists, in place use str "in your"
                if (toIndex == -1) {
                    toString = " in your";
                    toIndex = smsMsg.indexOf(toString);
                }

                if (fromIndex > 0) {
                    if (fromIndex > toIndex) {
                        reqMatch[0] = searchForAccountNum(smsMsg.substring(fromIndex), 1);
                        if (toIndex == -1) {
                            nonUPIMsg = smsMsg.substring(0, fromIndex);
                        }
                    } else {
                        reqMatch[0] = searchForAccountNum(smsMsg.substring(fromIndex, toIndex), 1);
                        nonUPIMsg = smsMsg.substring(0, fromIndex);
                    }
                }

                if (toIndex > 0) {
                    if (toIndex > fromIndex) {
                        reqMatch[1] = searchForAccountNum(smsMsg.substring(toIndex), 1);
                        if (toIndex == -1) {
                            nonUPIMsg = smsMsg.substring(0, toIndex);
                        }
                    } else {
                        reqMatch[1] = searchForAccountNum(smsMsg.substring(toIndex, fromIndex), 1);
                        nonUPIMsg = smsMsg.substring(0, toIndex);
                    }
                }

                if (fromIndex == -1) {
                    reqMatch[0] = searchForAccountNum(nonUPIMsg, 1);
                }
                if (toIndex == -1) {
                    reqMatch[1] = searchForAccountNum(nonUPIMsg, 1);
                }

                //if both the str are same then, reset 2nd index
                if (reqMatch[0].contains(reqMatch[1])) {
                    reqMatch[1] = "";
                }

                break;
            }

            getAccountDetails(reqMatch);

        } catch (Exception e) {
            Timber.e(e, "extractAccountDetails");
        }
    }

    private static String searchForAccountNum(String smsMsg, int mIndx) {
        String reqMatch = "";

        String[] searchFor = { "((\\s)?((\\d+)?[X]+(\\d+))(\\s)?)", "((\\s)?((\\d+)?[x]+(\\d+))(\\s)?)",
                "((\\s)?((\\d+)?[\\*]+(\\d+))(\\s)?)", "([\\(]((.*?)[@](.*?))[\\)])", "(from((.*?)@(.*?))[.])",
                "(linked((.*?)@(.*?))[.])", "((\\s)virtual(\\s)address((.*?)@(.*?))(\\s))", "([\\[](\\d+)[\\]])",
                "(using(.*?)(\\.))", "(.ay.m\\s.allet)" };

        int[] getGroup = { 3, 3, 3, 2, 2, 2, 4, 2, 2, 1 };

        int mFound;

        try {
            for (int i = 0; i <= searchFor.length - 1; i++) {
                mFound = 1;

                Pattern p = Pattern.compile(searchFor[i]);
                Matcher m = p.matcher(smsMsg);

                if (m != null && reqMatch.isEmpty()) {
                    while (m.find()) {
                        if (mFound == mIndx) {
                            reqMatch = m.group(getGroup[i]).trim();
                            break;
                        } else {
                            mFound = mFound + 1;
                        }
                    }
                }
            }
        } catch (Exception e) {
            Timber.e(e, "searchForAccountNum");
        }

        return reqMatch;
    }

    private static String extractTransAmount(String smsMsg, String fromAccCurrencySymbl) {
        String reqMatch = "";
        smsMsg = smsMsg.replace(",", "");
        String searchFor = "((\\s)?##SEARCH4CURRENCY##(.)?(\\s)?((\\d+)(\\.\\d+)?))";
        int[] getGroup = { 5 };

        //Handle multiple symbol for currency
        String[] searchCurrency;

        if (fromAccCurrencySymbl.contentEquals("INR")) {
            searchCurrency = new String[] { "INR", "Rs" };
        } else {
            searchCurrency = new String[] { fromAccCurrencySymbl };
        }

        try {
            for (int i = 0; i <= searchCurrency.length - 1; i++) {
                Pattern p = Pattern.compile(searchFor.replace("##SEARCH4CURRENCY##", searchCurrency[i]));
                Matcher m = p.matcher(smsMsg);

                if (m != null && reqMatch.isEmpty()) {
                    while (m.find()) {
                        reqMatch = m.group(getGroup[0]).trim();
                        break;
                    }
                }
            }

        } catch (Exception e) {
            Timber.e(e, "extractTransAmount");
        }

        return reqMatch;
    }

    private static String[] extractTransPayee(String smsMsg) {
        String[] searchFor = { "((\\s)at\\s(.*?)\\s+on)", "((\\s)favoring\\s(.*?)\\s+is)",
                "((\\s)to\\s(.*?)\\s+at)", "((\\s)to\\s(.*?)[.])", "((\\s)at\\s(.*?)[.])", "([\\*](.*?)[.])",
                "((\\s)FROM\\s(.*?)\\s+\\d)", "(from\\s(.*?)\\s(\\())", "(([a-zA-Z]+)(\\s)has(\\s)added)" };

        int[] getGroup = { 3, 3, 3, 3, 3, 2, 3, 2, 2 };
        String[] reqMatch = new String[] { "", "", "", "" };

        try {
            for (int i = 0; i <= searchFor.length - 1; i++) {
                Pattern p = Pattern.compile(searchFor[i]);
                Matcher m = p.matcher(smsMsg);

                if (m != null && reqMatch[0].isEmpty()) {
                    while (m.find()) {
                        reqMatch = getPayeeDetails(String.valueOf(m.group(getGroup[i]).trim()));

                        if (!reqMatch[0].isEmpty()) {
                            break;
                        }
                    }
                }
            }
        } catch (Exception e) {
            Timber.e(e, "extractTransPayee");
        }

        return reqMatch;
    }

    private static String extractTransRefNo(String smsMsg) {
        String reqMatch = "";
        String[] searchFor = { "(Cheque\\sNo[.*?](\\d+))", "(Ref\\s+no+\\s(\\d+))", "(\\/+(\\d+)+\\/)",
                "(ID(.)?(:)?(\\s)?((.*?)\\w+))", "(ID(.)?(:)?)(\\s)?(\\d+)", "(id(\\s)is(\\s)?(:)?(\\d+))",
                "((Reference:)(\\s)?(\\d+))", "([\\*](\\d+)[\\*])", "(Info(:)+(.*?)(\\d+)[:]?[-]?)",
                "((reference number)(.*?)(\\d+))", "(\\s)?#(\\s?)(\\d+)(\\s?)" };
        int[] getGroup = { 2, 2, 2, 5, 5, 5, 4, 2, 4, 4, 3 };

        try {
            for (int i = 0; i <= searchFor.length - 1; i++) {
                Pattern p = Pattern.compile(searchFor[i]);
                Matcher m = p.matcher(smsMsg);

                if (m != null && reqMatch.isEmpty()) {
                    while (m.find()) {
                        reqMatch = m.group(getGroup[i]).trim();
                        break;
                    }
                }
            }
        } catch (Exception e) {
            Timber.e(e, "extractTransRefNo");
        }

        return reqMatch;
    }

    private static String[] getPayeeDetails(String payeeName) {
        String[] payeeDetails = new String[] { "", payeeName.trim(), "", "" };

        try {
            if (!payeeName.trim().isEmpty()) {

                String sql = "SELECT PAYEEID, PAYEENAME, CATEGID, SUBCATEGID " + "FROM PAYEE_V1 "
                        + "WHERE PAYEENAME LIKE '%" + payeeName + "%' " + "ORDER BY PAYEENAME LIMIT 1";

                Cursor payeeCursor = db.rawQuery(sql, null);

                if (payeeCursor.moveToFirst()) {
                    payeeDetails = new String[] { payeeCursor.getString(payeeCursor.getColumnIndex("PAYEEID")),
                            payeeCursor.getString(payeeCursor.getColumnIndex("PAYEENAME")),
                            payeeCursor.getString(payeeCursor.getColumnIndex("CATEGID")),
                            payeeCursor.getString(payeeCursor.getColumnIndex("SUBCATEGID")) };
                }

                payeeCursor.close();
            }
        } catch (Exception e) {
            Timber.e(e, "getPayeeDetails");
        }

        return payeeDetails;
    }

    private static Integer getTxnId(String refNumber, String transDate) {
        int txnId = 0;

        try {
            if (!refNumber.trim().isEmpty()) {

                String sql = "SELECT TRANSID " + "FROM CHECKINGACCOUNT_V1 " + "WHERE TRANSACTIONNUMBER  LIKE '%"
                        + refNumber + "%' " + "AND TRANSDATE ='" + transDate + "' " + "ORDER BY TRANSID LIMIT 1";

                Cursor txnCursor = db.rawQuery(sql, null);

                if (txnCursor.moveToFirst()) {
                    txnId = parseInt(txnCursor.getString(txnCursor.getColumnIndex("TRANSID")));
                }

                txnCursor.close();
            }
        } catch (Exception e) {
            Timber.e(e, "getTxnId");
        }

        return txnId;
    }

    private static String[] getCategoryOrSubCategoryByName(String searchName) {
        String[] cTran = new String[] { "", "" };

        try {
            if (!searchName.trim().isEmpty()) {

                String sql = "SELECT c.CATEGID, c.CATEGNAME, s.SUBCATEGID, s.SUBCATEGNAME " + "FROM CATEGORY_V1 c  "
                        + "INNER JOIN SUBCATEGORY_V1 s ON s.CATEGID=c.CATEGID " + "WHERE s.SUBCATEGNAME = '"
                        + searchName + "' " + "ORDER BY s.SUBCATEGID  LIMIT 1";

                Cursor cCursor = db.rawQuery(sql, null);

                if (cCursor.moveToFirst()) {
                    cTran = new String[] { cCursor.getString(cCursor.getColumnIndex("CATEGID")),
                            cCursor.getString(cCursor.getColumnIndex("SUBCATEGID")) };
                } else { //search in only catogery

                    sql = "SELECT c.CATEGID, c.CATEGNAME " + "FROM CATEGORY_V1 c  " + "WHERE c.CATEGNAME = '"
                            + searchName + "' " + "ORDER BY c.CATEGID  LIMIT 1";

                    cCursor = db.rawQuery(sql, null);

                    if (cCursor.moveToFirst()) {
                        cTran = new String[] { cCursor.getString(cCursor.getColumnIndex("CATEGID")), "-1" };
                    }
                }

                cCursor.close();
            }
        } catch (Exception e) {
            Timber.e(e, "getCategoryOrSubCategoryByName");
        }

        return cTran;
    }

    private static void getAccountDetails(String[] reqMatch) {
        String[] accountDetails = new String[] { "", "", "", "", "", "", "" };

        try {
            for (int j = 0; j <= reqMatch.length - 1; j++) {
                if (reqMatch[j] != "") {

                    accountDetails = new String[] { "", "", "", "", "", "", reqMatch[j] };

                    String sql = "SELECT A.ACCOUNTID, A.ACCOUNTNAME, A.ACCOUNTNUM, A.CURRENCYID, "
                            + "C.CURRENCY_SYMBOL, C.DECIMAL_POINT, C.GROUP_SEPARATOR " + "FROM ACCOUNTLIST_V1 A "
                            + "INNER JOIN CURRENCYFORMATS_V1 C ON C.CURRENCYID = A.CURRENCYID "
                            + "WHERE A.STATUS='Open' AND A.ACCOUNTNUM LIKE '%" + reqMatch[j] + "%' "
                            + "ORDER BY A.ACCOUNTID " + "LIMIT 1";

                    Cursor accountCursor = db.rawQuery(sql, null);

                    if (accountCursor.moveToFirst()) {
                        accountDetails = new String[] {
                                accountCursor.getString(accountCursor.getColumnIndex("ACCOUNTID")),
                                accountCursor.getString(accountCursor.getColumnIndex("ACCOUNTNAME")),
                                accountCursor.getString(accountCursor.getColumnIndex("CURRENCYID")),
                                accountCursor.getString(accountCursor.getColumnIndex("CURRENCY_SYMBOL")),
                                accountCursor.getString(accountCursor.getColumnIndex("DECIMAL_POINT")),
                                accountCursor.getString(accountCursor.getColumnIndex("GROUP_SEPARATOR")),
                                reqMatch[j] };
                    }

                    switch (j) {
                    case 0: //from account
                        fromAccountDetails = accountDetails;
                        break;
                    case 1: //to account
                        toAccountDetails = accountDetails;
                        break;
                    }

                    accountCursor.close();
                }
            }
        } catch (Exception e) {
            Timber.e(e, "getAccountDetails");
        }
    }

    public boolean validateData() {

        boolean isTransfer = mCommon.transactionEntity.getTransactionType().equals(TransactionTypes.Transfer);
        Core core = new Core(mContext);

        if (mCommon.transactionEntity.getAccountId() == Constants.NOT_SET) {
            //Toast.makeText(mContext, "MMEX : " + (R.string.error_toaccount_not_selected), Toast.LENGTH_LONG).show();
            return false;
        }

        if (isTransfer) {
            if (mCommon.transactionEntity.getAccountToId() == Constants.NOT_SET) {
                //Toast.makeText(mContext, "MMEX : " + (R.string.error_toaccount_not_selected), Toast.LENGTH_LONG).show();
                return false;
            }
            if (mCommon.transactionEntity.getAccountToId().equals(mCommon.transactionEntity.getAccountId())) {
                //Toast.makeText(mContext, "MMEX : " + (R.string.error_transfer_to_same_account), Toast.LENGTH_LONG).show();
                return false;
            }

            // Amount To is required and has to be positive.
            if (this.mCommon.transactionEntity.getAmountTo().toDouble() <= 0) {
                //Toast.makeText(mContext, "MMEX : " + (R.string.error_amount_must_be_positive), Toast.LENGTH_LONG).show();
                return false;
            }
        } else {

            // payee required for automatic transactions.
            if (!mCommon.transactionEntity.hasPayee()) {
                //Toast.makeText(mContext, "MMEX : " + (R.string.error_amount_must_be_positive), Toast.LENGTH_LONG).show();
                return false;
            }

        }

        // Amount is required and must be positive. Sign is determined by transaction type.
        if (mCommon.transactionEntity.getAmount().toDouble() <= 0) {
            //Toast.makeText(mContext, "MMEX : " + (R.string.error_amount_must_be_positive), Toast.LENGTH_LONG).show();
            return false;
        }

        // Category is required if tx is not a split or transfer.
        boolean hasCategory = mCommon.transactionEntity.hasCategory();
        if (!hasCategory && !isTransfer) {
            //Toast.makeText(mContext, "MMEX : " + (R.string.error_category_not_selected), Toast.LENGTH_LONG).show();
            return false;
        }

        return true;
    }

    public boolean saveTransaction() {

        AccountTransactionRepository repo = new AccountTransactionRepository(mContext);

        if (!mCommon.transactionEntity.hasId()) {
            // insert
            mCommon.transactionEntity = repo.insert((AccountTransaction) mCommon.transactionEntity);

            if (!mCommon.transactionEntity.hasId()) {
                Toast.makeText(mContext, R.string.db_checking_insert_failed, Toast.LENGTH_SHORT).show();
                Timber.w("Insert new transaction failed!");
                return false;
            }
        } else {
            // update
            boolean updated = repo.update((AccountTransaction) mCommon.transactionEntity);
            if (!updated) {
                Toast.makeText(mContext, R.string.db_checking_update_failed, Toast.LENGTH_SHORT).show();
                Timber.w("Update transaction failed!");
                return false;
            }
        }
        return true;
    }

    /**
     * Note: Check the new NotificationUtils for creation of notification channel and the code that
     * utilizes it.
     * @param intent
     * @param notificationText
     */
    private void showNotification(Intent intent, String notificationText) {

        int NOTIFICATION_ID = (int) ((new Date().getTime() / 1000L) % Integer.MAX_VALUE);

        String NOTIFICATION_CHANNEL_ID = "ammex_" + String.valueOf(NOTIFICATION_ID); // The id of the channel.

        intent.putExtra("NOTIFICATION_ID", NOTIFICATION_ID);

        PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 1910, intent, 0);

        // Gets an instance of the NotificationManager service
        NotificationManager mNotificationManager = (NotificationManager) mContext
                .getSystemService(Context.NOTIFICATION_SERVICE);

        //Get an instance of NotificationManager//
        NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(mContext, NOTIFICATION_CHANNEL_ID)
                .setSmallIcon(R.drawable.ic_stat_notification)
                .setContentTitle(mContext.getString(R.string.application_name) + " - SMS Auto Transaction Failed")
                .setContentText(notificationText)
                .addAction(R.drawable.ic_action_folder_open_dark, "Edit", pendingIntent)
                .setContentIntent(pendingIntent).setPriority(Notification.PRIORITY_MAX).setNumber(NOTIFICATION_ID)
                .setAutoCancel(false);

        mNotificationManager.cancel(NOTIFICATION_ID);
        mNotificationManager.notify(NOTIFICATION_ID, mBuilder.build());
    }
}