I have one existing contact, I need to add a work address to that existing contact. I am using the following code, but it's not working.
String selectPhone = Data.CONTACT_ID + "=? AND " + Data.MIMETYPE + "='" +
ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE +
"'" + " AND " + ContactsContract.CommonDataKinds.StructuredPostal.TYPE + "=?";
String[] phoneArgs = new String[]
{String.valueOf(ContactId), String.valueOf(
ContactsContract.CommonDataKinds.StructuredPostal.TYPE_WORK)};
ops.add(ContentProviderOperation.newUpdate(Data.CONTENT_URI)
.withSelection(selectPhone, phoneArgs)
.withValue(ContactsContract.CommonDataKinds.StructuredPostal.STREET, STREET)
.withValue(ContactsContract.CommonDataKinds.StructuredPostal.CITY, CITY)
.withValue(ContactsContract.CommonDataKinds.StructuredPostal.REGION, REGION)
.withValue(ContactsContract.CommonDataKinds.StructuredPostal.POSTCODE, POSTCODE)
.withValue(ContactsContract.CommonDataKinds.StructuredPostal.COUNTRY, COUNTRY)
.build());
this.context.getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);
Any solution for this?
/**
* #param name name of the contact
* #param number mobile phone number of contact
* #param email work email address of contact
* #param ContactId id of the contact which you want to update
* #return true if contact is updated successfully<br/>
* false if contact is not updated <br/>
* false if phone number contains any characters(It should contain only digits)<br/>
* false if email Address is invalid <br/><br/>
*
* You can pass any one among the 3 parameters to update a contact.Passing all three parameters as <b>null</b> will not update the contact
* <br/><br/><b>Note: </b>This method requires permission <b>android.permission.WRITE_CONTACTS</b><br/>
*/
public boolean updateContact(String name, String number, String email,String ContactId)
{
boolean success = true;
String phnumexp = "^[0-9]*$";
try
{
name = name.trim();
email = email.trim();
number = number.trim();
if(name.equals("")&&number.equals("")&&email.equals(""))
{
success = false;
}
else if((!number.equals(""))&& (!match(number,phnumexp)) )
{
success = false;
}
else if( (!email.equals("")) && (!isEmailValid(email)) )
{
success = false;
}
else
{
ContentResolver contentResolver = activity.getContentResolver();
String where = ContactsContract.Data.CONTACT_ID + " = ? AND " + ContactsContract.Data.MIMETYPE + " = ?";
String[] emailParams = new String[]{ContactId, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE};
String[] nameParams = new String[]{ContactId, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE};
String[] numberParams = new String[]{ContactId, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE};
ArrayList<android.content.ContentProviderOperation> ops = new ArrayList<android.content.ContentProviderOperation>();
if(!email.equals(""))
{
ops.add(android.content.ContentProviderOperation.newUpdate(android.provider.ContactsContract.Data.CONTENT_URI)
.withSelection(where,emailParams)
.withValue(Email.DATA, email)
.build());
}
if(!name.equals(""))
{
ops.add(android.content.ContentProviderOperation.newUpdate(android.provider.ContactsContract.Data.CONTENT_URI)
.withSelection(where,nameParams)
.withValue(StructuredName.DISPLAY_NAME, name)
.build());
}
if(!number.equals(""))
{
ops.add(android.content.ContentProviderOperation.newUpdate(android.provider.ContactsContract.Data.CONTENT_URI)
.withSelection(where,numberParams)
.withValue(Phone.NUMBER, number)
.build());
}
contentResolver.applyBatch(ContactsContract.AUTHORITY, ops);
}
}
catch (Exception e)
{
e.printStackTrace();
success = false;
}
return success;
}
// To get COntact Ids of all contact use the below method
/**
* #return arraylist containing id's of all contacts <br/>
* empty arraylist if no contacts exist <br/><br/>
* <b>Note: </b>This method requires permission <b>android.permission.READ_CONTACTS</b>
*/
public ArrayList<String> getAllConactIds()
{
ArrayList<String> contactList = new ArrayList<String>();
Cursor cursor = activity.managedQuery(ContactsContract.Contacts.CONTENT_URI, null, null, null, "display_name ASC");
if (cursor != null)
{
if (cursor.moveToFirst())
{
do
{
int _id = cursor.getInt(cursor.getColumnIndex("_id"));
contactList.add(""+_id);
}
while(cursor.moveToNext());
}
}
return contactList;
}
private boolean isEmailValid(String email)
{
String emailAddress = email.toString().trim();
if (emailAddress == null)
return false;
else if (emailAddress.equals(""))
return false;
else if (emailAddress.length() <= 6)
return false;
else {
String expression = "^[a-z][a-z|0-9|]*([_][a-z|0-9]+)*([.][a-z|0-9]+([_][a-z|0-9]+)*)?#[a-z][a-z|0-9|]*\\.([a-z][a-z|0-9]*(\\.[a-z][a-z|0-9]*)?)$";
CharSequence inputStr = emailAddress;
Pattern pattern = Pattern.compile(expression,
Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(inputStr);
if (matcher.matches())
return true;
else
return false;
}
}
private boolean match(String stringToCompare,String regularExpression)
{
boolean success = false;
Pattern pattern = Pattern.compile(regularExpression);
Matcher matcher = pattern.matcher(stringToCompare);
if(matcher.matches())
success =true;
return success;
}
//Sorry for my bad english
// It seems that in the first post you forgot to add the MimeType in operation.
String selectPhone = Data.RAW_CONTACT_ID + "=? AND " + Data.MIMETYPE + "='" +
ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE + "'" ;
String[] phoneArgs = new String[]{String.valueOf(rawContactId)};
ops.add(ContentProviderOperation.newUpdate(Data.CONTENT_URI)
.withSelection(selectPhone, phoneArgs)
.withValue(ContactsContract.CommonDataKinds.StructuredPostal.TYPE, ContactsContract.CommonDataKinds.StructuredPostal.TYPE_WORK)
.withValue(ContactsContract.CommonDataKinds.StructuredPostal.STREET, STREET)
.withValue(ContactsContract.CommonDataKinds.StructuredPostal.CITY, CITY)
.withValue(ContactsContract.CommonDataKinds.StructuredPostal.REGION, REGION)
.withValue(ContactsContract.CommonDataKinds.StructuredPostal.POSTCODE, POSTCODE)
.withValue(ContactsContract.CommonDataKinds.StructuredPostal.COUNTRY, POSTCODE)
**
//Just add this line .withValue(Data.MIMETYPE,
"vnd.android.cursor.item/postal-address_v2")
**
.build());
this.context.getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);
Please check this and let me know the result
Finally I found the appropriate solution..Much thanks to this How to modify existing Contact
The secret is that you have to pass two values for .withSelection as shown below:
.withSelection(Data.RAW_CONTACT_ID + " = ?", new String[] {String.valueOf(id)})
.withSelection(Data._ID + " = ?", new String[] {mDataId})
where by Data._ID value mDataId is obtained this way:
Cursor mDataCursor = this.context.getContentResolver().query(
Data.CONTENT_URI,
null,
Data.RAW_CONTACT_ID + " = ? AND " + Data.MIMETYPE + " = ?",
new String[] { String.valueOf(id), ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE},
null);
if(mDataCursor.getCount() > 0) {
mDataCursor.moveToFirst();
mDataId = getCursorString(mDataCursor, Data._ID);
MLog.v("Data", "Found data item with MIMETYPE");
mDataCursor.close();
} else {
MLog.v("Data", "Data doesn't contain MIMETYPE");
result = ERROR;
mDataCursor.close();
}
And getCursorString method is something like:
private static String getCursorString(Cursor cursor, String columnName) {
int index = cursor.getColumnIndex(columnName);
if(index != -1) return cursor.getString(index);
return null;
}
This and only this is the trick..
Each field (email, name, adreess) has its own mime type, which you should use in order to update the field.
We will work with Data table, where each Data.RAW_CONTACT_ID represents a detail about some contact.
So, we need to find the Data.RAW_CONTACT_ID where the id is the id of the contact you want to edit.
I hope this code should be helpful to you.
String selectPhone = Data.RAW_CONTACT_ID + "=? AND " + Data.MIMETYPE + "='" +
ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE + "'" ;
String[] phoneArgs = new String[]{String.valueOf(rawContactId)};
ops.add(ContentProviderOperation.newUpdate(Data.CONTENT_URI)
.withSelection(selectPhone, phoneArgs)
.withValue(ContactsContract.CommonDataKinds.StructuredPostal.TYPE, ContactsContract.CommonDataKinds.StructuredPostal.TYPE_WORK)
.withValue(ContactsContract.CommonDataKinds.StructuredPostal.STREET, STREET)
.withValue(ContactsContract.CommonDataKinds.StructuredPostal.CITY, CITY)
.withValue(ContactsContract.CommonDataKinds.StructuredPostal.REGION, REGION)
.withValue(ContactsContract.CommonDataKinds.StructuredPostal.POSTCODE, POSTCODE)
.withValue(ContactsContract.CommonDataKinds.StructuredPostal.COUNTRY, POSTCODE)
.build());
this.context.getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);
If a new contact has been created, but without address, and now you want to add an address to that contcat. In this case use the same query as above, but just change newUpdate to newInsert, since such row isn't exist yet.
you should use "Data.RAW_CONTACT_ID" instead of "Data.CONTACT_ID" in the clause of selection.
Maybe you could use Intent and its ACTION_EDIT to get your user Edit the work address...
Related
I am unable to get all contact ids from the RawContacts table
private void displayAllContactsByType(String accountName)
{//e.g accountName="WHATSAPP"
Cursor rawCursor = null;
rawCursor = cResolver.query(
ContactsContract.RawContacts.CONTENT_URI,
new String[]{ContactsContract.RawContacts.CONTACT_ID},
ContactsContract.RawContacts.ACCOUNT_NAME + "= ?",
new String[]{accountName},
ContactsContract.Contacts.DISPLAY_NAME_PRIMARY + " COLLATE LOCALIZED ASC");
rawCursor.moveToFirst();
int contactIdColumn = rawCursor.getColumnIndex(ContactsContract.RawContacts.CONTACT_ID);
int rawCursorCount = rawCursor.getCount();
int total = 1;
Utils.Log("Raw Size", " " + rawCursorCount);//rawCursorCount is correct here
while (rawCursor.moveToNext()) {
Long contactId = rawCursor.getLong(contactIdColumn);
publishProgress(((total * 100) / rawCursorCount));
progressBar.setProgressNumberFormat("" + total + "/" + rawCursorCount);
storeContactDetails(contactId, accountName);
++total;
//I am facing problem in this method only below code is just for understanding.
}
}
Contact ids are passed to the below method with account name to get the contact details from respective id but contact ids are less compare to the log //Utils.Log("Raw Size", " " + rawCursorCount).
private void storeContactDetails(Long id, String accountName) {
Cursor phones = null;
String[] projection = new String[]{ContactsContract.CommonDataKinds.Phone.CONTACT_ID,
ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME,
ContactsContract.CommonDataKinds.Phone.HAS_PHONE_NUMBER,
ContactsContract.CommonDataKinds.Phone.NUMBER,
ContactsContract.Contacts.LOOKUP_KEY,
"account_name",
Phone.TYPE
};
//Cursor c=cResolver.query(ContactsContract.Data.CONTENT_URI,projection,ContactsContract.Data.RAW_CONTACT_ID + " = ?",new String[]{String.valueOf(id)} ,null);
phones = cResolver.query(Phone.CONTENT_URI,
projection,
Phone.CONTACT_ID + " = ?",
new String[]{String.valueOf(id)},
null);
phones.moveToFirst();
getResultsFromPhoneCursor(phones, accountName);
}
public void getResultsFromPhoneCursor(Cursor phones, String accountName) {
int colorcounter = 0;
String[] colorcounter_array = {"#91A46B", "#8BB6B5", "#CAA973", "#8DA6C8", "#D19B8D"};
int color_string;
String email_Id = "";
String contactType = "";
try {
contactId = 0;
String hasPhone = "";
display_name = "";
phoneNumber = "";
contactId = phones.getLong(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.CONTACT_ID));
display_name = phones.getString(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)).trim();
hasPhone = phones.getString(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.HAS_PHONE_NUMBER));
if (hasPhone.equalsIgnoreCase("1"))
hasPhone = "true";
else
hasPhone = "false";
if (Boolean.parseBoolean(hasPhone)) {
do {
this.accountName = phones.getString(phones.getColumnIndex("account_name"));
phoneNumber = phones.getString(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
int type = phones.getInt(phones.getColumnIndex(Phone.TYPE));
switch (type) {
case Phone.TYPE_HOME:
contactType = "HOME";
break;
case Phone.TYPE_MOBILE:
contactType = "MOBILE";
break;
case Phone.TYPE_WORK:
contactType = "WORK";
break;
}
String lookupKey = phones.getString(phones.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY));
if (colorcounter < 5) {
color_string = Color.parseColor(colorcounter_array[colorcounter]);
colorcounter++;
} else {
colorcounter = 0;
color_string = Color.parseColor(colorcounter_array[colorcounter]);
colorcounter++;
}
Cursor emails = cResolver.query(ContactsContract.CommonDataKinds.Email.CONTENT_URI, new String[]{ContactsContract.CommonDataKinds.Email.DATA},
ContactsContract.CommonDataKinds.Email.CONTACT_ID + " = " + contactId, null, null);
while (emails.moveToNext()) {
email_Id = emails.getString(emails
.getColumnIndex(ContactsContract.CommonDataKinds.Email.DATA));
}
emails.close();
if (this.accountName.equalsIgnoreCase(accountName)) {
if (!contactList.contains(new ContactsWrapper(contactId, display_name, phoneNumber, lookupKey, false, color_string, email_Id, contactType)))
contactList.add(new ContactsWrapper(contactId, display_name, phoneNumber, lookupKey, false, color_string, email_Id, contactType));
}
}
while (phones.moveToNext());
phones.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
please help me to solve this case or suggest any other ways to get the contact_ids . Thanks in Advance.
The problem is that you are loosing the first contact you fetch from the db.
The statement rawCursor.moveToFirst(); positions the cursor on the first available result record. The, when you want to iterate over the results, you call rawCursor.moveToNext() inside your loop. The loop condition is executed before it's body, so, you end up moving the cursor to the second row, loosing the first record.
You can fix this by getting rid of rawCursor.moveToFirst().
I am trying to edit existing phone contacts by using application. By using contact name am getting the corresponding contact id.Then am trying to update the existing contacts by using the contact id. But unfortunately it is not updating.
My code is as follows,
String select = "(" + ContactsContract.Contacts.DISPLAY_NAME + " == \"" + edt_nameDetail.getText() + "\" )";
Cursor c = getActivity().getApplicationContext().getContentResolver().query(ContactsContract.Contacts.CONTENT_URI, CONTACTS_SUMMARY_PROJECTION, select, null, ContactsContract.Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC");
getActivity().startManagingCursor(c);
if (c.moveToNext()) {
ContactId = c.getString(0);
Log.e("Tag contact id ","edit contact id "+ ContactId);
}
try
{
String name = edt_nameDetail.getText().toString().trim();
String email = edt_contactEmailDetail.getText().toString().trim();
String number = edt_mobileNumberDetail.getText().toString().trim();
ContentResolver contentResolver = getActivity().getContentResolver();
String where = ContactsContract.Data.CONTACT_ID + " = ? AND " + ContactsContract.Data.MIMETYPE + " = ?";
String[] emailParams = new String[]{ContactId, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE};
String[] nameParams = new String[]{ContactId, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE};
String[] numberParams = new String[]{ContactId, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE};
ArrayList<android.content.ContentProviderOperation> ops = new ArrayList<android.content.ContentProviderOperation>();
if(!email.equals("") &&!name.equals("")&& !number.equals(""))
{
ops.add(android.content.ContentProviderOperation.newUpdate(android.provider.ContactsContract.Data.CONTENT_URI)
.withSelection(where,emailParams)
.withValue(ContactsContract.CommonDataKinds.Email.DATA, email)
.build());
ops.add(android.content.ContentProviderOperation.newUpdate(android.provider.ContactsContract.Data.CONTENT_URI)
.withSelection(where,nameParams)
.withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, name)
.build());
ops.add(android.content.ContentProviderOperation.newUpdate(android.provider.ContactsContract.Data.CONTENT_URI)
.withSelection(where,numberParams)
.withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, number)
.build());
contentResolver.applyBatch(ContactsContract.AUTHORITY, ops);
Toast.makeText(getActivity(), "Contact is successfully edited", Toast.LENGTH_SHORT).show();
}
else {
Toast.makeText(getActivity(), "Fail edit", Toast.LENGTH_SHORT).show();
}
If you are searching with email then update your .withSelection as
.withSelection(ContactsContract.CommonDataKinds.Email.ADDRESS + "=? AND " +
Data.MIMETYPE + "='" +
ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE + "'",
new String[]{email})
update all your withselection code corresponding to your search type
Note: after this, contact ID will changed.
public static boolean updateContactName(String contactId, String pre, String first, String mid, String last, String suf)
{
try
{
if (pre == null && first == null && mid == null && last == null && suf == null)
return false;
String where = ContactsContract.Data.CONTACT_ID + " = ? AND " + ContactsContract.Data.MIMETYPE + " = ?";
String[] nameParams = new String[]{contactId, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE};
ArrayList<android.content.ContentProviderOperation> ops = new ArrayList<>();
android.content.ContentProviderOperation.Builder t ;
android.content.ContentProviderOperation b ;
t = android.content.ContentProviderOperation.newUpdate(Data.CONTENT_URI);
t = t.withSelection(where, nameParams);
if(pre != null)
t = t.withValue(StructuredName.PREFIX, pre.trim());
if(first != null)
t = t.withValue(StructuredName.GIVEN_NAME, first.trim());
if(mid != null)
t = t.withValue(StructuredName.MIDDLE_NAME, mid.trim());
if(last != null)
t = t.withValue(StructuredName.FAMILY_NAME, last.trim());
if(suf != null)
t = t.withValue(StructuredName.SUFFIX, suf.trim());
b = t.build();
ops.add(b);
ContentManager.getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);
return true;
}
catch (Exception e) {}
return false;
}
I'm trying to make my app update some contacts but it's updating the wrong people. I get the contact Name, Number and Raw Id. Then if it is true to my if I change his number and update it by his Raw ID. That's my code:
Cursor phones = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null,null,null, null);
while (phones.moveToNext())
{
String numero;
String fnum;
String phoneNumber = phones.getString(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
int id = phones.getInt(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.RAW_CONTACT_ID));
String tipo = phones.getString(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.TYPE));
phoneNumber = phoneNumber.replace(" ", "");
phoneNumber = phoneNumber.replace("-", "");
phoneNumber = phoneNumber.replace("+", "");
phoneNumber = phoneNumber.replace("*", "");
phoneNumber = phoneNumber.replace("#", "");
int tamanho = phoneNumber.length();
numero = phoneNumber;
if (tamanho == 12) {
if (checar.indexOf(numero.substring(1, 3) + ",") != -1) {
if(numero.substring(3).startsWith("9")){
fnum = numero.substring(0, 3) + "" + numero.substring(4);
update(id, fnum, tipo);
}
}
}
Thread.sleep(2000);
}
phones.close();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
And that's the update method:
public void update(int id, String number, String tipo)
{
ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
// Number
builder = ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI);
builder.withSelection(ContactsContract.Data.CONTACT_ID + "=?" + " AND " + ContactsContract.Data.MIMETYPE + "=?"+ " AND " + ContactsContract.CommonDataKinds.Organization.TYPE + "=?", new String[]{String.valueOf(id), ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE, tipo});
builder.withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, number);
ops.add(builder.build());
// Update
try
{
getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);
}
catch (Exception e)
{
e.printStackTrace();
}
}
What am I doing wrong?
I think that what's wrong is that you're retrieving RAW_CONTACT_ID when you get the number, but in update() you're passing this value to the column CONTACT_ID. The first one points to raw contacts, but the second one points to the Contacts table. Don't mix them!
I also notice that you're trying to update the ORGANIZATION column with the value of Phone.TYPE, which may or may not work.
In my project getting contacts is taking a long time to load.
What are ways to reduce the time of getting contacts
Assume there are 1000 contacts in my phone.
Right now it is taking more than 2 minutes to load all the contacts
How can I reduce the time to load contacts ?
Any Thoughts?
I referred to the the following link when programming the initial method.
http://www.coderzheaven.com/2011/06/13/get-all-details-from-contacts-in-android/
BETTER 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...:)
Total time will depend upon what fields you are trying to access from the Contacts table.
Accessing less field means less looping , less processing and hence faster results.
Also to speed up your contacts fetch operation you can use the ContentProvideClient instead of calling query on ContentResolver every time. This will make you query the specific table rather than querying first for the required ContentProvider and then to table.
Create an instance of ContentProviderClient
ContentResolver cResolver=context.getContextResolver();
ContentProviderClient mCProviderClient = cResolver.acquireContentProviderClient(ContactsContract.Contacts.CONTENT_URI);
Then reuse this mCProviderClient to get Contacts(data from any ContentProvider) data on your call.
For example in following method, I am accessing only one field.
private ArrayList<String> fetchContactsCProviderClient()
{
ArrayList<String> mContactList = null;
try
{
Cursor mCursor = mCProviderClient.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
if (mCursor != null && mCursor.getCount() > 0)
{
mContactList = new ArrayList<String>();
mCursor.moveToFirst();
while (!mCursor.isLast())
{
String displayName = mCursor.getString(mCursor.getColumnIndexOrThrow(ContactsContract.Contacts.DISPLAY_NAME));
mContactList.add(displayName);
mCursor.moveToNext();
}
if (mCursor.isLast())
{
String displayName = mCursor.getString(mCursor.getColumnIndexOrThrow(ContactsContract.Contacts.DISPLAY_NAME));
mContactList.add(displayName);
}
}
mCursor.close();
}
catch (RemoteException e)
{
e.printStackTrace();
mContactList = null;
}
catch (Exception e)
{
e.printStackTrace();
mContactList = null;
}
return mContactList;
}
Load Contact faster like other apps doing.
I have tested this code with multiple contacts its working fine and faster like other apps within 500 ms (within half second or less) I am able to load 1000+ contacts.
Total time will depend upon what fields you are trying to access from the Contacts table.
Mange your query according to your requirement do not access unwanted fields. Accessing less field means less looping , less processing and hence faster results.
Accessing right table in contact it also help to reduce contact loading time.
Query Optimization to load contact more faster use projection
String[] projection = {
ContactsContract.Data.MIMETYPE,
ContactsContract.Data.CONTACT_ID,
ContactsContract.Contacts.DISPLAY_NAME,
ContactsContract.Contacts.PHOTO_URI,
ContactsContract.Contacts.STARRED,
ContactsContract.RawContacts.ACCOUNT_TYPE,
ContactsContract.CommonDataKinds.Contactables.DATA,
ContactsContract.CommonDataKinds.Contactables.TYPE
};
Selection and selection argument
String selection = ContactsContract.Data.MIMETYPE + " in (?, ?)" + " AND " /*+ ContactsContract.Contacts.IN_VISIBLE_GROUP + " = '" + 1 + "' AND "*/ +
ContactsContract.Data.HAS_PHONE_NUMBER + " = '" + 1 + "'";
String[] selectionArgs = {
ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE,
ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE,
};
To order contacts alphabetically use following code
try {
Collections.sort(listview_address, new Comparator<ContactBook>() {
#Override
public int compare(ContactBook lhs, ContactBook rhs) {
return lhs.name.toUpperCase().compareTo(rhs.name.toUpperCase());
}
});
} catch (Exception e) {
e.printStackTrace();
}
Following is complete source code
public void initeContacts() {
List<ContactBook> listview_address = new LinkedList<ContactBook>();
SparseArray<ContactBook> addressbook_array = null;
{
addressbook_array = new SparseArray<ContactBook>();
long start = System.currentTimeMillis();
String[] projection = {
ContactsContract.Data.MIMETYPE,
ContactsContract.Data.CONTACT_ID,
ContactsContract.Contacts.DISPLAY_NAME,
ContactsContract.Contacts.PHOTO_URI,
ContactsContract.Contacts.STARRED,
ContactsContract.RawContacts.ACCOUNT_TYPE,
ContactsContract.CommonDataKinds.Contactables.DATA,
ContactsContract.CommonDataKinds.Contactables.TYPE
};
String selection = ContactsContract.Data.MIMETYPE + " in (?, ?)" + " AND " /*+ ContactsContract.Contacts.IN_VISIBLE_GROUP + " = '" + 1 + "' AND "*/ +
ContactsContract.Data.HAS_PHONE_NUMBER + " = '" + 1 + "'";
String[] selectionArgs = {
ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE,
ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE,
};
String sortOrder = ContactsContract.Contacts.SORT_KEY_ALTERNATIVE;
Uri uri = null;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR2) {
uri = ContactsContract.CommonDataKinds.Contactables.CONTENT_URI;
} else {
uri = ContactsContract.Data.CONTENT_URI;
}
// we could also use Uri uri = ContactsContract.Data.CONTENT_URI;
// we could also use Uri uri = ContactsContract.Contact.CONTENT_URI;
Cursor cursor = getActivity().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 nameIdx = cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME);
final int dataIdx = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Contactables.DATA);
final int photo = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Contactables.PHOTO_URI);
final int typeIdx = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Contactables.TYPE);
final int account_type = cursor.getColumnIndex(ContactsContract.RawContacts.ACCOUNT_TYPE);
while (cursor.moveToNext()) {
int contact_id = cursor.getInt(idIdx);
String photo_uri = cursor.getString(photo);
String contact_name = cursor.getString(nameIdx);
String contact_acc_type = cursor.getString(account_type);
int contact_type = cursor.getInt(typeIdx);
String contact_data = cursor.getString(dataIdx);
ContactBook contactBook = addressbook_array.get(contact_id);
/* if (contactBook == null) {
//list contact add to avoid duplication
//load All contacts fro device
//to add contacts number with name add one extra veriable in ContactBook as number and pass contact_data this give number to you (contact_data is PHONE NUMBER)
contactBook = new ContactBook(contact_id, contact_name, getResources(), photo_uri, contact_acc_type, "phone number");
addressbook_array.put(contact_id, contactBook);
listview_address.add(contactBook);
}*/
String Contact_mimeType = cursor.getString(mimeTypeIdx);
//here am checking Contact_mimeType to get mobile number asociated with perticular contact and email adderess asociated
if (Contact_mimeType.equals(ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)) {
if (contactBook != null) {
contactBook.addEmail(contact_type, contact_data);
}
} else {
if (contactBook == null) {
//list contact add to avoid duplication
//load All contacts fro device
//to add contacts number with name add one extra veriable in ContactBook as number and pass contact_data this give number to you (contact_data is PHONE NUMBER)
contactBook = new ContactBook(contact_id, contact_name, getResources(), photo_uri, contact_acc_type, "phone number");
addressbook_array.put(contact_id, contactBook);
listview_address.add(contactBook);
}
// contactBook.addPhone(contact_type, contact_data);
}
}
cursor.close();
try {
Collections.sort(listview_address, new Comparator<ContactBook>() {
#Override
public int compare(ContactBook lhs, ContactBook rhs) {
return lhs.name.toUpperCase().compareTo(rhs.name.toUpperCase());
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
You can use following code in above code that I have commented .It club the the single contact with its multiple number.To get all number associated with single contact use array in Object class.
if (contactBook == null) {
//irst contact add to avoid duplication
//load All contacts fro device
contactBook = new ContactBook(contact_id, contact_name, getResources(), photo_uri, contact_acc_type, "");
addressbook_array.put(contact_id, contactBook);
listview_address.add(contactBook);
}
String Contact_mimeType = cursor.getString(mimeTypeIdx);
//here am checking Contact_mimeType to get mobile number asociated with perticular contact and email adderess asociated
if (Contact_mimeType.equals(ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)) {
contactBook.addEmail(contact_type, contact_data);
} else {
contactBook.addPhone(contact_type, contact_data);
}
Object class
public class ContactBook {
public int id;
public Resources res;
public String name;
public String photo;
public String contact_acc_type;
public SparseArray<String> emails;
public SparseArray<String> phones;
/* public LongSparseArray<String> emails;
public LongSparseArray<String> phones;*/
public String header = "";
public ContactBook(int id, String name, Resources res, String photo, String contact_acc_type, String header) {
this.id = id;
this.name = name;
this.res = res;
this.photo = photo;
this.contact_acc_type = contact_acc_type;
this.header = header;
}
#Override
public String toString() {
return toString(false);
}
public String toString(boolean rich) {
//testing method to check ddata
SpannableStringBuilder builder = new SpannableStringBuilder();
if (rich) {
builder.append("id: ").append(Long.toString(id))
.append(", name: ").append("\u001b[1m").append(name).append("\u001b[0m");
} else {
builder.append(name);
}
if (phones != null) {
builder.append("\n\tphones: ");
for (int i = 0; i < phones.size(); i++) {
int type = (int) phones.keyAt(i);
builder.append(ContactsContract.CommonDataKinds.Phone.getTypeLabel(res, type, ""))
.append(": ")
.append(phones.valueAt(i));
if (i + 1 < phones.size()) {
builder.append(", ");
}
}
}
if (emails != null) {
builder.append("\n\temails: ");
for (int i = 0; i < emails.size(); i++) {
int type = (int) emails.keyAt(i);
builder.append(ContactsContract.CommonDataKinds.Email.getTypeLabel(res, type, ""))
.append(": ")
.append(emails.valueAt(i));
if (i + 1 < emails.size()) {
builder.append(", ");
}
}
}
return builder.toString();
}
public void addEmail(int type, String address) {
//this is the array in object class where i am storing contact all emails of perticular contact (single)
if (emails == null) {
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
emails = new SparseArray<String>();
emails.put(type, address);
/*} else {
//add emails to array below Jelly bean //use single array list
}*/
}
}
public void addPhone(int type, String number) {
//this is the array in object class where i am storing contact numbers of perticular contact
if (phones == null) {
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
phones = new SparseArray<String>();
phones.put(type, number);
/* } else {
//add emails to array below Jelly bean //use single array list
}*/
}
}}
For loading the contacts with mininum time the optimum solution is to use the concept of projection and selection argument while querying the cursor for contacts.
this can be done in following way
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");
}
With above method it took 400ms(less than second) to load contacts where as in normall way it was taking 10-12 sec.
For details imformation this post might help as i took help from it
http://www.blazin.in/2016/02/loading-contacts-fast-from-android.html
If your time increases with your data, then you are probably running a new query to fetch phones/emails for every contact. If you query for the phone/email field using ContactsContract.CommonDataKinds.Phone.NUMBER, then you will just retrieve 1 phone per contact.
The solution is to project the fields and join them by contact id.
Here is my solution in Kotlin (extracting id, name, all phones and emails):
val projection = arrayOf(
ContactsContract.Data.MIMETYPE,
ContactsContract.Data.CONTACT_ID,
ContactsContract.Contacts.DISPLAY_NAME,
ContactsContract.CommonDataKinds.Contactables.DATA
)
val selection = "${ContactsContract.Data.MIMETYPE} in (?, ?)"
val selectionArgs = arrayOf(
ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE,
ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
val contacts = applicationContext
.contentResolver
.query(ContactsContract.Data.CONTENT_URI, projection, selection, selectionArgs, null)
.run {
if (this == null) {
throw IllegalStateException("Cursor null")
}
val contactsById = mutableMapOf<String, LocalContact>()
val mimeTypeField = getColumnIndex(ContactsContract.Data.MIMETYPE)
val idField = getColumnIndex(ContactsContract.Data.CONTACT_ID)
val nameField = getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME)
val dataField = getColumnIndex(ContactsContract.CommonDataKinds.Contactables.DATA)
while (moveToNext()) {
val mimeType = getString(mimeTypeField)
val id = getString(idField)
var contact = contactsById[id]
if (contact == null) {
val name = getString(nameField)
contact = LocalContact(id = id, fullName = name, phoneNumbers = listOf(), emailAddresses = listOf())
}
val data = getString(dataField)
when(getString(mimeTypeField)) {
ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE ->
contact = contact.copy(emailAddresses = contact.emailAddresses + data)
ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE ->
contact = contact.copy(phoneNumbers = contact.phoneNumbers + data)
}
contactsById[id] = contact
}
close()
contactsById.values.toList()
}
And for reference, my LocalContact model:
data class LocalContact(
val id: String,
val fullName: String?,
val phoneNumbers: List<String>,
val emailAddresses: List<String>
)
I think this is a better solution:
public ContentValues getAllContacts() {
ContentValues contacts = new ContentValues();
ContentResolver cr = getContentResolver();
Cursor cur = cr.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
if (cur != null && 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 (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 != null) {
while (pCur.moveToNext()) {
String phoneNo = pCur.getString(pCur.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
contacts.put(phoneNo, name);
}
pCur.close();
}
}
}
cur.close();
}
return contacts;
}
for use it you need to call this lines once:
ContentValues contacts = new ContentValues();
contacts = getAllContacts();
and when you want to get contact name by number, just use:
String number = "12345";
String name = (String) G.contacts.get(number);
this algorithm is a bit faster...
Im using Android 2.1 Api
I need to add a String MIME type into existing contacts for storing a userdefined data apart from Phone numbers,email etc.Please help me how to add that Custom Field from my application.
I request with an example because I am a week old on Android.
Here is an example that saves a boolean as my custom mime type to the contacts. It uses the latest SDK 2.1
public static final String MIMETYPE_FORMALITY = "vnd.android.cursor.item/useformality";
public clsMyClass saveFormality() {
try {
ContentValues values = new ContentValues();
values.put(Data.DATA1, this.getFormality() ? "1" : "0");
int mod = ctx.getContentResolver().update(
Data.CONTENT_URI,
values,
Data.CONTACT_ID + "=" + this.getId() + " AND "
+ Data.MIMETYPE + "= '"
+ clsContacts.FORMALITY_MIMETYPE + "'", null);
if (mod == 0) {
values.put(Data.CONTACT_ID, this.getId());
values.put(Data.MIMETYPE, clsContacts.FORMALITY_MIMETYPE);
ctx.getContentResolver().insert(Data.CONTENT_URI, values);
}
} catch (Exception e) {
Log.v(TAG(), "saveFormality failed");
}
return this;
}
public boolean getFormality() {
if (data.containsKey(FORMALITY)) {
return data.getAsBoolean(FORMALITY);
} else {
// read formality
Cursor c = readDataWithMimeType(clsContacts.MIMETYPE_FORMALITY, this.getId());
if (c != null) {
try {
if (c.moveToFirst()) {
this.setFormality(c.getInt(0) == 1);
return (c.getInt(0) == 1);
}
} finally {
c.close();
}
}
return false;
}
}
public clsMyClass setFormality(Boolean value) {
data.remove(FORMALITY);
data.put(FORMALITY, value);
return this;
}
/**
* Utility method to read data with mime type
*
* #param mimetype String representation of the mimetype used for this type
* of data
* #param contactid String representation of the contact id
* #return
*/
private Cursor readDataWithMimeType(String mimetype, String contactid) {
return ctx.getContentResolver().query(
Data.CONTENT_URI,
new String[] {
Data.DATA1
},
Data.RAW_CONTACT_ID + "=" + contactid + " AND " + Data.MIMETYPE + "= '" + mimetype
+ "'", null, null);
}
Usage is
objContact.setFormality(true).saveFormality();