I am trying to find the contact details corresponding to an sms from the phone's sms inbox. From my understanding the person column is a foreign key to the _id column of ContactsContract.Contacts.
My problem is that I am getting wrong values for the person value from the sms query. Some person ids don't exist in the contact table and some are pointing to totally different contact.
Below is the code that I use for fetching the list of person values, Can anyone tell me if I missed something or faced a similar problem.
Tested with Nexus S running Android 4.1.2
Uri parsedUri = Uri.parse("content://sms/inbox");
String[] projection = new String[] { "_id", "address", "person" };
Cursor cursor = contentResolver.query(parsedUri, projection, null, null, null);
Log.d(TAG, "Total Count " + cursor.getCount());
if (cursor.getCount() > 0) {
// String address;
int person;
int personIndex = cursor.getColumnIndex("person");
if (cursor.moveToFirst())
do {
person = cursor.getInt(personIndex);
if (person > 0) {
// Add this id to a list
}
} while (cursor.moveToNext());
}
cursor.close();
Update: I was looking at some documentation from the Android Developer Portal (http://developer.android.com/guide/topics/providers/contacts-provider.html), Is it possible that the person id that is retrieved from the sms inbox is referring to the ContactsContract.RawContacts instead of ContactsContract.Contacts as I have done.
Indeed the person column in the sms inbox refers to a RawContacts entry's id. By querying the RawContacts table you can find the contact_id for the Contacts table for the specific user.
You can try this code to get the contact id from the RawContacts table..
long contactId = 0;
Uri uri = ContactsContract.RawContacts.CONTENT_URI;
String[] projection = new String[] { ContactsContract.RawContacts._ID, ContactsContract.RawContacts.CONTACT_ID };
Cursor cursor = contentResolver.query(uri, projection,
ContactsContract.RawContacts._ID + " = ?",
new String[] { rawContactId }, null);
if (cursor.moveToFirst()) {
int contactIdIndex = cursor.getColumnIndex(ContactsContract.RawContacts.CONTACT_ID);
contactId = cursor.getLong(contactIdIndex);
}
cursor.close();
This should solve your problem for fetching the correct contact details by reading an sms row in Android. However as the sms data provider is not documented and phone manufacturers may use them differently, there is chance that this approach may not work on phones other than pure android.
You first check that person number is exits in contact list or not.if yes then match both id.
Cursor cursor=managedQuery(uri, null, null, null, null);
while (cursor.moveToNext()) {
String contactId = cursor.getString(cursor.getColumnIndex(
ContactsContract.Contacts._ID));
String hasPhone = cursor.getString(cursor.getColumnIndex(
ContactsContract.Contacts.HAS_PHONE_NUMBER));
if (Boolean.parseBoolean(hasPhone)) {
// You know have the number so now query it like this
Cursor phones = getContentResolver().query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID +" = "+ contactId,
null, null);
while (phones.moveToNext()) {
String phoneNumber = phones.getString(
phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
}
phones.close();
}
}
Related
I am trying to get first name and last name in contact book by phone number.
Here is my code:
Uri contactUri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(phoneNumber));
Cursor cursor = cResolver.query(uri, null, null, null, null);
if(cursor != null && cursor.moveToFirst()) {
int idColumnIndex = cursor.getColumnIndex(ContactsContract.Contacts._ID);
int firstNameIndex = cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME);
int lastNameIndex = cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME);
while (!cursor.isAfterLast()) {
long id = cursor.getLong(idColumnIndex);
contact = new MyContact();
contact.setId(id);
contact.setFirstName(cursor.getString(firstNameIndex));
contact.setLastName(cursor.getString(lastNameIndex));
}
cursor.close();
}
But firstNameIndex and lastNameIndex is always -1. What I am doing wrong ? Please help me.
PhoneLookup is a nice and quick way to get contact data by a phone number, but it returns a cursor limited to the columns mentioned in the docs.
You can see there's DISPLAY_NAME you can access, but not GIVEN_NAME, FAMILY_NAME.
GIVEN_NAME & FAMILY_NAME are fields stored in the Data table, which means you need to query that table separately to get to those fields.
So, you can just add another query using the contact ID you got from PhoneLookup (note that for each looked up phone there might be multiple contacts returned).
Here's a sample method to get first/last names from contact ID:
private void addNames(MyContact contact, long contactId) {
String[] projection = new String[] {StructuredName.GIVEN_NAME, StructuredName.FAMILY_NAME};
// filter to just StructuredName rows from the data table for the given contact
String selection = Data.CONTACT_ID + "=" + contactID + " AND " + Data.MIMETYPE + "=" + StructuredName.CONTENT_ITEM_TYPE;
Cursor cursor = getContentResolver().query(Data.CONTENT_URI, projection, selection, null, null);
if (cursor.next()) {
contact.setFirstName(cursor.getString(0));
contact.setLastName(cursor.getString(1));
}
cursor.close();
}
I want to retrieve >10000 contacts from the android device. To fetch that much contact it takes about 8-10 min. Is there any other possible way to do this. I have implemented a method its working fine but when it comes to large number of contacts it taking it time to fetch the contacts.
ContentResolver cr = getActivity().getApplication().getContentResolver();
Cursor cur = cr.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
if (cur.getCount() > 0) {
while (cur.moveToNext()) {
String id = cur.getString(cur.getColumnIndex(
ContactsContract.Contacts._ID));
String name = cur.getString(cur.getColumnIndex(
ContactsContract.Contacts.DISPLAY_NAME));
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()) {
int phoneType = pCur.getInt(pCur.getColumnIndex(
ContactsContract.CommonDataKinds.Phone.TYPE));
String phoneNumber = pCur.getString(pCur.getColumnIndex(
ContactsContract.CommonDataKinds.Phone.NUMBER));
phoneNumber = phoneNumber.replace(" ","");
phoneNumber = phoneNumber.replace("-","");
boolean addNumber = stringCheck(phoneNumber,symbols);
if (!addNumber){
if (phoneNumber.length() == 10){
addContact(phoneNumber,phoneType,name);
}else if (phoneNumber.length() == 11){
phoneNumber = phoneNumber.substring(1);
addContact(phoneNumber,phoneType,name);
}else if (phoneNumber.length() == 12){
phoneNumber = phoneNumber.substring(2);
addContact(phoneNumber,phoneType,name);
}else if (phoneNumber.length() == 13){
phoneNumber = phoneNumber.substring(3);
addContact(phoneNumber,phoneType,name);
}
}
}
pCur.close();
}
}
}
If 900 out of the 1000 contacts have phone numbers, you're currently querying the DB 901 times, you can reduce it to only two queries:
Give me all contacts information
Give me all phone numbers
Then you use the contact-id field on both to match the phone to the right contacts.
Also, as noted in other answers, you really should add projection to all your queries to improve performance.
Another improvement you can make is to avoid runtime cur.getColumnIndex() calls, if you have a projection, you should already know the index - so use it hard-coded
Map<Long, List<String>> phones = new HashMap<>();
ContentResolver cr = getActivity().getApplication().getContentResolver();
// First build a mapping: contact-id > list of phones
Cursor cur = cr.query(Phone.CONTENT_URI, new String[] { Phone.CONTACT_ID, Phone.Number }, null, null, null);
while (cur != null && cur.moveToNext()) {
long contactId = cur.getLong(0);
String phone = cur.getString(1);
List list;
if (phones.contains(contactId)) {
list = phones.get(contactId);
} else {
list = new ArrayList<String>();
phones.put(contactId, list);
}
list.add(phone);
}
cur.close();
// Next query for all contacts, and use the phones mapping
cur = cr.query(Contacts.CONTENT_URI, new String[] { Contacts._ID, Contacts.DISPLAY_NAME }, null, null, null);
while (cur != null && cur.moveToNext()) {
long id = cur.getLong(0);
String name = cur.getString(1);
List<String> contactPhones = phones.get(id);
addContact(id, name, contactPhones);
}
Keep your fetching process in doInBackground method of AyncTask and then display it. And get only those info which is required first, eg, Contact Name and ID.
refer this ans for more clarification:
Fetching a large number of contacts
In order to fetch large number of contacts you must be fetching the contacts in the form of pages by using rest API.
For example page size of contacts is 100.
One possible approach:
If you are showing the contacts in the listview then you can fetch the data while user is scrolling the list. For that You have to set the threshold position of the list i.e. 80, So when user reaches at 80th position while scrolling you can hit your rest API for next page to fetch the new contacts and add those contacts in the listview.
From your comments i can see you want all the contacts in order to move forward.For this you can start an intent service when app is launched. This service will fetch all the contacts and store them. You can use them as per your needs
Here some optimization for speed up
String[] select = new String[]{ContactsContract.Contacts._ID, ContactsContract.Contacts.DISPLAY_NAME, ContactsContract.Contacts.HAS_PHONE_NUMBER};
Cursor cur = cr.query(ContactsContract.Contacts.CONTENT_URI, select, null, null, null);
String[] selectOnly = new String[]{ContactsContract.CommonDataKinds.Phone.NUMBER, ContactsContract.CommonDataKinds.Phone.TYPE};
Cursor pCur = cr.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
selectOnly,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID +" = ?",
new String[]{id}, null);
Do your number check, length check in server side if possible. Do it in Async task.
I want to filter what numbers I am getting from Android based on which contacts the user chose to display in his contact book. (For example only contacts saved on the device when all other contacts are excluded)
I read here that you can do this by using
Uri queryUri = ContactsContract.Contacts.CONTENT_URI;
I use following code to read the contacts and I allways get every contact, phone, SIM, etc..
//https://stackoverflow.com/questions/16651609/how-to-display-phone-contacts-only-exclude-sim-contacts
// http://www.higherpass.com/Android/Tutorials/Working-With-Android-Contacts/1/
ContentResolver cr = currentActivity.getContentResolver();
Uri queryUri = ContactsContract.Contacts.CONTENT_URI;
Cursor cur = cr.query(queryUri,
null, null, null, null);
if (cur.getCount() > 0) {
while (cur.moveToNext()) { //Are there still contacts?
//See if the contact has at least one number
if (Integer.parseInt(cur.getString(cur.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER))) > 0) {
String id = cur.getString( cur.getColumnIndex(ContactsContract.Contacts._ID) );
String name = cur.getString( cur.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME) );
ArrayList<String> numbers = new ArrayList<String>();
//Read numbers:
Cursor pCur = cr.query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID +" = ?",
new String[]{id}, null);
while (pCur.moveToNext()) {
numbers.add( pCur.getString(
pCur.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)) );
Log.e("Contactbook", "The latest added number is: " + numbers.get(numbers.size()-1) );
}
pCur.close();
}
}
}
What am I missing? This code still gives me both SIM and phone contacts to the log.
Edit: To clarifify, in the contactbook you got the "Display options". In there ist the "select contacts to display"-option, and I want to read the contacts that are shown based on the users choice there. So if a user choses to show only SIM-contacts, read only SIM-contacts, if he choses to only show Phone-Contacts, show onyl phone contacts etc...
Try with following "selection".
String selection = ContactsContract.Contacts.IN_VISIBLE_GROUP + " = ?";
From Android docs:
public static final String IN_VISIBLE_GROUP: An indicator of whether this contact is supposed to be visible in the UI. "1" if the contact has at least one raw contact that belongs to a visible group; "0" otherwise.
This should be your "selection" argument in query API.
Update: I tried below code on Android-2.3(I know it is old device, but right now Don't have newer device with me).
final ContentResolver cr = getContentResolver();
String[] projection = new String[] { ContactsContract.Contacts._ID, ContactsContract.Contacts.DISPLAY_NAME };
String selection = ContactsContract.Contacts.IN_VISIBLE_GROUP + " = ?" ;
String[] Args = { "1" };
final Cursor contacts = cr.query(
ContactsContract.Contacts.CONTENT_URI, projection,
selection,Args ,
null);
This could be a long operation (depending on no. of contacts), hence you should use CursorLoader class (A loader that queries the ContentResolver and returns a Cursor) for this.
cursorLoader.loadInBackground();
This will be called on a worker thread to perform the actual load and to return the result of the load operation.
You can easily create two Arrays for each kind of contacts
ArrayList<String> simContacts = new ArrayList<>();
//get all sim contacts
Uri simUri = Uri.parse("content://icc/adn");
Cursor cursorSim = getContext().getContentResolver().query(simUri, null, null, null, null);
while (cursorSim.moveToNext()) {
simContacts.add(cursorSim.getString(cursorSim.getColumnIndex("name")));
}
}
ArrayList<String> allContacts = new ArrayList<>();
//get all contacts
Cursor cur = cr.query(ContactsContract.Contacts.CONTENT_URI,
null, null, null, null);
if (cursor != null && cursor.getCount() > 0) {
while (!cursor.isAfterLast()) {
String phoneNumber = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.NUMBER));
String displayNameColumn = Utils.hasHoneycomb() ? ContactsContract.Contacts.DISPLAY_NAME_PRIMARY : ContactsContract.Contacts.DISPLAY_NAME;
String displayName = cursor.getString(cursor.getColumnIndexOrThrow(displayNameColumn));
//check if simContacts Array contains this particular name
if (!simContacts.contains(displayNameColumn){
allContacts.add(displayNameColumn);
}
}
}
This is just a working example,of course you can modify to your needs.You can parse more contact fields and make more complication queries.
I want to get some basic info of all contacts(I use api lvl 8). Currently i use this code snippet
private List<ContactInfo> readContacts() {
ContentResolver cr = getContentResolver();
Cursor cur = cr.query(ContactsContract.Contacts.CONTENT_URI, null,
null, null, Phone.DISPLAY_NAME + " ASC");
for (int i = 0; i < cur.getColumnCount(); i++) {
Log.v("COlum", cur.getColumnName(i));
}
List<ContactInfo> temp = new ArrayList<ContactInfo>();
if (cur.getCount() > 0) {
while (cur.moveToNext()) {
ContactInfo mContactInfo = new ContactInfo();
String id = cur.getString(cur
.getColumnIndex(ContactsContract.Contacts._ID));
mContactInfo.setId(Long.parseLong(id));
String name = cur
.getString(cur
.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
if (Integer
.parseInt(cur.getString(cur
.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER))) > 0) {
mContactInfo.setmDisplayName(name);
// get the <span class="IL_AD" id="IL_AD7">phone
// number</span>
Cursor pCur = cr.query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID
+ " = ?", new String[] { id }, null);
while (pCur.moveToNext()) {
String phone = pCur
.getString(pCur
.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
mContactInfo.setmPhoneNumber(phone);
}
pCur.close();
// get email and type
Cursor emailCur = cr.query(
ContactsContract.CommonDataKinds.Email.CONTENT_URI,
null,
ContactsContract.CommonDataKinds.Email.CONTACT_ID
+ " = ?", new String[] { id }, null);
while (emailCur.moveToNext()) {
// This would allow you get several email <span
// class="IL_AD" id="IL_AD9">addresses</span>
// if the email addresses were stored in an array
String email = emailCur
.getString(emailCur
.getColumnIndex(ContactsContract.CommonDataKinds.Email.DATA));
mContactInfo.setmEmail(email);
}
emailCur.close();
temp.add(mContactInfo);
}
}
}
return temp;
}
and pass to custom adapter (extended baseadapter). I get contact's photo using:
public static Bitmap loadContactPhoto(ContentResolver cr, long id) {
Uri uri = ContentUris.withAppendedId(
ContactsContract.Contacts.CONTENT_URI, id);
InputStream input = openContactPhotoInputStream1(cr, uri);
if (input == null) {
return null;
}
return BitmapFactory.decodeStream(input);
}
I tested on my phone with 2x contacts (had photo). I took ~ 10s to fetch all contact at 1st runtime. I try force close in application settings and run again. This time it took ~2s to get data.So i want to know the most effective way to get contacts info.
I found some similar SO questions but they dont need photo. contacts in android
I tried use cursor and cursor adapter but i dont know what query to get photo_uri + contact name at the same time.
Edit: i removed all getColumnIndex i can out of loop and project only column i want. The performance is better(10s => 5s).
What i want to know :
Better way to query info and set to my ContactInfo model or the query which get name, phone number, email, photo at the same time to pass to cursor adapter.
Thanks in advance
I changed to CusorAdapter and use ContactsPhotoLoader from Contacts app and performance is improved.
To get contact info you have to work with the Android Contact API. Also you have to keep in min that you have to handle this Api in a different way for android api below API 4 (1.6)and for the Android API 5 (2.0) and higher:
I will provide you some good links that will help you:
Working With Android Contacts
Handling Contact Photos (All API Levels)
Using the Contact Picker API 2.0 and above
Retrieving Contact Information (Name, Number, and Profile Picture)
API4 and lower
Thes also some SO thread similar to yours that must b helpful for you
get contact info from android contact picker
Getting a Photo from a Contact
Currently, I'm using this code in order to get the contact name and the phone number:
ContentResolver contentResolver = getContentResolver();
Cursor people = contentResolver.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
int nameIndex = people.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME);
int idIndex = people.getColumnIndex(ContactsContract.Contacts._ID);
int hasPhoneNumberIndex = people.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER);
String name, id;
int hasPhoneNumber;
while(people.moveToNext()){
name = people.getString(nameIndex);
id = people.getString(idIndex);
hasPhoneNumber = people.getInt(hasPhoneNumberIndex);
if(hasPhoneNumber > 0){
Cursor phones = contentResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, ContactsContract.CommonDataKinds.Phone.CONTACT_ID +" = "+id, null, null);
phones.moveToFirst();
int phoneIndex = phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
String phone = phones.getString(phoneIndex);
HashMap<String, String> namePhoneType = new HashMap<String, String>();
namePhoneType.put("Name", name);
namePhoneType.put("Phone", phone);
m_peopleList.add(namePhoneType);
phones.close();
}
}
But this is extremely slow.
Is there a way to retrieve name and phone in only one query?
It seems to me that the noted performance issue stems from the inherent "n+1 select" problem in the implementations proposed.
open a cursor to iterate over all contacts (1)
for each contact open a cursor to iterate over the phone numbers for that contact (n)
A faster approach, if you truly need all this data, is to perform two queries from contacts and phone numbers returning the appropriate surrogate and primary keys and then performing the join in memory.
Query 1: get all contacts as a Map by ContactId
With the myriad of solutions proposed being sure to pull out the _ID field as the ContactId
Query 2: get all the phone numbers and store them in a list
Cursor c = MyO2Application.getContext().getContentResolver().query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
new String[] {
ContactsContract.CommonDataKinds.Phone._ID,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID,
ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME,
ContactsContract.CommonDataKinds.Phone.NUMBER },
null,
null,
null
);
while (c.moveToNext()) {
String id = c.getString(0);
String contactId = c.getString(1); // this is the foreign key to the contact primary key
String displayName = c.getString(2);
String number = c.getString(3);
You can then iterate through the list of phone numbers, looking up the contact from the map by ContactId and associate the phone numbers with the contact.
Execution speeds for 1000 contacts went from 60 seconds down to 4 seconds. As is often the case, there is a trade-off on memory consumption and impact to GC.
Observation while posting: getting the ids as an int and using SparseArray may be an approach worth considering. Minimal impact expected, however, compared to the "n+1 select" issue addressed here.
You can read more about how to do it in a different way here
Here's a snippet
//query for the people in your address book
Cursor cursor = getContentResolver().query(People.CONTENT_URI, null, null, null,People.NAME + " ASC");
startManagingCursor(cursor);
//bind the name and the number fields
String[] columns = new String[] { People.NAME, People.NUMBER };
int[] to = new int[] { R.id.name_entry, R.id.number_entry };
SimpleContactAdapter mAdapter = new SimpleContactAdapter(this, R.layout.list_entry, cursor, columns, to);
this.setListAdapter(mAdapter);
I've founded a way:
Cursor people = getContentResolver()
.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
new String[] {Phone._ID, Phone.DISPLAY_NAME, Phone.NUMBER}, null, null, Phone.DISPLAY_NAME + " ASC");