Search code examples
xamlxamarin.formsxamarin.android

Read the SMS activation code automatically in Xamarin Forms instead of manually typing it by user


I wrote a project with Xamarin Forms. When every user has signed up, I send him/her an activation Code to confirm it and the user has to insert it to enter the app. But I am looking for a plugin or a way that the user does not need to insert the activation Code.

I want the activation Code to be read automatically without the need to enter it manually.


Solution

  • First add the required permissions in AndroidManifest:

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

    Here is SmsReceiver class in Android project:

        using System.Linq;
        using System.Text.RegularExpressions;
        using Android.App;
        using Android.Content;
        using Android.Runtime;
        using Android.Telephony;
        using Java.Lang;
        using Xamarin.Forms;
        namespace MyProject.Android
        {
            [BroadcastReceiver(Enabled = true, Label = "SMS Receiver")]
            [IntentFilter(new string[] { "android.provider.Telephony.SMS_RECEIVED", Intent.CategoryDefault })]
            public class SmsReceiver : BroadcastReceiver
            {
                private const string IntentAction = "android.provider.Telephony.SMS_RECEIVED";
                private static readonly string Sender = "SMS Sender number here";
                private static readonly string[] OtpMessageBodyKeywordSet = {"Keyword1", "Keyword2"}; //You must define your own Keywords
                public override void OnReceive(Context context, Intent intent)
                {
                    try
                    {
                        if (intent.Action != IntentAction) return;
                        var bundle = intent.Extras;
                        if (bundle == null) return;
                        var pdus = bundle.Get("pdus");
                        // var castedPdus = JNIEnv.GetArray(pdus.Handle);
                        var castedPdus = JNIEnv.GetArray<Object>(pdus.Handle);
                        var msgs = new SmsMessage[castedPdus.Length];
                        var sb = new StringBuilder();
                        string sender = null;
                        for (var i = 0; i < msgs.Length; i++)
                        {
                            var bytes = new byte[JNIEnv.GetArrayLength(castedPdus[i].Handle)];
                            JNIEnv.CopyArray(castedPdus[i].Handle, bytes);
                            string format = bundle.GetString("format");
                            msgs[i] = SmsMessage.CreateFromPdu(bytes, format);
                            if (sender == null)
                                sender = msgs[i].OriginatingAddress;
                            sb.Append(string.Format("SMS From: {0}{1}Body: {2}{1}", msgs[i].OriginatingAddress,
                                System.Environment.NewLine, msgs[i].MessageBody));
                            //Toast.MakeText(context, sb.ToString(), ToastLength.Long).Show();
                            //Log.Error("Vahid", sb.ToString());
        
                            var msgBody = msgs[i].MessageBody;
                            if(!sender.Contains(Sender)) return;
                            bool foundKeyword = OtpMessageBodyKeywordSet.Any(k => msgBody.Contains(k));
        
                            if (!foundKeyword) return;
                            var code = ExtractNumber(msgBody);
                            MessagingCenter.Send<RegisterSecondPageModel, string>(new RegisterSecondPageModel(), "OtpReceived", code);
        
                        }
                    }
                    catch (System.Exception ex)
                    {
                        //Toast.MakeText(context, ex.Message, ToastLength.Long).Show();
                    }
                }
    
                private static string ExtractNumber(string text)
                {
                    if (string.IsNullOrEmpty(text)) return "";
                    var regPattern = @"\d+";
                    var number = Regex.Match(text, regPattern).Value;
                    return number;
                }
    
            }
        }
    

    Note: In order to filter out the coming SMSes and detect only our own SMS we can apply these two filters:

    1- Ignoring all SMSes that their sender numbers are not our SMS sender number.

    2- Sometimes our SMS sender might send different SMSes to our clients, for example one SMS to send an activation code, and another to inform and confirm user's successfully registration in system. That said, we gotta distinguish them. For that we can search message body in order to find some pre-defined keywords. Of course our SMS server has to stick to a defined body format. "Activation", "Code", "Activation code" could be some example keywords in English language. Of course keywords should be defined in each language accordingly.

    Here is RegisterSecondPageModel inside PCL project:

    public class RegisterSecondPageModel
    {
        public RegisterSecondPageModel()
        {
            SubscribeToOtpReceiving();
        }
        
        private void SubscribeToOtpReceiving()
        {
            MessagingCenter.Subscribe<RegisterSecondPageModel, string>(this, "OtpReceived", (sender, code) =>
            {
                ActivationCode = code;
            });
        }
    }
    

    Another note is that as Jason already said, iOS doesn't allow apps to read SMSes.