Android SDK - get contacts is very slow - android

a new app, I need to import all contacts with mobile phone,
I runs the code below in an AsyncTask class.
Its works fine, but very slow, on a device with 2000 contacts, the device freeze for few moments.
I know it can be done much faster since there are a lot of apps that use the contacts.
Any ideas?
public ArrayList<ContactInfo> getContacts() {
ArrayList<ContactInfo> arrayList = new ArrayList<ContactInfo>();
ContentResolver cr = GlobalData.instance().getContext().getContentResolver();
Cursor cur = cr.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, Phone.DISPLAY_NAME + " ASC");
String id;
String name;
int counter = 0;
if (cur.getCount() > 0) {
int indexId= cur.getColumnIndex(ContactsContract.Contacts._ID);
int indexName = cur.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME);
int indexHasPhoneNum = cur.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER);
Log.d("getContacts", "Start");
while (cur.moveToNext()) {
id = cur.getString(indexId);
name = cur.getString(indexName);
if (Integer.parseInt(cur.getString(indexHasPhoneNum)) > 0) {
// Query phone here. Covered next
Cursor phones = cr.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = " + id, null, null);
while (phones.moveToNext()) {
int type = phones.getInt(phones.getColumnIndex(Phone.TYPE));
if(type == Phone.TYPE_MOBILE){
String phoneNumber = phones.getString(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
arrayList.add(new ContactInfo(name, phoneNumber));
counter++;
}
}
phones.close();
}
}
Log.d("getContacts", "End (" + counter + ")" );
}
cur.close();
return arrayList;

After some search with other resources and some common sense, the answer to get all mobile phones in the device is to query the ContactsContract.CommonDataKinds.Phone.CONTENT_URI instead of the ContactsContract.Data.CONTENT_URI and ran the cursor on that.

Related

Select one contact number from a list of multiple contact numbers

I want to fetch only the first contact number from a list of contact numbers of a particular user, with the help of cursor. Here is my code:
private ArrayList<ArrayList<String>> getAllContacts() {
ArrayList<ArrayList<String>> nameList = new ArrayList<ArrayList<String>>();
ArrayList<String> person=new ArrayList<>();
ArrayList<String> number=new ArrayList<>();
ArrayList<String> temp=new ArrayList<>();
ContentResolver cr = getContentResolver();
Cursor cur = cr.query(ContactsContract.Contacts.CONTENT_URI,
null, null, null, null);
if ((cur!=null ? cur.getCount() : 0) > 0) {
while (cur!=null && cur.moveToNext()) {
String id = cur.getString(
cur.getColumnIndex(ContactsContract.Contacts._ID));
String name = cur.getString(cur.getColumnIndex(
ContactsContract.Contacts.DISPLAY_NAME));
person.add(name);
if (cur.getInt(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);
if(pCur.getCount()==1) {
while (pCur.moveToNext()) {
String phoneNo = pCur.getString(pCur.getColumnIndex(
ContactsContract.CommonDataKinds.Phone.NUMBER));
number.add(phoneNo);
}
}
else{
while (pCur.moveToNext()) {
String phoneNo = pCur.getString(pCur.getColumnIndex(
ContactsContract.CommonDataKinds.Phone.NUMBER));
temp.add(phoneNo);
}
number.add(temp.get(0));
temp.clear();
}
pCur.close();
}
}
}
if (cur!=null) {
cur.close();
}
Log.d("contacts",String.valueOf(number.size())+" "+String.valueOf(person.size())); //the lists aren't of equal size
if(person.size()==number.size()){
nameList.add(person);
nameList.add(number);
}
else{
//don't know what to do here
}
return nameList;
}
But, the code still fetches multiple contact numbers saved for a single user, in other words person.size() is not equal to number.size(). What do I do?
The array sizes are not the same because not all contacts have phone-numbers, you are correctly checking for HAS_PHONE_NUMBER and only if true, getting that contact's phone numbers - which means number.size() would be < person.size() on most phones.
I would suggest instead of keeping separate Arrays for names and phones, having a single Array with a simple java class that represents a person.
Other than that, your code is very inefficient, as you're doing a ton of queries, while you can do just one.
Here's an example with the two suggestions above:
class Person {
long id,
String name,
String firstPhone;
public Person(id, name, firstPhone) {
this.id = id;
this.name = name;
this.firstPhone = firstPhone;
}
}
Map<Long, Person> mapping = new HashMap<>(); // mapping between a contact-id to a Person object
String[] projection = {Data.CONTACT_ID, Data.DISPLAY_NAME, Phone.NUMBER};
// query phones only
String selection = Data.MIMETYPE + "='" + Phone.CONTENT_ITEM_TYPE + "'";
ContentResolver cr = getContentResolver();
Cursor cur = cr.query(Data.CONTENT_URI, projection, selection, null, null);
while (cur != null && cur.moveToNext()) {
long id = cur.getLong(0);
String name = cur.getString(1); // full name
String phone = cur.getString(2); // phone
Log.d(TAG, "got " + id + ", " + name + " - " + data);
// only add a new object if we haven't seen this person before
if (!mapping.containsKey(id)) {
Person person = new Person(id, name, phone);
mapping.put(id, person);
}
}
cur.close();
Array<Person> people = mapping.values();
EDIT
Set<Long> ids = new HashSet<Long>();
Array<String> names = new ArrayList<String>();
Array<String> numbers = new ArrayList<String>();
String[] projection = {Data.CONTACT_ID, Data.DISPLAY_NAME, Phone.NUMBER};
// query phones only
String selection = Data.MIMETYPE + "='" + Phone.CONTENT_ITEM_TYPE + "'";
ContentResolver cr = getContentResolver();
Cursor cur = cr.query(Data.CONTENT_URI, projection, selection, null, null);
while (cur != null && cur.moveToNext()) {
long id = cur.getLong(0);
String name = cur.getString(1); // full name
String phone = cur.getString(2); // phone
Log.d(TAG, "got " + id + ", " + name + " - " + data);
// only add a new object if we haven't seen this person before
if (ids.add(id)) {
names.add(name);
numbers.add(phone);
}
}
cur.close();

