I'm looking for the count of sent sms messages in a particular thread (say, with id 15). I found this How do I get the count of SMS messages per contact into a textview? <-- but it doesn't solve my issue, as it counts both sent and received sms messages. Is it possible to count only sent messages? I think I could query "content://sms/sent" and walk through each SMS, but I wonder if there's a more efficient way.
Thanks
You can query Sms.Conversations with your thread ID, and a selection that restricts the TYPE column to MESSAGE_TYPE_SENT. Since you just want the count, we can do a SELECT COUNT() query, so there's no resources wasted building a Cursor with unused values. For example:
private int getThreadSentCount(String threadId) {
final Uri uri = Sms.Conversations.CONTENT_URI
.buildUpon()
.appendEncodedPath(threadId)
.build();
final String[] projection = {"COUNT(1)"};
final String selection = Sms.TYPE + "=" + Sms.MESSAGE_TYPE_SENT;
int count = -1;
Cursor cursor = null;
try {
cursor = getContentResolver().query(uri,
projection,
selection,
null,
null);
if (cursor != null && cursor.moveToFirst()) {
count = cursor.getInt(0);
}
}
catch (Exception e) {
e.printStackTrace();
}
finally {
if (cursor != null) {
cursor.close();
}
}
return count;
}
The Sms class used above is in the android.provider.Telephony class.
import android.provider.Telephony.Sms;
For reference, Sms.Conversations.CONTENT_URI is equivalent to Uri.parse("content://sms/conversations"), Sms.TYPE is "type", and Sms.MESSAGE_TYPE_SENT is 2.
Related
I'm developing a SMS application and come to the following issue. Currently I can read SMS conversation by using provider Telephony.Sms.Conversations by using CursorLoader. From cursor returned by this CursorLoader, I can display conversations's address which are phone numbers.
My question is how to retrieve SMS conversation contact name efficiently to display along with SMS conversation, not the phone number. Is there anyway to load list of contacts from list of phone numbers returned by the CursorLoader before?. Of course I've tried to load one by one contact name by using phone number but that terribly reduce the application performance.
Thank you in advance.
I've been searching for a solution myself and eventually came out with a good compromise in my opinion.
As soon as my query is finished, I store in an HashMap<String, String> contact_map my values as
int SENDER_ADDRESS = cursor.getColumnIndex(Telephony.TextBasedSmsColumns.ADDRESS);
while (cursor.moveToNext()) {
contact_map.put(
cursor.getString(SENDER_ADDRESS),
getContactName(getApplicationContext(), cursor.getString(SENDER_ADDRESS))
);
}
Method getContactName:
public static String getContactName(Context context, String phoneNumber) {
ContentResolver cr = context.getContentResolver();
Uri uri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(phoneNumber));
Cursor cursor = cr.query(uri, new String[]{ContactsContract.PhoneLookup.DISPLAY_NAME}, null, null, null);
if (cursor == null) {
return null;
}
String contactName = null;
if(cursor.moveToFirst()) {
contactName = cursor.getString(cursor.getColumnIndex(ContactsContract.PhoneLookup.DISPLAY_NAME));
}
if(cursor != null && !cursor.isClosed()) {
cursor.close();
}
if (contactName != null) {
return contactName;
} else {
return phoneNumber;
}
}
EDIT:
I then get the contact name with
String name = contact_map.get(cursor.getString(SENDER_ADDRESS));
Hope it helps!
In my android app when an incoming call i want to show my custom ui and i am able to do this.
No i want to check incoming number is from contacts or not.
Below is my code for doing so but it returns null for an incoming number which is stored in my contacts list.
public String findNameByNumber(String num){
Uri uri = Uri.withAppendedPath(Phones.CONTENT_FILTER_URL, Uri.encode(num));
String name = null;
Cursor cursor = mContext.getContentResolver().query(uri,
new String[] { Phones.DISPLAY_NAME }, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
name = cursor.getString(cursor.getColumnIndex(Phones.DISPLAY_NAME));
cursor.close();
callyName.setText(name+" Calling..");
}
return name;
}
and i have incoming call from a number say+917878787878 but in my contacts this contact is stored as name XYZ with number 78 78 787878,which is formated because between number there are space.and also try by excluding +91 but still it returns null.
So how can i find number which is stored in any format.Which may be stored with country code or not.
Thanks In advance.
Try this code instead (using PhoneLookup.CONTENT_FILTER_URI instead of Phones):
String res = null;
try {
ContentResolver resolver = ctx.getContentResolver();
Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(phoneNumber));
Cursor c = resolver.query(uri, new String[]{PhoneLookup.DISPLAY_NAME}, null, null, null);
if (c != null) { // cursor not null means number is found contactsTable
if (c.moveToFirst()) { // so now find the contact Name
res = c.getString(c.getColumnIndex(CommonDataKinds.Phone.DISPLAY_NAME));
}
c.close();
}
} catch (Exception ex) {
/* Ignore */
}
return res;
As docs says ContactsContract.PhoneLookup: A table that represents the result of looking up a phone number, for example for caller ID. To perform a lookup you must append the number you want to find to CONTENT_FILTER_URI. This query is highly optimized.
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 am querying the Contacts out of the built in Contacts provider URI in Android. I want to get just the PHONE contacts; is there any consistent way to do this? It seems from what I can find that the account name for phone contacts differs from manufacturer to manufacturer (see this question). Is there a way to get PHONE contacts (not SIM, Facebook, Twitter or others) in a consistent, reliable, manufacturer- and device- agnostic way?
Cursor cursor = null;
try {
String selection = ContactsContract.Data._ID + " = ?";
String[] selectionArgs = new String[] { id };
String[] projection = new String[] { ContactsContract.PhoneLookup.NUMBER};
cursor = getContentResolver().query(
ContactsContract.Contacts.CONTENT_URI,
projection, selection, selectionArgs, null);
if (cursor == null || !cursor.moveToFirst())
return;
String phone = cursor.getString(0);
} finally {
if (cursor != null && !cursor.isClosed())
try {
cursor.close();
} catch (Throwable ignore) {
// Ignored.
}
}
Where "?" is user ID, you can put this code into a loop.
I'm working on an extend SMS application. And now i can read all the SMS message from the mmssms.db. In the SMS database table, the field 'person' indicate the '_id' in contact table. When 'person' is >= 1, that means the message is sent from people in the contact list. So i can 'managedQuery' from contact table. But the question is, in my mobile phone, the test program sometimes can NOT query the contact infomation even 'person' >= 1. So can somebody show me some correct ways to query contact infomation by 'person' filed in SMS table ?
Here is some sample code which can make my question more clear:
class ContactItem {
public String mName;
}
ContactItem getContact(Activity activity, final SMSItem sms) {
if(sms.mPerson == 0) return null;
Cursor cur = activity.managedQuery(ContactsContract.Contacts.CONTENT_URI,
new String[] {PhoneLookup.DISPLAY_NAME},
" _id=?",
new String[] {String.valueOf(sms.mPerson)}, null);
if(cur != null &&
cur.moveToFirst()) {
int idx = cur.getColumnIndex(PhoneLookup.DISPLAY_NAME);
ContactItem item = new ContactItem();
item.mName = cur.getString(idx);
return item;
}
return null;
}
Ok, as there is no one help me out, i tried to read some open source project code, and i got answer now. As i supposed at the very beginning, the best way to query the contact information from a SMS message, is to query by the NUMBER(also know as ADDRESS):
ContactItem getContactByAddr(Context context, final SMSItem sms) {
Uri personUri = Uri.withAppendedPath(
ContactsContract.PhoneLookup.CONTENT_FILTER_URI, sms.mAddress);
Cursor cur = context.getContentResolver().query(personUri,
new String[] { PhoneLookup.DISPLAY_NAME },
null, null, null );
if( cur.moveToFirst() ) {
int nameIdx = cur.getColumnIndex(PhoneLookup.DISPLAY_NAME);
ContactItem item = new ContactItem();
item.mName = cur.getString(nameIdx);
cur.close();
return item;
}
return null;
}