2

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.

Community
  • 1
  • 1
That Marc
  • 1,134
  • 3
  • 19
  • 42
  • You've posted absolutely no code, so it's impossible for us to tell you why you're getting the results you are getting. Are you expecting us to read your mind or see your screen from where we are? – Ken White Jan 14 '14 at 03:44
  • No, but I am, however, expecting that I will get only replies and answers from people, who already know what I'm talking about and what's the code case. Because I believe that from my question there is clerly stated that I know what I'm doing (for this particular case, otherwise I'm just a complete beginner, that's for sure!!), and that it's not the problem in my or any specific code, but in the values themselves. I don't get any read errors, the "date_sent" parameter does exist in android's database, i do get a value, but it's always 0, which means that android itself fills the database – That Marc Jan 14 '14 at 05:05
  • with the default value, which is 0. So, once again, I don't think that the code is a necesarry thing here, since it's been posted many times all around, and I never intendet do make just another duplicate and get the reply "you're doing it right. If that's what you get, then that's what it is in the database. No help." I already know that. My question was WHY is this happening, and HOW to get the correct values, which, clearly are NOT in the "date_sent" parameter, so the question was also (as my second paragraph provides), are there any OTHER queries that are known, and is there any new – That Marc Jan 14 '14 at 05:09
  • documentaton available, that I wasn't aware of and didn't manage to find? From these facts, I'm truly dissapointed and politely ask you to undo the down vote for the question, because I really think and believe that it was more than correct and important. Also, because I believe that the last thing I deserve, after spending whole 2 hours now in investigating this on the phone's stock database itself, and also FINDING THE SOLUTION which is NOT available anywhere else on the internet (correct me if I'm wrong!), is to get a downvote, and lose another 6 points of my "reputation" on this usefull – That Marc Jan 14 '14 at 05:11
  • but yet really silly patented forum, since I cannot upvote any good question, but more importantly, can't comment on any posts outside mines, so that I could maybe tell people something I know, or ask something I don't understand, but rather have to make duplicate questions and then getting comments like "this was posted before" or "it's a silly question". So really, I beg you to rather upvote this question since it IS important, and since I also found the solution that surely will come handy to others as well, so that I can get my 50 reputation asap and be able to make comments. Thank you! – That Marc Jan 14 '14 at 05:14
  • It would be nice to see your solution – Stephane Wierzbicki Jan 14 '14 at 07:36
  • 1
    Will post as answer right after I test some other new findings as well, so that I post the whole thing together. – That Marc Jan 14 '14 at 07:40
  • I understand your frustration about vote count, but your vote count will increase faster with code examples, even when it is true that only very the knowledgeable know what you're talking about. Do not underestimate the curiosity of some people who will try your code just for the fun of it and find some useful answers. – Arnold Jan 18 '14 at 12:39
  • Ok, that is true story indeed. My bad, I guess, even though I thought that pasting the same code as it is already on quite several similar questions would be nonsense. I guess I should post the code after all... – That Marc Jan 18 '14 at 15:26

1 Answers1

4

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...

That Marc
  • 1,134
  • 3
  • 19
  • 42
  • 1
    +1 for your work and sharing it and it would be even better if you shared some example code. – Arnold Jan 18 '14 at 12:40
  • Great work! And thanks for the link to Lars D. You might be interested in an article of Brian Long in which he describes how to implement BroadcastReceivers (http://blog.blong.com/2013/11/delphi-and-android-services-part-2.html). You'll need that if you want to receive SMS messages (I know, it's another topic, but sending leads to receiving :-) – Arnold Jan 18 '14 at 19:37
  • I've seen this before, yes. But I'd only need that to implement an automatic backup option, but for right now my goal is to have manual only. Even at a latter time, I'd build a service only to wake the main app every while, since I don't like to keep heavy services up all the time. :) Right now my biggest problems are some java to delphi translations, to get what I want.. :/ Sms retrieving, saving to file, and possibility to upload to a ftp is already done, so "alpha" version in 10 days is quite a success for me so far :) Wish to have some direct contact to some java/delphi expert though. – That Marc Jan 18 '14 at 20:29