Search code examples
androiddelphidelphi-xe5

SMS messages date_sent on Android with Delphi XE5 always returns 0?


Does anyone know why I always get date_sent parameter as "0", instead of the correct info when the message was sent? (I get 0 for either sent or received messages, but as far as I remember, both, sent and received, have separate time in stock app, so there surely is that info available, just don't understand why I can't get it. The query used for this is "date_sent"... It's suggested on many sites, but always returns zero.

Are there any newer info about android api accessing messages? I found several info, but all are quite old, and says that messaging is not documented. Nothing changed ever since? Is this https://stackoverflow.com/a/6446831/1786516 all that there is about mms, or is there any delphi xe5 example already made?

UPDATE:

Here's the origin example code for reading smses in Delphi XE5, as provided at several pages:

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

function ReadSMSInbox:string;
var
  cursor: JCursor;
  uri: Jnet_Uri;
  address,person,msgdatesent,protocol,msgread,msgstatus,msgtype,
  msgreplypathpresent,subject,body,
  servicecenter,locked:string;
  msgunixtimestampms:int64;
begin
  uri:=StrToJURI('content://sms/inbox');
  cursor := SharedActivity.getContentResolver.query(uri, nil, nil,nil,nil);

  while (cursor.moveToNext) do begin
    address:=JStringToString(cursor.getString(cursor.getColumnIndex(StringToJstring('address'))));
    person:=JStringToString(cursor.getString(cursor.getColumnIndex(StringToJstring('person'))));
    msgunixtimestampms:=cursor.getLong(cursor.getColumnIndex(StringToJstring('date')));
    msgdatesent:=JStringToString(cursor.getString(cursor.getColumnIndex(StringToJstring('date_sent'))));
    protocol:=JStringToString(cursor.getString(cursor.getColumnIndex(StringToJstring('protocol'))));
    msgread:=JStringToString(cursor.getString(cursor.getColumnIndex(StringToJstring('read'))));
    msgstatus:=JStringToString(cursor.getString(cursor.getColumnIndex(StringToJstring('status'))));
    msgtype:=JStringToString(cursor.getString(cursor.getColumnIndex(StringToJstring('type'))));
    msgreplypathpresent:=JStringToString(cursor.getString(cursor.getColumnIndex(StringToJstring('reply_path_present'))));
    subject:=JStringToString(cursor.getString(cursor.getColumnIndex(StringToJstring('subject'))));
    body:=JStringToString(cursor.getString(cursor.getColumnIndex(StringToJstring('body'))));
    servicecenter:=JStringToString(cursor.getString(cursor.getColumnIndex(StringToJstring('service_center'))));
    locked:=JStringToString(cursor.getString(cursor.getColumnIndex(StringToJstring('locked'))));
    Result:=IntToStr(trunc(msgunixtimestampms/1000))+' '+address+' '+body; //+whatever you want to add here;
StringList1.Add(Result);   //Add the result in stringlist to collect all of them
  end;
end;

There is completely the same code for reading Outbox (Sent) sms messages, except that the function name is ReadSMSOutbox, and the uri string is uri:=StrToJURI('content://sms/sent');

See the answer for modified/optimized code, and solution about date_sent corrected queries.


Solution

  • So, after spending a few hours on the internet not finding anything interesting, I started to foll around my phone, and expanding the database file for messages, smsmms.db, in which I found the table /sms, which I'm clearly accessing for retrieving messages.

    The table has, along with all columns that are already "known" for message reading, a few more, for me the interesting ones were about dates. So therefore I gave it a try with using query for "date2" column, and "report_date".

    So, instead of using date_sent which clearly doesn't retrieve anything at all, you should use date2 for inbox (received messages, type = 1), while for outbox messages (sent, type=2) you should use "report_date". This seems to retrieve just what it should, the correct date when the received message was originally sent, and when sent message was delivered.

    The date_sent therefore can be excluded from query, although it exists.

    I'm not entirely sure whether this applies to all android versions, but it confirmatively does for all 4.x up (+).

    Will check on 2.2 and 2.3 in next few days and update my answer with confirmation.

    Also, the market application "Sms Backup&Restore", which is rather popular for doing sms backups (more or less readable xml format makes it beautiful for a difference of other apps), seems to use date_sent as well, instead of date2 and report_date.

    I'm not entirely sure if it's even possible to use when querying for both, inbox and outbox messages at once, without querying both columns for both message types, which gives you an extra parameter to be read; I'm using inbox ant outbox queryes in separate functions, so I use separated the dates as well.

    I hope this helps to anyone, since it hasn'b been covered anywhere.

    UPDATE:

    Here's the example code, optimized for speed, as Lars D on his website provided

    (see the section with addressidx and other integer values; his code provides column indexes in front, to have as fixed data, since that way the query already knows which column to read every time the cursor goes to "next", instead of getting the column index separately each time of the cursor query, as suggested by lots of java examples; This solution should be a little faster to execute!)

    uses
      SysUtils,
      FMX.Helpers.Android,
      Androidapi.JNI.GraphicsContentViewText,
      Androidapi.JNI.Net,
      Androidapi.JNI.JavaTypes,
      Androidapi.JNI.Telephony;
    
    function ReadSMSInbox:string;
    // function ReadSMSOutbox:string;  // for reading outbox
    var
      cursor: JCursor;
      uri: Jnet_Uri;
      address,person,msgdatesent,protocol,msgread,msgstatus,msgtype,
      msgreplypathpresent,subject,body,
      servicecenter,locked                       :string;
      msgunixtimestampms                         :int64;
    // here are new integer variables
      addressidx,personidx,msgdateidx,msgdatesentidx,protocolidx,msgreadidx,
      msgstatusidx,msgtypeidx,msgreplypathpresentidx,subjectidx,bodyidx,
      servicecenteridx,lockedidx                 :integer;
    begin
      uri:=StrToJURI('content://sms/inbox');
    // uri:=StrToJURI('content://sms/sent');   // use this for outbox messages!
    
      cursor := SharedActivity.getContentResolver.query(uri, nil, nil,nil,nil);
    // here we get column indexes in advance !!!
    
      addressidx:=cursor.getColumnIndex(StringToJstring('address'));
      personidx:=cursor.getColumnIndex(StringToJstring('person'));
      msgdateidx:=cursor.getColumnIndex(StringToJstring('date'));  //this is actual date, always the same; you may need/use this as main date;
    
      msgdatesentidx:=cursor.getColumnIndex(StringToJstring('date2')); //this is the actual sent date/time, note this different column used!!!
    
       //msgdatesentidx:=cursor.getColumnIndex(StringToJstring('report_date')); 
       //this is the actual delivery date/time, (when your message was actually delivered to the recepient!
       // Note this different column used as well!!!
    
    
      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'));
    
    // now the query
    
      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; //+whatever you want to add here;
    StringList1.Add(Result);   //Add the result in stringlist to collect all of them
    
      end;
    end;
    

    Again, for getting Outbox (Sent) messages, you can use the same function, only rename it to something like ReadSMSOutbox, + you need to change the uri to uri:=StrToJURI('content://sms/sent');, as is provided inside of the code above as the comment, however the date2 here is then NOT ok. For outbox, you should use report_date for getting the correct column index, and you have it in the comment of the code as well.

    Tested on 2.2, 2.3, 4.0, 4.2 and 4.3 and it works correctly in all of these android versions. Seems like it's the right choice. :)

    Good luck with your apps! :)

    For MMS fetching code, post a comment if u need a code. It's a whole different story...