I'm developing an SMS program and I want to get conversations.
I wrote the code below and it works fine, but I wonder if it could be more efficient
This is for geting conversation threads
Uri SMS_INBOX = Uri.parse("content://sms/conversations/");
Cursor c = getContentResolver().query(SMS_INBOX, null, null, null, "date desc");
startManagingCursor(c);
String[] count = new String[c.getCount()];
String[] snippet = new String[c.getCount()];
String[] thread_id = new String[c.getCount()];
c.moveToFirst();
for (int i = 0; i < c.getCount(); i++) {
count[i] = c.getString(c.getColumnIndexOrThrow("msg_count"))
.toString();
thread_id[i] = c.getString(c.getColumnIndexOrThrow("thread_id"))
.toString();
snippet[i] = c.getString(c.getColumnIndexOrThrow("snippet"))
.toString();
//Toast.makeText(getApplicationContext(), count[i] + " - " + thread_id[i]+" - "+snippet[i] , Toast.LENGTH_LONG).show();
c.moveToNext();
}
c.close();
for getting addresses according to conversation thread
for(int ad = 0; ad < thread_id.length ; ad++)
{
Uri uri = Uri.parse("content://sms/inbox");
String where = "thread_id="+thread_id[ad];
Cursor mycursor= getContentResolver().query(uri, null, where ,null,null);
startManagingCursor(mycursor);
String[] number = new String[mycursor.getCount()];
if(mycursor.moveToFirst()){
for(int i=0;i<mycursor.getCount();i++){
number[i]=mycursor.getString(mycursor.getColumnIndexOrThrow("address")).toString();
mycursor.moveToNext();
}
}
mycursor.close();
and finally checking the adresses (if in contact list) and adding to a list
for(int i =0 ; i < numaralar.length ;i++)
{
String a = numaralar[i].substring(0,1);
if(!a.equals("+")){ kisiismi = numaralar[i]; }
ContentResolver localContentResolver = getApplicationContext().getContentResolver();
Cursor contactLookupCursor =
localContentResolver.query(
Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI,
Uri.encode(numaralar[i])),
new String[] {PhoneLookup.DISPLAY_NAME, PhoneLookup._ID},
null,
null,
null);
try {
while(contactLookupCursor.moveToNext()){
String contactName = contactLookupCursor.getString(contactLookupCursor.getColumnIndexOrThrow(PhoneLookup.DISPLAY_NAME));
kisiismi = contactName;
}
}catch (Exception e) {
kisiismi = numaralar[i].toString();
}
finally {
//Toast.makeText(getApplicationContext(), ad + kisiismi + " " + count[ad], Toast.LENGTH_LONG).show();
myArr.add(kisiismi);
contactLookupCursor.close();
}
}
Are there any way to make this process easier?
Since it is a simple SQLite query and the Contact provider can be accessed in a similar way, you should try to get the job done in SQL by GROUPING via number, timestamp and maybe thrad_id and then matching the results to a query to the contact provider (also via SQLite)
The documentation holds a pretty good description of all available columns.
https://developer.android.com/reference/android/provider/ContactsContract.PhoneLookupColumns.html
From KitKat onwards, we have a specific Uri for this: content://mms-sms/messages/byphone. But before that, this is what we can come up with:
Set<Integer> conversationIds = new HashSet<Integer>();
String numbers = "+xxxxxxxxxx,+yyyyyyyyyy";
final String[] PROJECTION = { Sms._ID, Sms.THREAD_ID };
final String SELECTION = Sms.ADDRESS + " IN (" + numbers.replaceAll("[^,]+", "?") + ")";
final String[] selectionArgs = numbers.split(",");
Cursor cursor = context.getContentResolver().query(Sms.CONTENT_URI, PROJECTION, SELECTION, selectionArgs, null);
int threadColumn = cursor.getColumnIndexOrThrow(Sms.THREAD_ID);
while (cursor.moveToNext())
conversationIds.add(cursor.getInt(threadColumn));
cursor.close();
return conversationIds;
There is no easy way to do the same with MMS messages because they don't keep their address in the database, you have to query each and every one separately. If you need to do it repeatedly, a viable solution is to cache MMS-to-phone number relations in a database of your own.
You can then use the identifiers accumulated in conversationIds to query the individual conversations. Note that if you want to merge different coversations belonging to the same contact, you can reuse the same query selection pattern above with passing in all ids as IN (?,...,?) at once.
Related
I'm getting skype contacts in my android device with this code:
private void getContactList() {
Cursor c = getContentResolver().query(
RawContacts.CONTENT_URI,
new String[] { RawContacts.CONTACT_ID,
RawContacts.DISPLAY_NAME_PRIMARY,
RawContacts.DISPLAY_NAME_ALTERNATIVE },
RawContacts.ACCOUNT_TYPE + "= ?",
new String[] { "com.skype.contacts.sync" }, null);
int contactNameColumn = c
.getColumnIndex(RawContacts.DISPLAY_NAME_ALTERNATIVE);
int count = c.getCount();
skypeName = new String[count];
for (int i = 0; i < count; i++) {
c.moveToPosition(i);
skypeName[i] = c.getString(contactNameColumn);
Log.i("KEY", skypeName[i]);
}
}
This code works fine, but it returns the skype display names.However is there any possibility to get Skype name not the display name so I can call or video call using that Skype name?.
Thanks,
Regards.
You need to query over the Data table not RawContacts, the Data table's data is organized by mimetypes, you just need to find the mimetype containing the info you're looking for, in skype's case it's: "vnd.android.cursor.item/com.skype.android.skypecall.action"
private void getContactList() {
Cursor c = getContentResolver().query(
Data.CONTENT_URI,
new String[] { Data.CONTACT_ID, Data.DATA1 },
Data.MIMETYPE + "= ?",
new String[] { "vnd.android.cursor.item/com.skype.android.skypecall.action" },
null);
while (c != null && c.moveToNext()) {
String contact = c.getLong(0);
String skype = c.getString(1);
Log.i("contact " + contact + " has skype username: " + skype);
}
}
I've typed the code here, so it's not checked and I might have typos
Make sure you ALWAYS close cursors via c.close()
I'm not incredibly familiar with the Android-Skype API, but it looks like this line
.getColumnIndex(RawContacts.DISPLAY_NAME_ALTERNATIVE);
is what pulls the display name instead of the username. You could check the API and see if there's another RawContacts.method that fetches the username instead.
Additional information could be available here:
http://www.programmableweb.com/api/skype
or
http://www.skype.com/en/developer/
Can anybody guide me to find the solution for the following problem.
I have to identify whether the phone contact saved in locally or from Email ?(programatically)
i have read fromgoogle doc that ContactsContract.Groups, which contains information about raw contact groups such as Gmail contact groups. The current API does not support the notion of groups spanning multiple accounts.
Based on that i have tried the following code.
StringBuffer output = new StringBuffer();
final String[] GROUP_PROJECTION = new String[] {
ContactsContract.Groups._ID,
ContactsContract.Groups.TITLE,
ContactsContract.Groups.SUMMARY_WITH_PHONES
};
Cursor c = getContentResolver().query(
ContactsContract.Groups.CONTENT_URI, GROUP_PROJECTION, null,
null, ContactsContract.Groups.TITLE);
int IDX_ID = c.getColumnIndex(ContactsContract.Groups._ID);
int IDX_TITLE = c.getColumnIndex(ContactsContract.Groups.TITLE);
output.append("title"+IDX_TITLE+"\n");
Map<String,GroupInfo> m = new HashMap<String, GroupInfo>();
while (c.moveToNext()) {
output.append("test...\n");
GroupInfo g = new GroupInfo();
g.id = c.getString(IDX_ID);
g.title = c.getString(IDX_TITLE);
output.append("title"+c.getString(IDX_TITLE)+"\n");
int users = c.getInt(c.getColumnIndex(ContactsContract.Groups.SUMMARY_WITH_PHONES));
if (users>0) {
// group with duplicate name?
GroupInfo g2 = m.get(g.title);
if (g2==null) {
m.put(g.title, g);
output.append("title"+g.title+"\n");
groups.add(g);
} else {
g2.id+=","+g.id;
}
}
}
outputText.setText(output);
c.close();
but no hope.
I am posting this answer for future use. We can differentiate the local phone contacts and the sync contacts by using the field called RawContacts.SOURCE_ID
It is described here
SOURCE_ID
read/write
String that uniquely identifies this row to its source account. Typically it is set at the time the raw contact is inserted and never changed afterwards. The one notable exception is a new raw contact: it will have an account name and type (and possibly a data set), but no source id. This indicates to the sync adapter that a new contact needs to be created server-side and its ID stored in the corresponding SOURCE_ID field on the phone.
The sample code is follows, it gives the id for sync contacts and null for others.
private void testContact() {
StringBuffer output = new StringBuffer();
ContentResolver resolver = getContentResolver();
Cursor contacts = resolver.query(Contacts.CONTENT_URI, null,
Contacts.HAS_PHONE_NUMBER + " != 0", null, Contacts._ID
+ " ASC");
Cursor data = resolver.query(Data.CONTENT_URI, null, Data.MIMETYPE
+ "=? OR " + Data.MIMETYPE + "=?", new String[]{
Email.CONTENT_ITEM_TYPE, Phone.CONTENT_ITEM_TYPE},
Data.CONTACT_ID + " ASC");
int idIndex = contacts.getColumnIndexOrThrow(Contacts._ID);
int nameIndex = contacts.getColumnIndexOrThrow(Contacts.DISPLAY_NAME);
int cidIndex = data.getColumnIndexOrThrow(Data.CONTACT_ID);
int data1Index = data.getColumnIndexOrThrow(Data.DATA1);
boolean hasData = data.moveToNext();
while (contacts.moveToNext()) {
long id = contacts.getLong(idIndex);
Uri rawContactUri =
ContentUris.withAppendedId(RawContacts.CONTENT_URI, id);
Uri entityUri =
Uri.withAppendedPath(rawContactUri, Entity.CONTENT_DIRECTORY);
Cursor c =
getContentResolver().query(
entityUri,
new String[] {
RawContacts.ACCOUNT_NAME,
RawContacts.SOURCE_ID, Entity.DATA_ID, Entity.MIMETYPE, Entity.DATA1},
null, null, null);
try {
while (c.moveToNext()) {
String sourceId = c.getString(0);
if (!c.isNull(1)) {
String source_id = c.getString(1);
try {
output.append(c.getString(4)+sourceId+" "+source_id+"\n");
//output.append(datas+ "Sync1 "+ c.getString(4)+" Sync2 "+ c.getString(5)+" Sync3"+ c.getString(6)+" Sync4 "+ c.getString(7)+"\n");
} catch (Exception e) {
e.printStackTrace();
}
//decide here based on mimeType, see comment later
}
}
} finally {
c.close();
}
}
outputText.setText(output);
}
Have a read through this and see if ACCOUNT_TYPE might be able to help you
I used Log, when i get contacts number and there is everything right, until I read them from database and puts in a list, then I loose the "+"character from numbers.
public void getNumbers(String box) {
Uri uri = Uri.parse("content://sms/" + box);
Cursor c = getContentResolver().query(uri, null, null, null, null);
startManagingCursor(c);
// Read the sms data and store it in the list
if (c.moveToFirst()) {
for (int i = 0; i < c.getCount(); i++) {
SMSData sms = new SMSData();
sms.setNumber(c.getString(c.getColumnIndexOrThrow("address"))
.toString());
String nr = sms.getNumber();
Log.d("I got this:", nr);
db.getSMSDataBySearch(nr);
c.moveToNext();
}
}
c.close();
}
And the SQLite code:
SQLiteDatabase db = this.getWritableDatabase();
Cursor cursor = db.rawQuery("INSERT INTO version3 (number) SELECT "+value+" WHERE NOT EXISTS "
+ "(SELECT 1 FROM version3 WHERE number = "+value+")", null);
The SMS content provider does not store the formatting, only the numeric data.
If you want it formatted, there are tools or you can build it:
Java phone number format API
in my app i am listing contacts in a listview. no of contacts is 1000+. i get the contacts
by using ContentResolver query that is cr.query(...),store the values in an arraylist
and after that load the array list in setListAdapter(...). to display the all contacts my
apps takes nearly 1 minute so that i use Async task but there is no big differences by using the async task.
i need to display all contacts within 2 to 4 seconds. i check in the default contacts
application on android simulator which is load within in 2 to 4 seconds. i have spend
long time in google. but i could not get any helpful solution. please help me how to fast the loading contacts on listview. please help me.
my coding sample:
private ArrayList<ContactListEntry> loadContactListInternal(String searchString) {
ArrayList<ContactListEntry> contactList = new ArrayList<ContactListEntry>();
ContentResolver cr = getContentResolver();
Cursor cur = null;
String[] projection = new String[] {BaseColumns._ID,ContactsContract.Contacts.DISPLAY_NAME,ContactsContract.Contacts.PHOTO_ID};
....
cur=cr.query(ContactsContract.Contacts.CONTENT_URI, projection, selection, null, ContactsContract.Contacts.DISPLAY_NAME + " ASC");
while (cur.moveToNext()) {
int id = Integer.parseInt(cur.getString(0));
....
if (input !=null)
photo = BitmapFactory.decodeStream(input);
....
ArrayList<ContactListEntry.PhoneEntry> phoneEntries = new ArrayList<ContactListEntry.PhoneEntry>();
String[] projection1 = new String[] {ContactsContract.CommonDataKinds.Phone.NUMBER,ContactsContract.CommonDataKinds.Phone.TYPE};
Cursor pcur = cr.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,projection1, ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?", new String[] { String.valueOf(id) }, null);
while (pcur.moveToNext()) {
...
}
pcur.close();
ContactListEntry entry = new ContactListEntry(id, name, photo, phoneEntries);
contactList.add(entry);
}
cur.close();
return contactList;
}
.....
in another class
private void selectionUpdated() {
....
setListAdapter(new SelectedArrayAdapter(this, app.selectedContacts));
...
}
Use the Concept of projections and selection arguments to retrive the contacts in my case for 500 contacts intially it was taking 12 sec.
Now it is taking 350ms(lessthan second)
void getAllContacts() {
long startnow;
long endnow;
startnow = android.os.SystemClock.uptimeMillis();
ArrayList arrContacts = new ArrayList();
Uri uri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI;
String selection = ContactsContract.Contacts.HAS_PHONE_NUMBER;
Cursor cursor = ctx.getContentResolver().query(uri, new String[]{ContactsContract.CommonDataKinds.Phone.NUMBER, ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME, ContactsContract.CommonDataKinds.Phone._ID, ContactsContract.Contacts._ID}, selection, null, ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME + " ASC");
cursor.moveToFirst();
while (cursor.isAfterLast() == false) {
String contactNumber = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
String contactName = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
int phoneContactID = cursor.getInt(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone._ID));
int contactID = cursor.getInt(cursor.getColumnIndex(ContactsContract.Contacts._ID));
Log.d("con ", "name " + contactName + " " + " PhoeContactID " + phoneContactID + " ContactID " + contactID)
cursor.moveToNext();
}
cursor.close();
cursor = null;
endnow = android.os.SystemClock.uptimeMillis();
Log.d("END", "TimeForContacts " + (endnow - startnow) + " ms");
}
More information on this link http://www.blazin.in/2016/02/loading-contacts-fast-from-android.html ....
So your problem is that you do a lot of subqueries for each contact. I has the same issue once upon a time. My case was that I showed many contacts and allowed the user to click on any of them. After that I started processing the contact in another activity.
Back then I finally decided that I should display only the name and lazily fetch all the other data later on, just before I launch the next activity. This was amazing: decreased the speed of my program almost by a factor of 200, and the operations became completely invisible to the user.
The default android list does the same if I am not wrong - it displays only the name, and later on loads all the other contact-related data.
i use cursor adapter and i cut the databse,arrayadapter and optimize the code.
Consider having just one query and getting rid of the sub query idea (as already suggested). You can achieve speed by just querying using the Content Uri:
"ContactsContract.CommonDataKinds.Phone.CONTENT_URI"
This URI also has the "ContactsContract.Contacts.DISPLAY_NAME" field.
You might also want to consider doing this query and working with your adapter in a seperate thread to make it completely transparent.
This worked for me.
OPTIMIZED SOLUTION HERE.....
private static final String[] PROJECTION = new String[] {
ContactsContract.CommonDataKinds.Phone.CONTACT_ID,
ContactsContract.Contacts.DISPLAY_NAME,
ContactsContract.CommonDataKinds.Phone.NUMBER
};
.
.
.
ContentResolver cr = getContentResolver();
Cursor cursor = cr.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, PROJECTION, null, null, null);
if (cursor != null) {
try {
final int nameIndex = cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME);
final int numberIndex = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
String name, number;
while (cursor.moveToNext()) {
name = cursor.getString(nameIndex);
number = cursor.getString(numberIndex);
}
} finally {
cursor.close();
}
}
CHEERS...:)
I have a big performance issue in my app. After going through traceview i found that most of my app's performance has been consumed by cursors. So i was wondering is there any alternative to Cursors for dealing with device contact list. And if there is no alternative then please advise me how to deal with cursors so it won't slow down your app.
Please HELP!!
Thanks.
This part of my code has performance issue :-
public void getDisplayName()
{
Cursor c1 = this.getContentResolver().query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
String personName = null, number = null;
try
{
Log.e(TAG, "I am here!!");
if(c1.getCount() > 0)
{
Log.e(TAG, "I am here2!!");
while(c1.moveToNext())
{
HashMap<String,String> item = new HashMap<String,String>();
String id = c1.getString(c1.getColumnIndex(Contacts._ID));
personName = c1.getString(c1.getColumnIndex(Contacts.DISPLAY_NAME));
item.put("Name", personName);
Cursor cur = this.getContentResolver().query(CommonDataKinds.Phone.CONTENT_URI, null, CommonDataKinds.Phone.CONTACT_ID +" = ?", new String[]{id}, null);
while(cur.moveToNext())
{
Log.e(TAG, "I am here!!3");
number = cur.getString(cur.getColumnIndex(CommonDataKinds.Phone.NUMBER));
item.put("Number", number);
}
displayName.add(item);
}
}
}
finally
{
c1.close();
}
}
How to fetch a contact name and number without using Cursors?
That is not possible.
I have a big performance issue in my app.
Rather than using a single query, you use N+1 queries, where N is the number of rows returned by the other query. This is guaranteed to give you poor performance compared to just doing a single query.
You can get the user's name and _ID along with the phone numbers:
String[] PROJECTION=new String[] { Contacts._ID,
Contacts.DISPLAY_NAME,
Phone.NUMBER
};
Cursor c=a.managedQuery(Phone.CONTENT_URI, PROJECTION, null, null, null);
Also, never call getColumnIndex() in a loop, since the value never changes.
I was working on same code and I need email and phone numbers both in first approach It took around 35 seconds to fetch 612 contacts, when I optimized it, it took 2 seconds to fetch 612 contacts
my code.
ContentResolver cr = context.getContentResolver();
String[] projection = new String[] { Contacts.DISPLAY_NAME, Phone.NUMBER };
String selection = ContactsContract.Contacts.HAS_PHONE_NUMBER + " = ?";
String[] selectionArgs = { String.valueOf(1) };
Cursor cur = cr.query(Phone.CONTENT_URI, projection, selection, selectionArgs, null);
if (cur.getCount() > 0) {
while (cur.moveToNext()) {
String name = cur.getString(cur.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
String phNo = cur.getString(cur.getColumnIndex(Phone.NUMBER));
ContactModel contactModel = new ContactModel();
contactModel.setName(name);
contactModel.setPhoneNumber(phNo);
contactList.add(contactModel);
}
}
String[] projectionEmail = new String[] { Contacts.DISPLAY_NAME, Email.ADDRESS };
String selectionEmail = ContactsContract.Contacts.HAS_PHONE_NUMBER + " = ?";
String[] selectionArgsEmail = { String.valueOf(1) };
Cursor curEmail = cr.query(Email.CONTENT_URI, projectionEmail, selectionEmail, selectionArgsEmail, null);
if (curEmail.getCount() > 0) {
while (curEmail.moveToNext()) {
String Emailname = curEmail.getString(curEmail.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
String email = curEmail.getString(curEmail.getColumnIndex(Email.ADDRESS));
ContactModel contactModel = new ContactModel();
contactModel.setName(Emailname);
contactModel.setEmailId(email);
contactList.add(contactModel);
}
}
`