Method to return contacts phone number always returns null - Android [duplicate]

I am able to retrieve the contact ID, but then later I wish to separately retrieve the phone number based on the contact ID. The code below is returning a null result for the phone number. (I do wish later to retrieve the name and phone number together and populate a view, but I am just trying to get the phone number to work first).
In my onCreate I have this code
String phoneNum = getPhoneNumber(myID);
TextView phoneTextView = (TextView) findViewById(R.id.textViewPhone);
phoneTextView.setText(phoneNum);
This is the method for getPhoneNumber()
protected String getPhoneNumber(String id) {
ArrayList<String> phones = new ArrayList<String>();
Cursor cursor = getContentResolver().query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?",
new String[]{id}, null);
while (cursor.moveToNext()) {
phones.add(cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)));
}
cursor.close();
String phoneNum;
phoneNum = phones.get(0);
return phoneNum;
}//end getPhoneNumber();
}
This produces the error java.lang.IndexOutOfBoundsException: Invalid index 0, size is 0, which I plan on creating some error handling for. But still, I am certain I have the ID from the previous code, so I don't know why the ArrayList returns null. If you would like to see that code, it is also in my onCreate:
Cursor cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null);
if (cursor.getCount() != 0) {
int numContacts = cursor.getCount();
ArrayList<String> idList = new ArrayList<>();
Random rand = new Random();
int randomNum = rand.nextInt(numContacts);
while (cursor.moveToNext()) {
String id = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID));
idList.add(id);
}
myID = idList.get(randomNum);
String myString = Integer.toString(randomNum);
TextView myTextView = (TextView) findViewById(R.id.textViewID);
myTextView.setText(myString);
if (myID != null) {
myTextView.setText(myID);
} else {
myTextView.setText("Try Again!");
}
} else {
Toast.makeText(getApplicationContext(), "Your have no contacts.", Toast.LENGTH_SHORT).show();
}
cursor.close();
// You can fetch the Contact Number and Email With Following Methods.
String phone = getPhoneNumber(ContactId);
String email = getEmail("" + ContactId);
private String getPhoneNumber(long id) {
String phone = null;
Cursor phonesCursor = null;
phonesCursor = queryPhoneNumbers(id);
if (phonesCursor == null || phonesCursor.getCount() == 0) {
// No valid number
//signalError();
return null;
} else if (phonesCursor.getCount() == 1) {
// only one number, call it.
phone = phonesCursor.getString(phonesCursor
.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
} else {
phonesCursor.moveToPosition(-1);
while (phonesCursor.moveToNext()) {
// Found super primary, call it.
phone = phonesCursor.getString(phonesCursor
.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
break;
}
}
return phone;
}
private Cursor queryPhoneNumbers(long contactId) {
ContentResolver cr = getContentResolver();
Uri baseUri = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI,
contactId);
Uri dataUri = Uri.withAppendedPath(baseUri,
ContactsContract.Contacts.Data.CONTENT_DIRECTORY);
Cursor c = cr.query(dataUri, new String[]{ContactsContract.CommonDataKinds.Phone._ID, ContactsContract.CommonDataKinds.Phone.NUMBER,
ContactsContract.CommonDataKinds.Phone.IS_SUPER_PRIMARY, ContactsContract.RawContacts.ACCOUNT_TYPE,
ContactsContract.CommonDataKinds.Phone.TYPE,
ContactsContract.CommonDataKinds.Phone.LABEL},
ContactsContract.Data.MIMETYPE + "=?",
new String[]{ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE}, null);
if (c != null && c.moveToFirst()) {
return c;
}
return null;
}
private String getEmail(String id) {
String email = "";
ContentResolver cr = getContentResolver();
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 addresses
// if the email addresses were stored in an array
email = emailCur.getString(
emailCur.getColumnIndex(ContactsContract.CommonDataKinds.Email.DATA));
// String emailType = emailCur.getString(
// emailCur.getColumnIndex(ContactsContract.CommonDataKinds.Email.TYPE));
}
emailCur.close();
return email;
}
I have not been able to successfully retrieve code based on the Contact ID - but this post gave me a clue: Retrieving a phone number with ContactsContract in Android - function doesn't work
I have altered my original code to query on DISPLAY_NAME instead and things are working now. Here is the method I am using to retrieve the phone number:
private String retrieveContactNumber(String contactName) {
Log.d(TAG, "Contact Name: " + contactName);
Cursor cursorPhone = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
new String[]{ContactsContract.CommonDataKinds.Phone.NUMBER},
ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME + " = ? AND " +
ContactsContract.CommonDataKinds.Phone.TYPE + " = " +
ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE,
new String[]{contactName},
null);
if (cursorPhone.moveToFirst()) {
contactNumber = cursorPhone.getString(cursorPhone.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
}
cursorPhone.close();
Log.d(TAG, "Contact Phone Number: " + contactNumber);
return contactNumber;
}

