Sunday, October 27, 2013

How to fetch SMS messages from the Android inbox using Delphi

The code speaks for itself:

uses
  SysUtils,
  FMX.Helpers.Android,
  Androidapi.JNI.GraphicsContentViewText,
  Androidapi.JNI.Net,
  Androidapi.JNI.JavaTypes,
  Androidapi.JNI.Telephony;

function FetchSms:string;
var
  cursor: JCursor;
  uri: Jnet_Uri;
  address,person,msgdatesent,protocol,msgread,msgstatus,msgtype,
  msgreplypathpresent,subject,body,
  servicecenter,locked:string;
  msgunixtimestampms:int64;
  addressidx,personidx,msgdateidx,msgdatesentidx,protocolidx,msgreadidx,
  msgstatusidx,msgtypeidx,msgreplypathpresentidx,subjectidx,bodyidx,
  servicecenteridx,lockedidx:integer;
begin
  uri:=StrToJURI('content://sms/inbox');
  cursor := SharedActivity.getContentResolver.query(uri, nil, nil,nil,nil);
  addressidx:=cursor.getColumnIndex(StringToJstring('address'));
  personidx:=cursor.getColumnIndex(StringToJstring('person'));
  msgdateidx:=cursor.getColumnIndex(StringToJstring('date'));
  msgdatesentidx:=cursor.getColumnIndex(StringToJstring('date_sent'));
  protocolidx:=cursor.getColumnIndex(StringToJstring('protocol'));
  msgreadidx:=cursor.getColumnIndex(StringToJstring('read'));
  msgstatusidx:=cursor.getColumnIndex(StringToJstring('status'));
  msgtypeidx:=cursor.getColumnIndex(StringToJstring('type'));
  msgreplypathpresentidx:=cursor.getColumnIndex(StringToJstring('reply_path_present'));
  subjectidx:=cursor.getColumnIndex(StringToJstring('subject'));
  bodyidx:=cursor.getColumnIndex(StringToJstring('body'));
  servicecenteridx:=cursor.getColumnIndex(StringToJstring('service_center'));
  lockedidx:=cursor.getColumnIndex(StringToJstring('locked'));
  while (cursor.moveToNext) do begin
    address:=JStringToString(cursor.getString(addressidx));
    person:=JStringToString(cursor.getString(personidx));
    msgunixtimestampms:=cursor.getLong(msgdateidx);
    msgdatesent:=JStringToString(cursor.getString(msgdatesentidx));
    protocol:=JStringToString(cursor.getString(protocolidx));
    msgread:=JStringToString(cursor.getString(msgreadidx));
    msgstatus:=JStringToString(cursor.getString(msgstatusidx));
    msgtype:=JStringToString(cursor.getString(msgtypeidx));
    msgreplypathpresent:=JStringToString(cursor.getString(msgreplypathpresentidx));
    subject:=JStringToString(cursor.getString(subjectidx));
    body:=JStringToString(cursor.getString(bodyidx));
    servicecenter:=JStringToString(cursor.getString(servicecenteridx));
    locked:=JStringToString(cursor.getString(lockedidx));
    Result:=IntToStr(trunc(msgunixtimestampms/1000))+' '+address+' '+body;
  end;
end;

9 comments:

  1. Thanks, this code was really helpful for me.
    Now I'm trying to understand how to get an event triggered
    when a sms arrives so that I don't have to check the sms
    inbox continously by means of a timer.
    Any ideas about that?
    /Ole

    ReplyDelete
  2. In the Android SDK, that is supported using services. I have not heard, that it is possible to implement that with Delphi XE5, we will probably have to wait for Delphi XE6 to do that. XE5 is Embarcadero's first venture into Android land, and while it makes GUI app development significantly easier, it does not support everything.

    ReplyDelete
  3. Lard, is it possible to access the contact directory of android phone with XE5 ?

    ReplyDelete
  4. I'd also like to know if there's any way to get the contacts from XE5 android app?
    I'm trying to make a sms/mms backup app, along with contact names.

    So, to add one more questions: this code reads smses. Can you please provide sample for reading mms-es as well? (with attachments or without them, text-only would also be a way...)

    THANKS! :)

    ReplyDelete
  5. +, why cursor.getString(msgdatesentidx)); always returns 0, either for sent or received messages?

    ReplyDelete
  6. If you want to get the correct delivery and sent dates in your query, you need to use "date2" instead of sent_date -> for inbox!

    However, for outbox, for which you keep the exact same code as above, only change the path from /sms/inbox to /sms/sent you must use "report_date" instead of "sent_date".
    So, this little modification, and you'll have all the data you need :)

    There's also SO topic on this one:
    http://stackoverflow.com/questions/21104999/sms-messages-date-sent-on-android-with-delphi-xe5-always-returns-0

    ReplyDelete
  7. How can I use the result of this function? Can I put the 'fetchsms' in one timer?
    Like this:
    begin
    memo1.lines.add(fetchsms);
    end;

    If not, show me the way please.

    ReplyDelete
  8. Hi, Lars,
    and thanks for this very useful code !!
    However when i call this function with this little bit of code, i get only the very first sms on the android device.

    procedure TForm1.Button2Click(Sender: TObject);
    begin
    Memo1.Text := FetchSms;
    end;
    Can you tell me how to get to the other end of the table containing the sms's and maybe how to select a sender phone number.
    I suspect that this latter one is done with the line
    getContentResolver.query(uri, nil, nil,nil,nil); , but i dont quite get it.
    If you could help me with theese 2 problems i would be grateful.
    Thanks in advance,
    Christian

    ReplyDelete
  9. How do I delete a sms after I read it

    ReplyDelete