I am working on an sms app.By using sms content provider I got all the fields.
Uri uriSms = Uri.parse("content://sms/inbox");
Cursor c = context.getContentResolver().query(uriSms, null,null,null,null);
_id
thread_id
address
person
date
read
status
type
subject
body
locked
I could able to do all basic operations using the above fields.Now I want to make a sms locked state. How can I do that?.From status field I am always getting -1.What that means.I checked with both inbox and outbox.Please help me friends
Sms.CONTENT_URI= Uri.parse("content://sms");
Mms.CONTENT_URI = Uri.parse("content://mms");
private void lockMessage(MessageItem msgItem, boolean locked) {
Uri uri;
if ("sms".equals(msgItem.mType)) {
uri = Sms.CONTENT_URI;
} else {
uri = Mms.CONTENT_URI;
}
final Uri lockUri = ContentUris.withAppendedId(uri, msgItem.mMsgId);
final ContentValues values = new ContentValues(1);
values.put("locked", locked ? 1 : 0);
new Thread(new Runnable() {
public void run() {
getContentResolver().update(lockUri,
values, null, null);
}
}).start();
}
Just reminder, everything above is not include in SDK so carefull in usage.
Related
I am working on android application (as a final year project in my university) which backup and restores contacts, messages, calendar, call logs etc. I started working on it yesterday and until now I am able to get the message conversations through ContentResolver like this:
private void MsgBackup(){
try {
Uri uri = Uri.parse("content://sms");
//Uri uri = Uri.parse("content://mms-sms/conversations/");
ContentResolver contentResolver = getContentResolver();
final String[] projection = new String[]{"*"};
Cursor SMSL = contentResolver.query(uri, projection, null, null, null);
int msgscount = SMSL.getCount();
msgs = new String[SMSL.getCount()][2];
int i = 0;
while (SMSL.moveToNext()) {
address = SMSL.getString(SMSL.getColumnIndex("address"));
body = SMSL.getString(SMSL.getColumnIndex("body"));
msgs[i][0] = address;
msgs[i][1] = body;
i++;
}
SMSL.close();
//will do something here to backups msgs array
}catch (Exception e){
e.printStackTrace();
Toast.makeText(getApplicationContext(),"Error Fetching messages, check permissions!",Toast.LENGTH_LONG).show();
}
}
What I am worried about is "Restoring them". I read somewhere that it is not possible to restore messages without root access. If so, then how come the applications in the market do this job without root access?
I am using the following code to insert a draft into content://sms/draft
ContentValues values = new ContentValues();
values.put("address", receiver2);
values.put("body", body2);
values.put("date", String.valueOf(System.currentTimeMillis()));
values.put("type", "3");
values.put("thread_id", thread_id);
getContentResolver().insert(Uri.parse("content://sms/draft"), values);
thread_id is 0 if there wasn't any conversation with the address above, else it's the id of that thread.
When I run this code, the draft is indeed saved, but thread in the native sms client (stock android 4.0.3) isn't updated as "draft" [I can see the draft message body, but there is no "Draft" label on it. I have to open-close the thread, in order to be marked as marked]. I have read somewhere that there is an issue with the thread not updating properly. How can I force the threads to be updated so it shows ok in all the clients?
EDIT:
Having read your answers, I have updated my code a bit, but the problem remains. I have added a screenshot below, since when I wrote my question I was in a hurry and couldn't write it clearly enough.
protected void save_draft(String[] recipients, String body) {
Uri threadIdUri = Uri.parse("content://mms-sms/threadID");
Uri.Builder builder = threadIdUri.buildUpon();
for (String recipient : recipients) {
builder.appendQueryParameter("recipient", recipient);
}
Uri uri = builder.build();
Long thread_id = get_thread_id(uri);
Log.d("thread_id", thread_id + " ");
ContentValues values = new ContentValues();
values.put("body", body);
values.put("date", String.valueOf(System.currentTimeMillis()));
values.put("type", 3);
values.put("thread_id", thread_id);
getContentResolver().insert(Uri.parse("content://sms/draft"), values);
//^tried "content://sms/" as well, but got the same result
}
private Long get_thread_id(Uri uri) {
long threadId = 0;
Cursor cursor = getContentResolver().query(uri, new String[] { "_id" },
null, null, null);
if (cursor != null) {
try {
if (cursor.moveToFirst()) {
threadId = cursor.getLong(0);
}
} finally {
cursor.close();
}
}
return threadId;
}
As you can see, there is no "Draft" label, next to the draft I made via the code above.
It's been a while since I made this question, but here is the answer:
First of all as stated before, the fact that the "Draft" hint doesn't appear on the Native SMS app, shouldn't be bothering anyone. Nothing can be done about it, and it's just the way the Native SMS app works. In particular a cache is initialised when the app starts, saving the thread ids of the threads that contain a draft. The draft cache is updated only from the app itself and not from an actual change in the sms table
For the saving draft part here is the piece of code to save a draft properly:
public static final Uri CONTENT_URI =
Uri.parse("content://sms/draft");
public static Uri addDraft(ContentResolver resolver,
String address, String body, String subject,
Long date, long threadId) {
ContentValues values = new ContentValues(6);
values.put(ADDRESS, address);
if (date != null) {
values.put(DATE, date);
}
values.put(READ, Integer.valueOf(1));
values.put(SUBJECT, subject);
values.put(BODY, body);
if (threadId != -1L) {
values.put(THREAD_ID, threadId);
}
return resolver.insert(CONTENT_URI , values);
}
Note: Draft messages may or may not contain the address of the recipient of the message. Drafts are saved on the thread (a thread can contain many recipients)
Although the sms database is not documented at all, you can grab the Telephony class from the AOSP and have a look at how to add/remove messages and handle various tasks about sms and mms.
http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.2.2_r1/android/provider/Telephony.java
I think your approach is different from built-in messaging application.
thread_id is 0 if there wasn't any conversation with the address above, else it's the id of that thread.
As far as i know even draft got auto generated thread_id. If it's not, all draft (had receiver address never appeared) will group at the same conversation with thread_id = 0
Here is how built-in app add an draft.
public static final Uri SmsCONTENT_URI =
Uri.parse("content://sms");
ContentValues values = new ContentValues(3);
values.put("thread_id", threadId);
values.put("body", contents); //
values.put("type", Sms.MESSAGE_TYPE_DRAFT); // type = 3 is draft.
SqliteWrapper.insert(mActivity, mContentResolver, Sms.CONTENT_URI, values);
Final reminder : This is not public API to access message data so I don't suggest you to use. But now is the only way.
Use the method described in this answer , just insert to content://sms/draft instead of content://sms/sent.
thanks a lot i have try save_draft() try this and insert into inbox/sent/draft etc....
public class AddData {
Activity act;
Context ctx,context;
ContentResolver cr;
public AddData(Activity act)
{
cr = act.getContentResolver();
this.act = act;
}
public void addsms(String address,String body,String date,String type,String read)
{
String[] addr = address.split(" ");
String thread_id = save_draft(addr);
ContentValues values = new ContentValues();
values.put("body", body);
values.put("date", date);
values.put("type", type);
if(type.equals("3"))
{
values.put("thread_id", thread_id);
}else
{
values.put("address", address);
}
Uri uri = cr.insert(Uri.parse("content://sms/"), values);
cr.notifyChange(uri, null);
}
protected String save_draft(String[] recipients) {
Uri threadIdUri = Uri.parse("content://mms-sms/threadID");
Uri.Builder builder = threadIdUri.buildUpon();
for (String recipient : recipients) {
builder.appendQueryParameter("recipient", recipient);
}
Uri uri = builder.build();
String thread_id = get_thread_id(uri).toString();
Log.d("thread_id", thread_id + " ");
//^tried "content://sms/" as well, but got the same result
return thread_id;
}
private Long get_thread_id(Uri uri) {
long threadId = 0;
Cursor cursor = act.getContentResolver().query(uri, new String[] { "_id" },
null, null, null);
if (cursor != null) {
try {
if (cursor.moveToFirst()) {
threadId = cursor.getLong(0);
}
} finally {
cursor.close();
}
}
return threadId;
}
}
I am trying to create an app which simply offers an edittext and imagebutton. If the butten gets clicked, the idea is that an album is added to the Playlist, named in the edittext box. Albums should be selected randomly. Goes without saying that the album tracks should be in the correct order.
I can add more functionality later eg. save, overwrite, delete etc.
I have the interface but am struggling with the code. I sort of get the concept of ContentProviders.
so the code needs to:
access the Playlists, which I believe is achieved by using MediaStore.Audio.Playlists
access the Albums, which I believe is achieved by using MediaStore.Audio.Albums
add to the Playlist
I have the following code (most bits obtained from this site. Thanks btw) to access the Playlist but it crashes with a Null Exception error.
public void checkforplaylists()
{
//Get a cursor over all playlists.
final ContentResolver resolver= MediaProvider.mContentResolver;
final Uri uri=MediaStore.Audio.Playlists.INTERNAL_CONTENT_URI;
final String id=MediaStore.Audio.Playlists._ID;
final String name=MediaStore.Audio.Playlists.NAME;
final String[]columns={id,name};
final Cursor playlists= resolver.query(uri, columns, null, null, null);
if(playlists==null)
{
Log.e(TAG,"Found no playlists.");
return;
}
return;
}
Anyone who can help?
I think you mean NullPointerException, which means one of your assignments is null and then you try to access a member of the object you intended it to be. Most likely it is resolver, but to be sure you need the line number reported and/or to step through that with a debugger.
This works. When using the ContentResolver, the Context (this) is required.
public void checkforplaylists(Context context)
{
ContentResolver cr = context.getContentResolver();
final Uri uri=MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI;
final String id=MediaStore.Audio.Playlists._ID;
final String name=MediaStore.Audio.Playlists.NAME;
final String[]columns={id,name};
final Cursor playlists= cr.query(uri, columns, null, null, null);
if(playlists==null)
{
Log.e(TAG,"Found no playlists.");
return;
}
Log.e(TAG,"Found playlists.");
return;
}
use this code, will work
public boolean addPlaylist(String pname) {
Uri playlists = MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI;
Cursor c = resolver.query(playlists, new String[] { "*" }, null, null,
null);
long playlistId = 0;
c.moveToFirst();
do {
String plname = c.getString(c
.getColumnIndex(MediaStore.Audio.Playlists.NAME));
if (plname.equalsIgnoreCase(pname)) {
playlistId = c.getLong(c
.getColumnIndex(MediaStore.Audio.Playlists._ID));
break;
}
} while (c.moveToNext());
c.close();
if (playlistId != 0) {
Uri deleteUri = ContentUris.withAppendedId(playlists, playlistId);
Log.d(TAG, "REMOVING Existing Playlist: " + playlistId);
// delete the playlist
resolver.delete(deleteUri, null, null);
}
Log.d(TAG, "CREATING PLAYLIST: " + pname);
ContentValues v1 = new ContentValues();
v1.put(MediaStore.Audio.Playlists.NAME, pname);
v1.put(MediaStore.Audio.Playlists.DATE_MODIFIED,
System.currentTimeMillis());
Uri newpl = resolver.insert(playlists, v1);
Log.d(TAG, "Added PlayLIst: " + newpl);
flag=true;
return flag;
}
I've spent 5 days trying out different things and lots of googling with no luck
I have a broadcast receiver to monitor and backup incoming mms & sms.
The sms - outgoing and incoming is easy no problem. MMS however...
I have a broadcast receiver for incoming MMS, no problem there.
For outgoing MMS however, I use a content observer directed towards content://mms
heres the part registering the content observer from the service class
mo = new MMSObserver(new Handler(),getApplicationContext());
try{
getContentResolver().unregisterContentObserver(mo);
}
finally{
getContentResolver().registerContentObserver(Uri.parse("content://mms"), true, mo);
}
This is the onchange part in the above content observer
public void onChange(boolean bSelfChange)
{
super.onChange(bSelfChange);
Log.i(TAG,"MMSObserver onChange");
ContentResolver contentResolver = context.getContentResolver();
Uri uri = Uri.parse("content://mms");
Cursor cur = contentResolver.quert("content://mms",null,null,null,null)
if(cur.moveToNext()){
String id = cur.getString(cur.getColumnIndex("_id"));
String date = cur.getString (cur.getColumnIndex ("date"));
String address = getAddress(id);
}
}
private static String getAddress(String id){
String selectionAdd = new String("msg_id=" + id);
String uriStr = MessageFormat.format("content://mms/{0}/addr", id);
Uri uriAddress = Uri.parse(uriStr);
Cursor cAdd = context.getContentResolver().query(uriAddress, null,
selectionAdd, null, null);
String name = null;
if (cAdd.moveToFirst()) {
do {
String number = cAdd.getString(cAdd.getColumnIndex("address"));
if (number != null) {
try {
Long.parseLong(number.replace("-", ""));
name = number;
} catch (NumberFormatException nfe) {
if (name == null) {
name = number;
}
}
}
} while (cAdd.moveToNext());
}
if (cAdd != null) {
cAdd.close();
}
return name;
}
The problem is the address column always returns "insert-address-token" for outgoing mms.
Is there any possible way to get the number the mms is going to?
Also I noticed that the content observer is triggered when the message is in draft form not when it is sent or pending. since depending on those uris is generally a bad idea since they're not part of the sdk, i switched to a different method. cataloging all sms & mms messages and storing their _id columns, and just syncing them with the backup. However my problem still remains.
MMS address column is always "insert-address-token"
Any suggestions?
You may want to try something like this:
Uri uri = Uri.parse("content://mms-sms/conversations/" + mThreadId);
String[] projection = new String[] {
"body", "person", "sub", "subject", "retr_st", "type", "date", "ct_cls", "sub_cs", "_id", "read", "ct_l", "st", "msg_box", "reply_path_present", "m_cls", "read_status", "ct_t", "status", "retr_txt_cs", "d_rpt", "error_code", "m_id", "date_sent", "m_type", "v", "exp", "pri", "service_center", "address", "rr", "rpt_a", "resp_txt", "locked", "resp_st", "m_size"
};
String sortOrder = "normalized_date";
Cursor mCursor = getActivity().getContentResolver().query(uri, projection, null, null, sortOrder);
String messageAddress;
int type;
while (mCursor.moveToNext()) {
String messageId = mCursor.getString(mCursor.getColumnIndex("_id"));
Uri.Builder builder = Uri.parse("content://mms").buildUpon();
builder.appendPath(messageId).appendPath("addr");
Cursor c = mContext.getContentResolver().query(builder.build(), new String[] {
"*"
}, null, null, null);
while (c.moveToNext()) {
messageAddress = c.getString(c.getColumnIndex("address"));
if (!messageAddress.equals("insert-address-token")) {
type = c.getInt(c.getColumnIndex("type"));
c.moveToLast();
}
}
c.close();
}
I am not actually calling the mCursor's moveToNext() method in my code but instead I am implementing this logic in the getView() method of a SimpleCursorAdapter.
I currently register a content observer on the following URI "content://sms/" to listen out for incoming and outgoing messages being sent.
This seems to work ok and I have also tried deleting from the sms database but I can only delete an entire thread from the following URI "content://sms/conversations/"
Here is the code I use for that
String url = "content://sms/";
Uri uri = Uri.parse(url);
getContentResolver().registerContentObserver(uri, true, new MyContentObserver(handler));
}
class MyContentObserver extends ContentObserver {
public MyContentObserver(Handler handler) {
super(handler);
}
#Override public boolean deliverSelfNotifications() {
return false;
}
#Override public void onChange(boolean arg0) {
super.onChange(arg0);
Log.v("SMS", "Notification on SMS observer");
Message msg = new Message();
msg.obj = "xxxxxxxxxx";
handler.sendMessage(msg);
Uri uriSMSURI = Uri.parse("content://sms/");
Cursor cur = getContentResolver().query(uriSMSURI, null, null,
null, null);
cur.moveToNext();
String protocol = cur.getString(cur.getColumnIndex("protocol"));
if(protocol == null){
Log.d("SMS", "SMS SEND");
int threadId = cur.getInt(cur.getColumnIndex("thread_id"));
Log.d("SMS", "SMS SEND ID = " + threadId);
Cursor c = getContentResolver().query(Uri.parse("content://sms/outbox/" + threadId), null, null,
null, null);
c.moveToNext();
int p = cur.getInt(cur.getColumnIndex("person"));
Log.d("SMS", "SMS SEND person= " + p);
//getContentResolver().delete(Uri.parse("content://sms/conversations/" + threadId), null, null);
}
else{
Log.d("SMS", "SMS RECIEVE");
int threadIdIn = cur.getInt(cur.getColumnIndex("thread_id"));
getContentResolver().delete(Uri.parse("content://sms/conversations/" + threadIdIn), null, null);
}
}
}
However I want to be able to get the recipricant and the message text from the SMS Content Provider, can anyone tell me how to do this?
And also how to delete one message instead of an entire thread?
This was already discussed.
To read SMS from the Content provider check:
- android-1-5-reading-sms-messages
Check this threads:
delete-sms-in-android-1-5
how-to-delete-sms-from-inbox-in-android-programmatically
can-we-delete-an-sms-in-android-before-it-reaches-the-inbox
About your comment saying that you are deleting a whole thread instead of a single sms:
Have you tried out this code?
The address column contains the telephone number of the sms sender.
Use cursor.getString(cursor.getColumnIndex("address")) to pull the telephone number as a String. Pop a cursor on content://sms and sort it in date descending order to get the most recent message. You will have to wait for the new message to enter the table otherwise you will pull information from the wrong message. In an incomingSMS broadcastReceiver use a while loop, in a thread, polling for the cursor.getCount() to change. Then after the while loop cursor.moveToFirst will be the new message.
For example:
Cursor cur = getContentResolver().query(Uri.parseUri(content://sms), null, null, null, null);
int count = cur.getCount();
while (cur.getCount() == count)
{
Thread.sleep(1000);
cur = getContentResolver().query(Uri.parseUri(content://sms), null, null, null, null);
}
Then get the address of the sms sender:
cur = getContentResolver().query(Uri.parseUri(content://sms), null, null, null, "date DESC");
cur.MoveToFirst();
String telephoneNumber = cur.getString(cur.getColumnIndex("address");
This while loop will pause the thread until the new message arrives. You can also use a contentObserver, but this while loop is simple and does not require registration, unregistration, and a separate class.
Frankly I think its faster to pull the address and the body of the message directly from the pdu of the incoming intent. This way you dont have to wait for the message to enter the table to get the address and the body. The Android class SmsMessage has a variety of useful methods.