context.getContentResolver().query(ContactsContract.Data.CONTENT_URI, null , null ,null, null);

Hi I am working in Android Contact search module.I am running below Query.
cur = context.getContentResolver().query(ContactsContract.Data.CONTENT_URI, null , null ,null, null);
from this query I am getting Result Multiple times.Is there any thing which I am doing wrong.I want DISTINCT Result Set.
please help me.
I think you mean you got duplicate record for some contacts. So you must add condition for your query
String selection = ContactsContract.Contacts.IN_VISIBLE_GROUP + " = '"
+ ("1") + "'";
String sortOrder = ContactsContract.Contacts.DISPLAY_NAME
+ " COLLATE LOCALIZED ASC";
cur = context.getContentResolver().query(
ContactsContract.Contacts.CONTENT_URI, projection, selection
+ " AND " + ContactsContract.Contacts.HAS_PHONE_NUMBER
+ "=1", null, sortOrder);// this query only return contacts which had phone number and not duplicated
Try this code will help you
public void getContact() {
Cursor cur = getContentResolver().query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
ContentResolver contect_resolver = getContentResolver();
int size = cur.getCount();
if (size > 0 && cur != null) {
for (int i = 0; i < size; i++) {
cur.moveToPosition(i);
String id = cur.getString(cur.getColumnIndexOrThrow(ContactsContract.Contacts._ID));
String name = "";
Cursor phoneCur = contect_resolver.query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null, ContactsContract.CommonDataKinds.Phone.CONTACT_ID
+ " = ?", new String[] { id }, null);
if (phoneCur.moveToFirst()) {
name = phoneCur.getString(phoneCur .getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
if (!name.equalsIgnoreCase("")) {
String id1 = phoneCur.getString(phoneCur
.getColumnIndex(ContactsContract.CommonDataKinds.Phone.CONTACT_ID));
Cursor emails = getContentResolver()
.query(ContactsContract.CommonDataKinds.Email.CONTENT_URI,
null,
ContactsContract.CommonDataKinds.Email.CONTACT_ID
+ " = " + Integer.parseInt(id1),
null, null);
emailAddress="";
if (emails!=null && emails.getCount() > 0) {
emails.moveToFirst();
emailAddress = emails
.getString(emails
.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DATA));
}
emails.close();
contact.setEmail(emailAddress);
id1 = "";
mcontact_arraylist.add(contact);
}
phoneCur.close();
}
}
cur.close();
}
}
Each record should contain a portion of the data for a contact (eg each phone number or address is a separate row) each row has a mimetype associated with it that is used to determine the data stored in each column. So for an address, the "data1" column holds the street data and data4 might hold the state.

