I'm using the following code segment to get phone numbers of a contact by contact ID
private static ArrayList<PhoneName> getPhonesFromID(Context context,
String contactID, String column) {
// Run query
Uri uri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI;
String[] projection = new String[] {
ContactsContract.CommonDataKinds.Phone._ID,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID,
ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME,
ContactsContract.CommonDataKinds.Phone.NUMBER,
ContactsContract.CommonDataKinds.Phone.STARRED,
ContactsContract.CommonDataKinds.Phone.TYPE };
String selection = column + " = '" + contactID + "'";
String[] selectionArgs = null;
String sortOrder = ContactsContract.CommonDataKinds.Phone.NUMBER
+ " COLLATE LOCALIZED ASC";
Cursor cursor = context.getContentResolver().query(uri, projection,
selection, selectionArgs, sortOrder);
if (cursor == null)
return null;
int numberIndex = cursor
.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
int nameIndex = cursor
.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME);
int typeIndex = cursor
.getColumnIndex(ContactsContract.CommonDataKinds.Phone.TYPE);
int favIndex = cursor
.getColumnIndex(ContactsContract.CommonDataKinds.Phone.STARRED);
ArrayList<PhoneName> phones = new ArrayList<PhoneName>();
while (cursor.moveToNext()) {
PhoneName pn = new PhoneName();
pn.name = cursor.getString(nameIndex);
pn.number = cursor.getString(numberIndex);
pn.starred = cursor.getString(favIndex);
pn.state = DataHelper.getInstance(context)
.getSubscriptionStateByNumber(pn.number);
Log.d("Number", pn.number);
int type = cursor.getInt(typeIndex);
if (type == ContactsContract.CommonDataKinds.Phone.TYPE_HOME) {
pn.type = "Home";
} else if (type == ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE) {
pn.type = "Mobile";
} else if (type == ContactsContract.CommonDataKinds.Phone.TYPE_WORK) {
pn.type = "work";
} else {
pn.type = "other";
}
phones.add(pn);
}
cursor.close();
return phones;
}
Here's PhoneName is my self-defined class to store contact information of that specific contact. This function is called like:
getPhonesFromID(context, _id,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID);
I'm getting the contact information correctly but here's the problem, sometimes this function returns null when I make frequent queries. When this problem occurs, default people application of my android don't show any contact too. All contacts are like vanished. Then if I close my application and restart then it shows all contacts like before. Then again when I go to the contact details of a contact from my application it works fine several times and then all contacts are gone again.
This is a weird problem and I don't know actually what is happening here. I double checked my queries and couldn't figure out what's wrong in here.
Finally, this problem got solved.
Like I said, my application is like Phonebook and I had to show a favourite icon for each contact in the list which is my favourite contact. In my case, I had a function like isFavourite() to determine if a contact is in my favourite list or not and this is called in every single time when a contact row is loading.
Each time I open a cursor to get the query result and it was found that the cursor was not closed anywhere. So basically, closing the cursor properly solved the problem! Cheers!
Related
I'm currently using the answer to this question: How to get all contacts first name, last name, email, phone number, etc without duplicates to retrieve the user contact list with, for each contact, all phone numbers, the firstname, lastname and the photo id.
The issue I've with that answer is it's creating one request per data wanted. It's creating performances issues on my app. I would like to retrieve all these information from a single request, but I've got a lot of difficulty to understand how Android's query are managed and where are stored data I'm looking for.
Example of what I've tried (I've done a lot of test without success, following one is the last one I've tried):
new CursorLoader(
this,
ContactsContract.Data.CONTENT_URI,
null,
ContactsContract.Data.HAS_PHONE_NUMBER + "!=0 AND (" + ContactsContract.Data.MIMETYPE + "=?)",
new String[]{ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE},
ContactsContract.Data.CONTACT_ID)
;
And information I would like to retrieve:
int firstNameCol = cursor.getColumnIndex(CommonDataKinds.StructuredName.GIVEN_NAME);
int lastNameCol = cursor.getColumnIndex(CommonDataKinds.StructuredName.FAMILY_NAME);
int photoIdCol = cursor.getColumnIndex(CommonDataKinds.StructuredName.PHOTO_ID);
int phoneCol = cursor.getColumnIndex(Phone.NUMBER);
There is two issues with that request:
I only get one phone number per contact, however some of my contacts have several numbers and I would like to retrieve them
I'm not retrieving the firstname and the lastname (I get a weird number instead)
Please note that I don't want to retrieve the DISPLAY_NAME, I want both given_name and family_name. Please also note I would like to have one result per contact and not one result per phone number (i.e one result contain one to many phone numbers)
Any idea to do that in a single request?
EDIT: trying with another piece of code from #pskink but I get a "column '_id' does not exists" error
String[] projection = {
ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME,
ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME,
ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME,
ContactsContract.CommonDataKinds.StructuredName.PHOTO_ID
};
String selection = ContactsContract.Data.MIMETYPE + " in (?, ?, ?, ?)";
String[] selectionArgs = {
ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME,
ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME,
ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME,
ContactsContract.CommonDataKinds.StructuredName.PHOTO_ID
};
//String sortOrder = ContactsContract.Contacts.SORT_KEY_ALTERNATIVE;
//Uri uri = ContactsContract.CommonDataKinds.Contactables.CONTENT_URI;
Uri uri = ContactsContract.Data.CONTENT_URI;
// ok, let's work...
return new CursorLoader(
this, uri, projection, selection, selectionArgs, null
);
Thanks to #pskink, I've now a fast single request to retrieve what I want. Unfortunately, I cannot use the CursorAdapter and I've to request the whole DB and store result in an object model, but the result is very fast so it's OK.
Here's the complete code:
public ArrayList<ContactModel> retrieveContactList(){
ArrayList<ContactModel> list = new ArrayList<>();
LongSparseArray<ContactModel> array = new LongSparseArray<>();
Set<String> set = new HashSet<String>();
set.add(ContactsContract.Data.MIMETYPE);
set.add(ContactsContract.Data.CONTACT_ID);
set.add(ContactsContract.Data.PHOTO_ID);
set.add(ContactsContract.CommonDataKinds.Phone.NUMBER);
set.add(ContactsContract.CommonDataKinds.Phone.TYPE);
set.add(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME);
set.add(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME);
set.add(ContactsContract.Contacts.PHOTO_ID);
Uri uri = ContactsContract.Data.CONTENT_URI;
String[] projection = set.toArray(new String[0]);
String selection = ContactsContract.Data.MIMETYPE + " in (?, ?)";
String[] selectionArgs = {
ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE,
ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE
};
String sortOrder = ContactsContract.Contacts.SORT_KEY_ALTERNATIVE;
Cursor cursor = this.context.getContentResolver().query(
uri,
projection,
selection,
selectionArgs,
sortOrder
);
final int mimeTypeIdx = cursor.getColumnIndex(ContactsContract.Data.MIMETYPE);
final int idIdx = cursor.getColumnIndex(ContactsContract.Data.CONTACT_ID);
final int phoneIdx = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
final int phoneTypeIdx = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.TYPE);
final int givenNameIdx = cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME);
final int familyNameIdx = cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME);
final int photoIdIdx = cursor.getColumnIndex(ContactsContract.Data.PHOTO_ID);
while (cursor.moveToNext()) {
long id = cursor.getLong(idIdx);
ContactModel addressBookContact = array.get(id);
if (addressBookContact == null) {
addressBookContact = new ContactModel(id);
array.put(id, addressBookContact);
list.add(addressBookContact);
}
Long photoId = cursor.getLong(photoIdIdx);
if (photoId != null){
addressBookContact.addPhotoId(photoId);
}
switch (cursor.getString(mimeTypeIdx)) {
case ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE:
// row's data: see ContactsContract.CommonDataKinds.Phone
addressBookContact.addPhone(cursor.getInt(phoneTypeIdx), cursor.getString(phoneIdx));
break;
case ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE:
// row's data: see ContactsContract.CommonDataKinds.StructuredName
addressBookContact.addName(cursor.getString(givenNameIdx), cursor.getString(familyNameIdx));
break;
}
}
cursor.close();
return list;
}
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 am trying to get contact version for each contacts in my phone,
but some contacts return contact-version null in 'kitkat'.
private String getContactVersion(String id)
{
Uri uri = ContactsContract.RawContacts.CONTENT_URI;
String[] projection = new String[] { ContactsContract.RawContacts._ID, ContactsContract.RawContacts.VERSION };
String selection = ContactsContract.RawContacts._ID + " = '" + id + "'";
Cursor cursor = context.getContentResolver().query(uri, projection, selection, null, null);
cursor.moveToFirst();
int columnVersion = cursor.getColumnIndex(ContactsContract.RawContacts.VERSION);
String version = cursor.getString(columnVersion);
cursor.close();
return version;
}
According to the official documentation: http://developer.android.com/reference/android/provider/ContactsContract.RawContacts.html
Version number that is updated whenever this row or its related data changes. This field can be used for optimistic locking of a raw contact.
So it can be null actually. "null" means the contact has not been changed.
Additionally "version" column is not an integer. You should get it like below
int columnVersion = cursor.getColumnIndex(ContactsContract.RawContacts.VERSION);
String version = cursor.getInt(columnVersion);
I am trying to find out if the caller id is in the contacts list. So the code I am using for that:
ContentResolver resolver = this.service.getContentResolver();
String number = PhoneLookup.NUMBER;
String[] projections = { number };
String selectionClause = number + " = ?";
String[] selectionClauseArgs = { callerId };
Cursor people = resolver.query(
ContactsContract.Contacts.CONTENT_URI, projections,
selectionClause, selectionClauseArgs, null);
return people.getCount() > 0 ? true : false;
I am giving the filtering functionality to the ContentResolver itself instead of fetching everything, iterating over them and checking one by one.
But I receive the following error for something reason (There are many codes but they are working just fine, since they are just subscribing to the telephony events in order to receive caller id when some one is calling!")
08-28 19:20:05.903: E/AndroidRuntime(737): java.lang.IllegalArgumentException: Invalid column number
I don't what is invalid. I am using right column name which is PhoneLookup.NUMBER constant. A similar question was asked here before: How to read contacts on Android 2.0 which I followed.
There's no phone numbers in this table. You should query it from another place.
Here's fast scratch (I belive there's better way to do it):
boolean hasPhoneNumber = (people.getInt(people.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER)) > 0);
if (hasPhoneNumber) {
String id = people.getString(people.getColumnIndex(ContactsContract.Contacts._ID));
String num = getNumber(id);
}
public String getNumber(String id) {
String number = null;
String name = null;
Cursor pCur = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?", new String[] { id }, null);
if (pCur.moveToFirst()) {
number = pCur.getString(pCur.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
name = pCur.getString(pCur.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
}
pCur.close();
return number;
}
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...:)