0

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.

jwan
  • 41
  • 5
  • You need to use the sentIntents parameter in your call to sendMultipartTextMessage. As per [the docs](https://developer.android.com/reference/android/telephony/SmsManager#sendMultipartTextMessage(java.lang.String,%20java.lang.String,%20java.util.ArrayList%3Cjava.lang.String%3E,%20java.util.ArrayList%3Candroid.app.PendingIntent%3E,%20java.util.ArrayList%3Candroid.app.PendingIntent%3E)), when the message is sent, the intent(s) will be broadcast, so you need to set up a BroadcastReceiver to intercept it/them and check the result – Dave Nottage Oct 13 '21 at 03:17
  • Further, there is a [Java example here](https://stackoverflow.com/a/19084559/3164070) that you could learn from. It uses a service to send the message however you just need to focus on the sentIntents part. You may or may not be interested in the deliveredIntents part. – Dave Nottage Oct 13 '21 at 03:20
  • Although Im not good at java, im trying to relate it to its Delphi equivalent. However, I would like to ask... assuming Im able to figure out the "sentintent" part, would this mean that whatever sms my app send out, there would be a copy of the sms (recipient and msg body) in the phone's default sms app? – jwan Oct 13 '21 at 08:50
  • Not sure about the second part (i.e. retrieving the sent message details using SmsManager), however it should answer part 2 of your question. I'll look into part 1 when I have time – Dave Nottage Oct 13 '21 at 23:32
  • @dave-nottage, following the second answer from this [link](https://stackoverflow.com/questions/34898950/how-read-jpendingintent-in-delphi ) I am able to get the "status" of the send sms if sent or failed. However, the example uses sendTextMessage which limit the length of the sms, When I use back sendmultiparttextMessage, I got error that says I need to use a JArrayList for my sentintent part. May I know how to convert a JPendingIntent into a JArrayList type? – jwan Oct 16 '21 at 00:09
  • Please see: https://gist.github.com/DelphiWorlds/1a7534be4f765ab2acd4d780c5b54550 – Dave Nottage Oct 16 '21 at 00:47
  • 1
    The issue on my send sms not showing up in phone's sms app is kind of a bug of Huawei - android 8.0 similar with this post https://stackoverflow.com/questions/51537810/oreo-8-0-sendtextmessage-is-not-saving-the-message-to-the-sent-folder I tried on another phone with Oreo 8.1, and there is no problem. – jwan Oct 16 '21 at 14:24

1 Answers1

2

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;
    
jwan
  • 41
  • 5