I'm trying to get frequently called list. CONTENT_STREQUENT_URI gives you starred and frequently and i want just frequently.
Cursor c = this.getContentResolver (). query (Contacts.CONTENT_STREQUENT_URI,
null, null, null,null);
This may help you in getting frequent/starred contacts that you are searching for. I wrote the getPhoneNumbers method out for you as an example of how to pull the information out of the cursor. Also, note that if you remove the contacts from your device/ unstar them, this will not automatically update your ui. You will need to resync your data. To do this, there are a few ways. In my opinion, if you just refetch this data again it won't be too bad, as you probably don't have too many starred contacts. But for bigger input size, you will want to paginate this list and find a more elegant way than fetching this data every time.
public static List<? extends ContactsModel> getFreqContacts(Context context) {
List<ContactsModel> contacts = new ArrayList<>();
ContentResolver contentResolver = context.getContentResolver();
Cursor cursor = contentResolver.query(ContactsContract.Contacts.CONTENT_URI, null, "starred=?", new String[]{"1"}, "upper(" + ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME + ") ASC");
if(cursor.getCount() > 0) {
while (cursor.moveToNext()) {
final String name = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
final String contact_id = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID));
final Set<String> phone_numbers = getContactPhoneNumber(contentResolver, cursor, contact_id);
final Set<String> emails = getContactEmailAddress(contentResolver, contact_id);
// do whatever you want. Now you have the phone numbers and emails of the contacts you want
}
}
cursor.close();
Collections.sort(faveContactsModels);
return faveContactsModels;
}
private static Set<String> getContactPhoneNumber(ContentResolver contentResolver, Cursor cursor,String id) {
Set<String> contactNumbers = new HashSet<>();
if(Integer.parseInt(cursor.getString(
cursor.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER))) > 0
) {
Cursor pCur = contentResolver.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));
contactNumbers.add(phoneNo);
}
pCur.close();
}
return contactNumbers;
}
Related
I am trying to fetch contacts from the phonebook in my Android application. But it fetches the contacts that are present only in the local phone storage. I need to fetch all the contacts including the ones synced to the device using various accounts like Google. That is currently not happening. I am using a RecyclerView to display the contacts fetched.
I have tried using https://github.com/mirrajabi/rx-contacts2 library for fetching asynchronously. But that doesn't include Google contacts as well. Then I tried using Android's built-in CotentResolver
Contact contact;
ContentResolver contentResolver = getContentResolver();
Cursor cursor = contentResolver.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME + " ASC");
if (cursor != null) {
if (cursor.getCount() > 0) {
while (cursor.moveToNext()) {
int hasPhoneNumber = Integer.parseInt(cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER)));
if (hasPhoneNumber > 0) {
String id = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID));
String name = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
contact = new Contact(Long.parseLong(id));
contact.setDisplayName(name);
Cursor phoneCursor = contentResolver.query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?",
new String[]{id},
null);
if (phoneCursor != null) {
if (phoneCursor.moveToNext()) {
String phoneNumber = phoneCursor.getString(phoneCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
Set<String> phoneNumbers = new HashSet<>();
phoneNumbers.add(phoneNumber);
contact.setPhoneNumbers(phoneNumbers);
}
phoneCursor.close();
}
Cursor emailCursor = contentResolver.query(
ContactsContract.CommonDataKinds.Email.CONTENT_URI,
null,
ContactsContract.CommonDataKinds.Email.CONTACT_ID + " = ?",
new String[]{id}, null);
if (emailCursor != null) {
while (emailCursor.moveToNext()) {
String emailId = emailCursor.getString(emailCursor.getColumnIndex(ContactsContract.CommonDataKinds.Email.DATA));
}
emailCursor.close();
}
listContacts.add(contact);
}
}
}
cursor.close();
}
Currently, I am trying to fetch the contacts synchronously and it hangs up the main thread. It would be really helpful if you could suggest some ways to do that asynchronously. When doing so I also require a trigger to know when the task is completed.
Your code should work on all contacts synced to the device, including Google contacts (assuming the Google account is installed, and the contacts sync is enabled).
However, your code has some bugs, and can be greatly improved, currently for a device with 500 contacts, you are doing ~1000 queries.
All the data you need is on a single table called Data so you can get everything in a single quick query, see here:
Map<Long, Contact> contacts = new HashMap<>();
String[] projection = {Data.CONTACT_ID, Data.DISPLAY_NAME, Data.MIMETYPE, Data.DATA1};
String selection = Data.MIMETYPE + " IN ('" + Phone.CONTENT_ITEM_TYPE + "', '" + Email.CONTENT_ITEM_TYPE + "')";
Cursor cur = cr.query(Data.CONTENT_URI, projection, selection, null, null);
while (cur.moveToNext()) {
long id = cur.getLong(0);
String name = cur.getString(1);
String mime = cur.getString(2); // email / phone
String data = cur.getString(3); // the actual info, e.g. +1-212-555-1234
// get the Contact class from the HashMap, or create a new one and add it to the Hash
Contact contact;
if (contacts.containsKey(id)) {
contact = contacts.get(id);
} else {
contact = new Contact(id);
contact.setDisplayName(name);
// start with empty Sets for phones and emails
contact.setPhoneNumbers(new HashSet<>());
contact.setEmails(new HashSet<>());
contacts.put(id, contact);
}
switch (mime) {
case Phone.CONTENT_ITEM_TYPE:
contact.getPhoneNumbers().add(data);
break;
case Email.CONTENT_ITEM_TYPE:
contact.getEmails().add(data);
break;
}
}
cur.close();
Notes:
I've changed your listContacts to a HashMap called contacts so we can quickly find an existing contact
I've added setEmails, getPhoneNumbers and getEmails to your Contact class
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.
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...:)
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");
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);
}
}
`