How to mark all text messages as read on Android? - java

I'm trying to mark all text messages as read when user opens my inbox. I've pieced together code from a few tutorials online and ended up with this:
Uri uri = Uri.parse("content://sms/inbox");
Cursor cursor = getContentResolver().query(uri, null, null, null, null);
while (cursor.moveToNext()) {
if ((cursor.getInt(cursor.getColumnIndex("read")) == 0)) {
String SmsMessageId = cursor.getString(cursor.getColumnIndex("_id"));
ContentValues values = new ContentValues();
values.put("read", true);
getContentResolver().update(Uri.parse("content://sms/inbox"), values, "read=0", null);
}
I just want to mark all text messages as read in the onResume() function on this activity. My code may be a pile of crap, like i said it's mashed together from a few places. Corrections to, or alternatives to, my code would be very appreciated. Compiling the code with the sdk for 5.1, testing on 4.4, my app is the default SMS app.

If you want to mark all messages as read, you can do that in one go.
ContentValues values = new ContentValues();
values.put(Telephony.Sms.READ, 1);
getContentResolver().update(Telephony.Sms.Inbox.CONTENT_URI,
values, Telephony.Sms.READ + "=0", null);

Related

How to turn Cursor into CursorLoader?

I have a little problem with SQLite and ContentProviders. First of all here is my current code:
public void loadFavoriteMovies(){
movieList.clear();
Cursor cursor = getContentResolver().query(FavoriteMoviesContract.MovieEntry.CONTENT_URI,
null, null, null, FavoriteMoviesContract.MovieEntry._ID);
if(cursor != null){
while(cursor.moveToNext()) {
MovieResults.Movie movie = new MovieResults.Movie(
cursor.getString(cursor.getColumnIndex(FavoriteMoviesContract.MovieEntry.COLUMN_TITLE)),
cursor.getString(cursor.getColumnIndex(FavoriteMoviesContract.MovieEntry.COLUMN_IMAGE)),
cursor.getString(cursor.getColumnIndex(FavoriteMoviesContract.MovieEntry.COLUMN_BACKDROP)),
cursor.getString(cursor.getColumnIndex(FavoriteMoviesContract.MovieEntry.COLUMN_RELEASE)),
Double.valueOf(cursor.getString(cursor.getColumnIndex(FavoriteMoviesContract.MovieEntry.COLUMN_RATING))),
cursor.getString(cursor.getColumnIndex(FavoriteMoviesContract.MovieEntry.COLUMN_PLOT)),
Integer.valueOf(cursor.getString(cursor.getColumnIndex(FavoriteMoviesContract.MovieEntry.COLUMN_ID)))
);
movieList.add(movie);
}
cursor.close();
}
Log.v("Size of Favorites:", String.valueOf(movieList.size()));
mAdapter.setmMovieList(movieList);
}
What I'm trying to do is I'm reading the SQLite database and putting the values there into a Movie object. But my problem is I want to do this operation using CursorLoader. How can I achieve this?
My main aim for doing this is because Cursor does not automatically update the value when I delete an item from the database and I need to reload the activity which the items are displayed. If anyone can offer an alternative way, that'd also be appreciated.

Telephony.Sms.Inbox.PERSON uses deprecated Contacts.People._ID

Bounty Award - The bounty will be awarded to an answer that gets from a populated Telephony.Sms.Inbox.PERSON value, to the associated Contact using only ContractsContact tables.
I'm reading SMS messages in the standard way in my application:
final String[] projection = {Telephony.Sms.Inbox.BODY,
Telephony.Sms.Inbox.ADDRESS,
Telephony.Sms.Inbox.READ,
Telephony.Sms.Inbox.DATE,
Telephony.Sms.Inbox.PERSON};
final Cursor cursor = ctx.getContentResolver().query(Telephony.Sms.Inbox.CONTENT_URI,
projection, null, null, Telephony.Sms.Inbox.DEFAULT_SORT_ORDER);
When populated, the id returned from the index Telephony.Sms.Inbox.PERSON relates to the id of the deprecated Contacts.People._ID and can be used to query further contact information in the following way:
final String[] projection = {Contacts.People.DISPLAY_NAME};
final String[] selectionArgs = {contactId};
final Cursor cursor = ctx.getContentResolver().query(Contacts.People.CONTENT_URI,
projection, Contacts.People._ID + " = ?", selectionArgs, null);
Why would the relatively new Telephony API use deprecated tables, instead of ContactsContract?
Telephony.Sms.Inbox.PERSON documentation states:
Type: INTEGER (reference to item in content://contacts/people)
I've tried unsuccessfully (but not unsurprisingly?) to find a mapping to the id in any of the ContactsContract id fields, so I'm left having to use deprecated APIs in order to resolve the queries I need to perform quickly.
Such queries include searching for messages by a particular contact, for which I only have the name. The contact could have multiple numbers, which may not be in the correct format to potentially match Telephony.Sms.Inbox.ADDRESS entries.....
The workaround of using Telephony.Sms.Inbox.ADDRESS and ContactsContract.PhoneLookup is not the end of the world when going from the number to the contact, but I still feel I must be missing something here?
Here is the process I'm using to get the messages for 'Joe Bloggs'.
1) Query the ContactsContract table to confirm a contact by the name of Joe Bloggs exists on the device - or get a close match if the contact is actually listed as 'Joe Blogs'.
2) Using the confirmed name, I query the deprecated Contact.People table to get all associated ids for the contact in the following way:
final String selection = Contacts.People.DISPLAY_NAME + " LIKE ?";
final String[] projection = {Contacts.People.DISPLAY_NAME,
Contacts.People._ID};
final String[] selectionArgs = {contactName};
final Cursor cursor = ctx.getContentResolver().query(Contacts.People.CONTENT_URI,
projection, selection, selectionArgs, null);
3) Using the list of deprecated contact ids, I query the message table as so:
final String[] referredArgs = new String[contactIdArray.size()];
for (int i = 0; i < contactIdArray.size(); i++) {
referredArgs[i] = contactIdArray.get(i);
}
final String referredSelection = Telephony.Sms.Inbox.PERSON + " IN "
+ "(" + TextUtils.join(",", referredArgs) + ")";
final String[] projection = {Telephony.Sms.Inbox.BODY,
Telephony.Sms.Inbox.ADDRESS,
Telephony.Sms.Inbox.READ,
Telephony.Sms.Inbox.DATE,
Telephony.Sms.Inbox.PERSON};
final Cursor cursor = ctx.getContentResolver().query(Telephony.Sms.Inbox.CONTENT_URI,
projection, referredSelection, null, Telephony.Sms.Inbox.DEFAULT_SORT_ORDER);
I'm hoping someone will tell me I'm going round the houses here and there is a more obvious solution using current APIs. I don't consider iterating the entire message table using ContactsContract.PhoneLookup an optimised solution.
Thanks in advance.
I wouldn't use the Telephony.Sms.Inbox.PERSON field, and definitely wouldn't query the deprecated People apis if I were you.
The People apis had been deprecated for so long you can't count on all devices our there to properly support it anymore.
First thing you need to understand is that there isn't a one-to-one link between sms and contacts.
An SMS can come from a non-contact phone number, a single contact, multiple contacts, a mixture of contacts and non-contacts, alpha-numeric ids, and even other, more rare options.
Next thing, you should read carefully the stock code and how it handles a properly called "Recipient ID" that you can get from the SMS collection, there's a collection called canonical-addresses (or canonical-address) that serves as a mapping between a phone number (or a comma-separated list of phones) and a recipient id.
The code does a single query on launch to cache the entire table in memory, and then uses it to map between phones and recipient-ids.
Here's the mapping class
Why would the relatively new Telephony API use deprecated tables, instead of ContactsContract?
What you are referring to is not new. In Telephony.java, you see it relies on the existing content://sms provider:
public static final class Inbox implements BaseColumns, TextBasedSmsColumns {
/**
* The {#code content://} style URL for this table.
*/
public static final Uri CONTENT_URI = Uri.parse("content://sms/inbox");
It was already there in Donut (and probably before, but I didn't check).
What's new in Kitkat is the ability to change the SMS app.
It's been five years and it's still relevant. You still need to do endlessly phoneLookup and hang up callbacks on contact tables if all you need to do is synchronize text messages.
I do not understand your concern properly but I am working on similar project, here is the basic code, and basic, important columns for fetching and display a message:
ContentResolver contentResolver = getContentResolver();
final String[] projection = new String[]{"*"};
Cursor SMSL = contentResolver.query(Telephony.Sms.CONTENT_URI, projection, null, null, "date ASC");
int msgscount = SMSL.getCount();
if (msgscount>0) {
msgs = new String[SMSL.getCount()][msgs_column_count];
int i = 0;
while (SMSL.moveToNext()) {
progress.setProgress(i);
msgs[i][0] = SMSL.getString(SMSL.getColumnIndex("address"));
msgs[i][1] = SMSL.getString(SMSL.getColumnIndex("date_sent"));
msgs[i][2] = SMSL.getString(SMSL.getColumnIndex("date"));
msgs[i][3] = SMSL.getString(SMSL.getColumnIndex("type"));
msgs[i][4] = SMSL.getString(SMSL.getColumnIndex("body"));
msgs[i][5] = SMSL.getString(SMSL.getColumnIndex("read"));
if (SMSL.getString(SMSL.getColumnIndex("service_center")) != null){
msgs[i][6] = SMSL.getString(SMSL.getColumnIndex("service_center"));
}else{
msgs[i][6] = "";
}
i++;
}
SMSL.close();
}else{
msgs = new String[0][0];
Toast.makeText(getApplicationContext(),"No messages found!",Toast.LENGTH_LONG).show();
}
If you want any help with this or fetching messages, let me know.

Best way to load a large amount of data in android without making the user wait

I'm working in an application that read phone contacts and use them in my application (Call history, Favorite contacts and All contacts).
My UI consist of tab host control and user can swap between them, as I want my data to be shared across all my activities and also to be saved in only one place.
So I have created a singleton class called data controller, and when I open the application I show loading screen until all data loaded.
The problem now that user is complaining because of waiting a lot of time about (1 minute) every time they open the application when he has a very large amount of contacts, so how can I optimize my code in a good way?
EDIT
This is the method that I'm using to get all contacts:
public static ArrayList<ContactInfo> getAllContactWithNumberAndNameAndPhoto(
Context context, boolean starred) {
ArrayList<ContactInfo> retList = new ArrayList<ContactInfo>();
ContentResolver cr = context.getContentResolver();
Cursor cur = null;
if (starred == true) {
cur = cr.query(ContactsContract.Contacts.CONTENT_URI, null,
"starred=?", new String[] { "1" }, null);
} else {
cur = cr.query(ContactsContract.Contacts.CONTENT_URI, null, null,
null, null);
}
if (cur.getCount() > 0) {
while (cur.moveToNext()) {
ContactInfo item = new ContactInfo();
String id = cur.getString(cur
.getColumnIndex(ContactsContract.Contacts._ID));
String name = cur
.getString(cur
.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
Uri photo = PhoneUtils.getPhotoUriFromID(context, id);
String starredValue = cur.getString(cur
.getColumnIndex(ContactsContract.Contacts.STARRED));
boolean isFav = false;
if (starredValue.equals("1"))
isFav = true;
if (Integer
.parseInt(cur.getString(cur
.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER))) > 0) {
Cursor pCur = cr.query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID
+ " = ?", new String[] { id }, null);
while (pCur.moveToNext()) {
String phoneNo = pCur
.getString(pCur
.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
item.addPhone(removeCharactersFromPhoneNumber(phoneNo));
}
pCur.close();
if (photo != null) {
item.setPhoto(photo.toString());
}
item.setName(name);
item.setFavorite(isFav);
item.setRecent(false);
retList.add(item);
}
}
cur.close();
}
return retList;
}
Please let me know if I can optimize this method.
Im surprised it takes that long to load the contacts from the device.
Have you profiled the app to see where the time is actually spent? Something seems wrong here.
If it truly takes that long to load from the system providers (due to the OS) you could cache the results (i.e. put in your own SQL db) so you can load quickly on each app visit (< 1 sec) and refresh from device in the background.
I guess that the bottleneck of your method is photo loading. Try to load everything except photos, and then show your activity, concurrently loading photos.
Also you can try to create your own app table that contains just the data you need. So you'll do less selects while loading contacts. But you would have to synchronize your table. You can do so concurrently.

How to get Ringtone name in Android?

I'm allowing my user to pick a ringtone for notifications in my app. I want to store the URI of the sound along with the human readable title of the sound.
So far the URI code works great:
Uri uri = intent.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI);
But when I try to get the title, and set it as a button text, I don't get anything. Seems to have no title?
String title = intent.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_TITLE);
button.setText(title);
But my button text is empty. If I do:
button.setText(uri.toString());
then I see the uri perfectly. Should I just try to get the title from the URI? Thanks
This should get it:
Ringtone ringtone = RingtoneManager.getRingtone(this, uri);
String title = ringtone.getTitle(this);
Refer to http://developer.android.com/reference/android/media/Ringtone.html for the documentation, but the short story: Ringtone.getTitle(Context ctx);
I personally had a serious performance problem when I tried the accepted answer, it took about 2 seconds to just load a list of 30 ringtones. I changed it a bit and it works about 10x faster:
uri = ringtoneMgr.getRingtoneUri(cursor.getPosition());
ContentResolver cr = getContext().getContentResolver();
String[] projection = {MediaStore.MediaColumns.TITLE};
String title;
Cursor cur = cr.query(uri, projection, null, null, null);
if (cur != null) {
if (cur.moveToFirst()) {
title = cur.getString(0);
cur.close();
I had problems with 'MediaPlayer finalized without being released'. I use this:
Cursor returnCursor = getContentResolver().query(uri, null, null, null, null);
returnCursor.moveToFirst();
String title = returnCursor.getString(returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
returnCursor.close();
Refer to https://developer.android.com/training/secure-file-sharing/retrieve-info.html for the documentation.

Optimized way to list all contact numbers and sms?

I'm developing a small application so I can get more experience of Android development. I wrote a method that list all of contact number and total messages (just like GoSMS or default SMS Application). But problem that I'm facing right now is slow performance. Below is what I have done to get the result.
Sample result: Mr AAA (10000)
Steps:
Get all SMS thread ID
Loop and get total messages belong to each thread ID.
Get the contact number belong to that thread.
Use that number and PhoneLookup to get contact name.
Here is the method:
public void populateContactList()
{
// Get all sms threads
Cursor smsAddressCursor = getContentResolver().query(
SMSCVar.CONTENT_URI,
new String[] { "DISTINCT "+SMSCVar.THREAD_ID},
null,
null,
null);
while(smsAddressCursor.moveToNext())
{
Contact c = new Contact();
// Get thread_id
String thread_id = smsAddressCursor.getString(smsAddressCursor.getColumnIndex(SMSCVar.THREAD_ID));
// Get total messages
Cursor totalMessage = getContentResolver().query(
SMSCVar.CONTENT_URI,
new String[] {"count("+SMSCVar.BODY+")"},
SMSCVar.THREAD_ID+" = ?",
new String[] {thread_id},
null);
totalMessage.moveToNext();
c.setNumberOfSMS(totalMessage.getInt(0));
totalMessage.close();
// Get number
Cursor number = getContentResolver().query(
SMSCVar.CONTENT_URI,
new String[] {SMSCVar.ADDRESS},
SMSCVar.THREAD_ID+" = ?",
new String[] {thread_id},
null);
number.moveToNext();
String pNumber = number.getString(0);
number.close();
// Get contact name
Uri uriPhonenumber = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI
, Uri.encode(pNumber));
Cursor contactDisplayName = getContentResolver().query(
uriPhonenumber,
new String[] {ContactsContract.PhoneLookup.DISPLAY_NAME},
null,
null,
null);
// If cursor is not null and has at least one result
if(!contactDisplayName.isNull(0) && contactDisplayName.getCount() > 0)
{
// Get contact name for display
contactDisplayName.moveToNext();
c.setContactName(contactDisplayName
.getString(contactDisplayName
.getColumnIndex(ContactsContract.PhoneLookup.DISPLAY_NAME)));
}
else
{
// Get contact number for display
c.setContactName(pNumber);
}
// Don't get confuse here, setContactNumber method is not used for display purpose.
c.setContactNumber(pNumber);
contactListAdapter.add(c);
contactDisplayName.close();
}
smsAddressCursor.close();
}
I've coded to close cursor properly but I still get GARBAGE COLLECTOR messages and slow retrieving time (5 seconds). My SMS is over 11000 messages.
So please assist me!
Thank you!
P/S: I'm not an English native speaker so I've tried my best to make my question easy to understand.

Categories