Getting name and email from contact list is very slow

I'm implementing an AutoCompleteTextView and I need Name and E-Mail of all my contacts.
I found this snippet that I'm running asynchronously but it's very slow.
ContentResolver cr = 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));
Cursor emailCur = cr.query(ContactsContract.CommonDataKinds.Email.CONTENT_URI, null, ContactsContract.CommonDataKinds.Email.CONTACT_ID + " = ?", new String[]{id}, null);
while (emailCur.moveToNext()) {
String email = emailCur.getString(emailCur.getColumnIndex(ContactsContract.CommonDataKinds.Email.DATA));
autoCompleteAdapter.add(name + " - " + email);
}
emailCur.close();
}
}
}
I'm performing a sort of inner query and I think that's the problem. Is there a way to tune it and make it faster?
private static final String[] PROJECTION = new String[] {
ContactsContract.CommonDataKinds.Email.CONTACT_ID,
ContactsContract.Contacts.DISPLAY_NAME,
ContactsContract.CommonDataKinds.Email.DATA
};
...
ContentResolver cr = getContentResolver();
Cursor cursor = cr.query(ContactsContract.CommonDataKinds.Email.CONTENT_URI, PROJECTION, null, null, null);
if (cursor != null) {
try {
final int contactIdIndex = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Email.CONTACT_ID);
final int displayNameIndex = cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME);
final int emailIndex = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Email.DATA);
long contactId;
String displayName, address;
while (cursor.moveToNext()) {
contactId = cursor.getLong(contactIdIndex);
displayName = cursor.getString(displayNameIndex);
address = cursor.getString(emailIndex);
...
}
} finally {
cursor.close();
}
}
few notes:
use just ContactsContract.CommonDataKinds.Email.CONTENT_URI to get information you need, see ContactsContract.CommonDataKinds.Email for information what columns you can query
use projection to get only those columns you really need, you save some memory and increase query performance
get column indexes only once, just before the while cycle
You should not query directly the ContactsContract.Contacts
Make just one query on the ContactsContract.CommonDataKinds with the email data kind.
The ContactsContract.CommonDataKinds.Email inherits a lot of other interfaces that you can use to build your projection. (see inherited constants from the documentation)
For example :
import android.provider.ContactsContract.CommonDataKinds.Email;
[...]
public static final String[] EMAILS_PROJECTION = new String[] {
Email._ID,
Email.DISPLAY_NAME_PRIMARY,
Email.ADDRESS
};
to be used with the
Email.CONTENT_URI
You can retrieve a lot of information (such as user id, user display name ...) directly from the email data kind.
EDIT:
I just realized you're trying to build an AutoCompleteTextView.
You should override the runQueryOnBackgroundThread method and the convertToString of your CursorAdapter and use the Email.CONTENT_FILTER_URI
I really strongly suggest you to take a look at the ApiDemo samples.
Especially the AutoComplete4.java sample that you can find HERE.
ContentResolver cr = mContext.getContentResolver();
Cursor cursor = mContext.getContentResolver().query(ContactsContract.Contacts.CONTENT_URI, PROJECTION, "HAS_PHONE_NUMBER <> 0", null, null);
if (cursor!= null)
{
final int displayNameIndex = cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME);
final int numberIndex = cursor.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER);
final int idIndex= cursor.getColumnIndex(ContactsContract.Contacts._ID);
String displayName, number = null, idValue;
while (cursor.moveToNext())
{
displayName = cursor.getString(displayNameIndex);
idValue= cursor.getString(idIndex);
Cursor phones = mContext.getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, "contact_id = '" + idValue + "'", null, null);
phones.moveToFirst();
try
{
number = phones.getString(phones.getColumnIndex("data1"));
}
catch (CursorIndexOutOfBoundsException e)
{
}
phones.close();
userList.add(new ContactModel(displayName, number, null));
}
}

