jwan
jwan

Reputation: 51

Delphi Firemonkey (Android) - Detecting Failed Sending SMS

when my app was using Android5, I can monitor if the SMS was sent or failed by checking the content://sms/sent and content://sms/failed. Aside from via my app, I can also see the message and its status directly from the SMS app of the phone.

Now that the good old Android5 smartphone is dead, I am upgrading to a newer smartphone base on Android8 (Oreo). However I am facing a problem of detecting the status of the sent sms.

First... I can no longer "see" the sms using the phone's default SMS app. Second... I cant also find the sms using my app even if I scroll through all messages one by one.

This is the code I use to send the SMS:

smsManager := TJSmsManager.JavaClass.getDefault;
smsTo      := StringToJString(targetstr);
smsarray   := smsmanager.divideMessage(stringtojstring(bodystr));
smsManager.sendmultiparttextMessage(smsTo, nil,smsarray, nil, nil);

While this is the code I use to scroll through all my messages:

uri := StrToJURI('content://sms');
smscursor := TAndroidHelper.Activity.getContentResolver.query(uri, nil, nil,nil,nil);
id_id := smscursor.getColumnIndex(StringToJstring('_id'));
idaddress := smscursor.getColumnIndex(StringToJstring('address'));
idbody := smscursor.getColumnIndex(StringToJstring('body'));
idstatus := smscursor.getColumnIndex(StringToJstring('status'));
idthread_id := smscursor.getColumnIndex(StringToJstring('thread_id'));
idtype := smscursor.getColumnIndex(StringToJstring('type'));
smscursor.moveToFirst;

** using the repeat until loop to read each and every sms until "islast" is true **

But I cant find the sent SMS irregardless if the SMS was successfully sent or not.

I need to do this detection because the signal strength in my area is very low and around 20% of the time the sms failed, and my app should resend the intended message.

PS: I also tried searching for the SMS from the content://sms/sent and content://sms/failed but to no avail.

Anyway, my questions are:

  1. How to make the send out sms show up inside the phone's default sms app?
  2. How to correctly determine if the send out sms was successful or not?

I read somewhere that the method above is using the API and there is a way to use the phone's sms app instead... But I dont have any idea how to do it.

Thank you.

Upvotes: 0

Views: 417

Answers (1)

jwan
jwan

Reputation: 51

For the benefit of others who may be in similar situation that requires to:

(A) Able to send an SMS directly from the app without using the Phone's built-in SMS default app

(B) The SMS length can be more than the default 160 characters limit (using sendmultiparttextMessage)

(C) Able to know if the SMS was process or not (base on aResultCode of the onReceive from the PendingIntent)

Known Limitation:

  • Under multi-sim phone, sms is sent via the default sim
  • if sending to multiple phone recipients, there is no way to know which ones failed, if any. So I suggest send 1 sms to 1 recipient at a time
  • Doesnt detect if the intended recipient receive the sms or not as the DeliveryIntent is nil

PS: tested under Delphi Community Edition 10.4.2 and Android Oreo 8.0 and 8.1

What I did:

  1. Create and save a unit for the BroadcastReceiver (I call it BCast.pas) from the 2nd answer from this link How read JPendingIntent in Delphi?

  2. In your main.pas (assuming it is save as main.pas), add the following:

  • ADD TO PROJECT the unit created in step 1 (in my case bcast.pas)

  • add BCAST in uses clause

  • add variable for the TBroadCastReceiver (ie fbroadcast : Tbroadcastreceiver;)

  • make an OnReceiveBroadcast procedure in my case:

    private
       { Private declarations }
       procedure OnReceiveBroadcast(aContext: JContext; aIntent: JIntent; aResultCode: integer);
    
  • then under implementation, create the code for the OnReceiveBroadcast:

      procedure Tmain.OnReceiveBroadcast(aContext: JContext; aIntent: JIntent; aResultCode: integer);
      begin
          // ... put in the codes here for the result of the last sms send. //
          // ... use the the value of AResultcode to do the checking //
          // ... AResultcode=1 means ok, other number means possible error //
      end;
    
  • initialize the fbroadcast as follows: (i put them under my main's onshow):

      fbroadcast := TBroadcastReceiver.Create(OnReceiveBroadcast);
      fbroadcast.addactions([StringToJString('xxx')]);
    

    where xxx is any string that will be use to match the intent's action

  1. Add in a procedure to sendsms:

     procedure tmain.SendSMSAPI(target,messagestr:string);
     var
       smsManager: JSmsManager; // Androidapi.JNI.Telephony //
       smsTo     : JString;     // Androidapi.JNI.JavaTypes //
       PIntent   : JPendingIntent; // Androidapi.JNI.App //
       smsarray  : jarraylist;
       APIntent  : JArraylist;
       intent    : JIntent;
     begin
       Intent    := TJIntent.Create;
       Intent.setAction(StringToJString('xxx'));
       PIntent   := TJPendingIntent.JavaClass.getBroadcast(TAndroidHelper.Context, 0, Intent, 0);
       APIntent  := Tjarraylist.create;
       APIntent.add(PIntent);
       smsManager:= TJSmsManager.JavaClass.getDefault;
       smsTo     := StringToJString(target); // Androidapi.Helpers //
       smsarray  := smsmanager.divideMessage(stringtojstring(messagestr));
       smsManager.sendmultiparttextMessage(smsTo, nil,smsarray, APIntent, nil); 
     end;
    

Upvotes: 3

Related Questions