Android query phonenumber to obtain rawcontactID

I would like to query on phonenumber to obtain the rawcontactID.
The only thing I know of the contact is the given phonenumber, but for my function I need to have the rawcontactID. I got a working code but now I did use 2 seperate queries. What I would like to have is 1 query that can do both just to save some query time.
my code:
Uri uri = Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, Uri.encode(phoneNumber));
String[] columns = new String[]{Phone.CONTACT_ID, Phone.DISPLAY_NAME, Phone.NUMBER, Phone._ID };
Cursor cursor = contentResolver.query(uri, columns, null, null, null);
if(cursor!=null) {
int clenght = cursor.getCount();
while(cursor.moveToNext()){
//contactName = cursor.getString(cursor.getColumnIndexOrThrow(PhoneLookup.DISPLAY_NAME));
id = cursor.getString(cursor.getColumnIndex(Phone.CONTACT_ID));
}
cursor.close();
}
Cursor pCur = contentResolver.query(ContactsContract.Data.CONTENT_URI, new String[]{ContactsContract.Data.RAW_CONTACT_ID}, ContactsContract.Data.CONTACT_ID+" = "+ id, null, null);
if(pCur!=null) {
int clenght = pCur.getCount();
while(pCur.moveToNext()){
//contactName = cursor.getString(cursor.getColumnIndexOrThrow(PhoneLookup.DISPLAY_NAME));
id = pCur.getString(pCur.getColumnIndex(ContactsContract.Data.RAW_CONTACT_ID));
}
pCur.close();
}
thanks in advance
Edit:
My code above works fine, but I am still looking for increasing speed for large number of contacts. Therefore I will give a bounty if someone comes with a solution to combine my queries.
private String[] getRawContactIdFromNumber(String givenNumber){
List<String> rawIds = new ArrayList<String>();
Cursor phones = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, new String[]{ContactsContract.CommonDataKinds.Phone.RAW_CONTACT_ID},ContactsContract.CommonDataKinds.Phone.NUMBER + "='"+ givenNumber +"'",null, ContactsContract.CommonDataKinds.Phone.NUMBER);
while (phones.moveToNext())
{
rawIds.add( phones.getString(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.RAW_CONTACT_ID)));
Log.v("contacts","Given Number: " + givenNumber + "Raw ID: " +rawIds.get(rawIds.size() - 1));
}
phones.close();
String[] ret = new String[0];
return rawIds.toArray(ret);
}
Edited to only include the raw id in the cursor for efficiency. Also changed return type to array in case multiple contacts have the same number.
Please try
String phonenumber = "input your phone number";
Cursor pCur = getContentResolver().query(
ContactsContract.Data.CONTENT_URI,
new String[] { ContactsContract.Data.RAW_CONTACT_ID,
Phone.CONTACT_ID }, Phone.NUMBER + " = " + phonenumber,
null, null);
if (pCur != null) {
while (pCur.moveToNext()) {
String contactID = pCur.getString(pCur
.getColumnIndex(Phone.CONTACT_ID));
String Rowid = pCur.getString(pCur
.getColumnIndex(ContactsContract.Data.RAW_CONTACT_ID));
Log.e("RAW_CONTACT_ID", Rowid);
Log.e("CONTACT_ID", contactID);
}
pCur.close();
}
Now you can get Both CONTACT_ID & RAW_CONTACT_ID in single query.

Categories

Resources