I am very new android developer and i'm trying to fill a autocompletetextview with contact display names, and when a suggestion is clicked i want it to autofill the email field from that contact. I managed to get it working, but my problem is speed. Because I need to get the email address of all contacts, I need to do a query for every single contact, which takes a very long time when I have about 3000 contacts on the device i'm testing on. The thing is, most of the contacts don't even have an email address, but I still need to do a query to discover that. I am getting the contacts via an async task. Here is the
doInBackground of the AsyncTask:
protected Object[] doInBackground(ContentResolver...cr) {
try{
List<String> names = new ArrayList<String>();
Map<Integer, String> emails = new HashMap<Integer, String>();
Map <Integer, List<String>> contacts = new HashMap<Integer, List<String>>();
/*********** Reading Contacts Name **********/
//Query to get contact name
Cursor cur = cr[0]
.query(ContactsContract.Contacts.CONTENT_URI,
null,
null,
null,
null);
// If data data found in contacts
if (cur.getCount() > 0) {
Log.i("AutocompleteContacts", "Reading contacts........");
String name = "";
String id = "";
while (cur.moveToNext())
{
name = cur.getString(cur.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
id = cur.getString(cur.getColumnIndex(ContactsContract.Contacts._ID));
String email = "";
if(name != null)
{
names.add(name.toString());
}
Cursor cur1 = cr[0].query(ContactsContract.CommonDataKinds.Email.CONTENT_URI, null,ContactsContract.CommonDataKinds.Email.CONTACT_ID + " = ?", new String[]{id}, null);
if(cur1.getCount()>0)
{
while(cur1.moveToNext())
{
email = cur1.getString(cur1.getColumnIndex(ContactsContract.CommonDataKinds.Email.DATA));
if(email != null)
{
emails.put(Integer.parseInt(id), email);
}
}
}
cur1.close();
List<String> line = new ArrayList<String>();
line.add(name);
line.add(email);
contacts.put(Integer.parseInt(id), line);
} // End while loop
} // End Cursor value check
else
{
Log.i("contacts", "No contacts found");
}
cur.close();
results[0] = names;
results[1] = contacts;
results[2] = emails;
} catch (NullPointerException e) {
Log.i("AutocompleteContacts","Exception : "+ e);
}
return results;
}
Basically my question is: Is there a way to check if the contact has an email without another query like HAS_EMAIL_ADDRESS column or something.
If you want to view the full source of my app: https://github.com/michael-elgavi/perlib
After thinking for a while a realized I only need the email of the contact that you click on, so I just removed the email code from the asynctask and made another asynctask that loads an email of a specific contact. Then I get the email of the contact that was clicked and fill the email field. Code can be found in the repository I linked to in the question.
Related
I have sorted and listed my phone contacts in to an arraylist but ,i got many duplicates of same contact names in the list .How this happens? how to avoid this?
This is what i have tried,
cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null,
"(" + ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME + ") ASC");
while (cursor.moveToNext()) {
try {
name = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
phonenumber = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
contact_names_list.add(name);
phone_num_list.add(phonenumber);
} catch (Exception e) {
e.printStackTrace();
}
can anyone help??
No one here seems to answer your question.
The reason you're seeing duplicate contacts is that you're querying for phones not contacts.
In Android there are 3 main tables:
Contacts table - has one item per contact
RawContacts table - has one item per-contact per-account (such as Google, Outlook, Whatsapp, etc.) - multiple RawContacts are linked to a single Contact
Data table - has one item per detail (name, email, phone, address, etc.) - each data item is linked to a single RawContact, and multiple Data rows are linked to each RawContact.
You're querying on CommonDataKinds.Phone.CONTENT_URI which is a part of the Data table, so if a contact has more then one phone, and/or it has the same phone from multiple sources (e.g. Google and Whatsapp) you'll get the same phone with the same CONTACT_ID more then once.
The solution would be, to use a HashMap (rather then a HashSet), where the key is CONTACT_ID, so you can display multiple phones per contact:
String[] projection = new String[] { CommonDataKinds.Phone.CONTACT_ID, CommonDataKinds.Phone.DISPLAY_NAME, CommonDataKinds.Phone.NUMBER };
Cursor cursor = getContentResolver().query(CommonDataKinds.Phone.CONTENT_URI, projection, null, null, null);
HashMap<Long, Contact> contacts = new HashMap<>();
while (cursor.moveToNext()) {
long id = cursor.getLong(0);
String name = cursor.getString(1);
String phone = cursor.getString(2);
Contact c = contacts.get(id);
if (c == null) {
// newly found contact, add to Map
c = new Contact();
c.name = name;
contacts.put(id, c);
}
// add phone to contact class
c.phones.add(phone);
}
cursor.close();
// simple class to store multiple phones per contact
private class Contact {
public String name;
// use can use a HashSet here to avoid duplicate phones per contact
public List<String> phones = new ArrayList<>();
}
If you want to sort your HashMap by name:
List<Contact> values = new ArrayList<>(contacts.values());
Collections.sort(values, new Comparator<Contact> {
public int compare(Contact a, Contact b) {
return a.name.compareTo(b.name);
}
});
// iterate the sorted list, per contact:
for (Contact contact : values) {
Log.i(TAG, "contact " + contact.name + ": ");
// iterate the list of phones within each contact:
for (String phone : contact.phones) {
Log.i(TAG, "\t phone: " + phone);
}
}
You can try with HashSet.
public class HashSet extends AbstractSet implements Set,
Cloneable, Serializable
Duplicate values are not allowed.
Code Structure
HashSet<String> hashSET = new HashSet<String>();
hashSET.add("AA");
hashSET.add("BB");
hashSET.add("CC");
hashSET.add("AA"); // Adding duplicate elements
Then
Iterator<String> j = hashSET.iterator();
while (j.hasNext())
System.out.println(j.next()); // Will print "AA" once.
}
Now SORT your Hashset Values using TreeSet.
TreeSet implements the SortedSet interface so duplicate values are not
allowed.
TreeSet<String> _treeSET= new TreeSet<String>(hashSET);
May be in your contacts having multiple groups, and that group will be a WhatsApp,Google etc..Go to your contacts and search that contact having whatsApp account. will showing double entry with different Group
you should use or change your ContactsBean , in your Bean use HashSet
Note: HashSet can avoid duplicate entry more
HashSet contains unique elements only,it can avoid same key element form HashSet
Example bean
public class ContactBean {
private HashSet<String> number = new HashSet<String>();
public void setNumber(String number) {
if (number == null)
return;
this.number.add(number.trim());
}
public HashSet<String> getNumber() {
return this.number;
}
}
Simple Example
//Creating HashSet and adding elements
HashSet<String> hashSet=new HashSet<String>();
hashSet.add("Dhruv");
hashSet.add("Akash");
hashSet.add("Dhruv"); //Avoiding this entry
hashSet.add("Nirmal");
//Traversing elements
Iterator<String> itr = hashSet.iterator();
while(itr.hasNext()){
System.out.println(itr.next());
}
You can use HashSet for avoid duplication:-
HashSet<String> hset =
new HashSet<String>();
you can add like ArrayList in HashSet:-
hset.add(your_string);
OR
Convert your ArrayList to HashSet :-
Set<String> set = new HashSet<String>(your_arraylist_object);
HashSet avoid Duplicate Entry :)
I don't know why are you getting duplicate items from contacts, maybe phone contacts already have duplicate values.You can check that in Contacts app.
You should always use set data structure wherever you want to avoid duplicate items. You can find the better explanation and example here.
I think your duplication is because of Whatsapp contact interfering with contact . so you can use something like this
String lastPhoneName = "";
String lastPhoneNumber = "";
//IN YOUR CONTACT FETCHING WHILE LOOP , INSIDE TRY
String contactName = c.getString(c
.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
String phNumber = c
.getString(c
.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
if (!contactName.equalsIgnoreCase(lastPhoneName) && !phNumber.equalsIgnoreCase(lastPhoneNumber)) {
lastPhoneName = contactName;
lastPhoneNumber = phNumber;
ContactModel model = new ContactModel();
model.setContact_id(contactid);
model.setContact_name(contactName.replaceAll("\\s+", ""));
model.setContact_number(phNumber.replaceAll("\\D+", ""));
list_contact_model.add(model);
}
this will check that previous number is same as old one than skip it . I hope You get your answer
HashSet add items in key/value pair and also remove duplicate entry from item set.
List<String> phone_num_list= new ArrayList<>();
// add elements to phone_num_list, including duplicates
Set<String> hs = new HashSet<>();
hs.addAll(phone_num_list);
phone_num_list.clear();
phone_num_list.addAll(hs);
Happy coding!!
private void displayAllContactsByType(String accountName) {
Cursor rawCursor = null;
try {
rawCursor = mCProviderClient.query(
ContactsContract.RawContacts.CONTENT_URI,
null,
ContactsContract.RawContacts.ACCOUNT_NAME + "= ?",
new String[]{accountName},
null);
} catch (RemoteException e) {
e.printStackTrace();
}
int contactIdColumn = rawCursor.getColumnIndex(ContactsContract.RawContacts.CONTACT_ID);
Utils.Log("Raw Size", " " + rawCursor.getCount());
while (rawCursor.moveToNext()) {
String contactId = rawCursor.getString(contactIdColumn);
// Utils.Log("contactId ",contactId);
storeContactDetails(contactId);
}
if (contactList != null) {
contactListener = (ContactListener) context;
contactListener.updateView(contactList);
}
}
I am getting all the account name which raw contacts contain. And I would like to get all the contacts belongs to the particular account name but I am getting null contact id. How can I get all the contact id to a particular account name and then respective data of that contact id along with it?
First, RawContacts.CONTACT_ID is of type Long, not String, so you should do:
Long contactId = rawCursor.getLong(contactIdColumn);
See: https://developer.android.com/reference/android/provider/ContactsContract.RawContactsColumns.html#CONTACT_ID don't pay attention to the type of the CONTACT_ID field which are always Strings, look at Type: INTEGER below it.
Second, this is rare, but can happen when a RawContact does not belong to any Contact, I call those zombie RawContacts, they can be created either via a corrupt DB or a bug, or intentionally using AGGREGATION_MODE when creating the RawContact, see AGGREGATION_MODE_DISABLED
I'm trying to retrieve data from the Contact provider but the data shows seems not query correctly. The code I use is just below:
mCursor = mContext.getContentResolver().query(
ContactsContract.Data.CONTENT_URI, null, null, null, null);
if(mCursor == null){
mListener.onRetrieveError();
return;
}
for (mCursor.moveToFirst(); !mCursor.isAfterLast(); mCursor.moveToNext()) {
String contact_f = mCursor.getString(mCursor
.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME));
String contact_m = mCursor.getString(mCursor
.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME));
String contact_l = mCursor.getString(mCursor
.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME));
String phone_type = mCursor.getString(mCursor
.getColumnIndex(ContactsContract.CommonDataKinds.Phone.TYPE));
String email_type = mCursor.getString(mCursor
.getColumnIndex(ContactsContract.CommonDataKinds.Email.TYPE));
Log.d(TAG, "|" + contact_l + ", " + contact_f + " " + contact_m + " | " + phone_type + " | " + email_type);
}
The Log shows for example :
Log: |LastName, FirstName | FirstName | FirstName
Log: |null, 3 null | 3 | 3
I was expecting :
Log: |LastName, FirstName MiddleName | PhoneType| EmailType
I want to be able to get the firstname, lastname, middle name, email type (home/work...) - and all email types listed and also the phone type.
The goal is to for example get for a contact his lastname, firstname middle name and also saying that we got is home and work phone or email
The result I got seems a mix.
Any ideas.
Regards
That's not how it works. The Data table contains one row per contact value field (phone number, email address, structured name ...). Each row has a mimetype value that tells you how to interpret the columns of that row.
For example, if the mimetype column has the value Phone.CONTENT_ITEM_TYPE you must use the Phone column mapping to access the data.
So if a contact has one phone number and one email address there are at least 3 rows for this contact in the Data table. One for the StructuredName (this one is required exactly once for each RawContact), one for the Phone number and one for the Email address.
Each row also has a RAW_CONTACT_ID that contains the row id of the RawContact the value belongs to.
When you query the Data table, you actually read from a view that joins the real Data table with the RawContacts table, so each row also contains the CONTACT_ID of the contact the row belongs to. That means if two rows have the same CONTACT_ID they belong to the same contact.
I hope that explains it. I don't really know what you're trying to achieve in the end, so I can't give any helpful code snippets.
Update
The key to this is to check the mimetype of each row before you decide how to interpret the values. Also you need to read the CONTACT_ID or RAW_CONTACT_ID to be able to aggregate the data correctly. If you order the result by CONTACT_ID you can assume that all rows belonging to a contact have been read when the CONTACT_ID value changes.
The (untested) code snippet below should point you to the right direction. The point is, you can not get all the data of a contact in a single row. You always need to read multiple rows and aggregate them in some way.
mCursor = mContext.getContentResolver().query(
ContactsContract.Data.CONTENT_URI, null, null, null, ContactsContract.Data.CONTACT_ID);
if(mCursor == null){
mListener.onRetrieveError();
return;
}
colIdxContactId = mCursor.getColumnIndex(ContactsContract.Data.CONTACT_ID);
colIdxMimetype = mCursor.getColumnIndex(ContactsContract.Data.MIMETYPE);
long lastContactId = -1L;
for (mCursor.moveToFirst(); !mCursor.isAfterLast(); mCursor.moveToNext())
// this is the contact Id the current row belongs to
long contactId = mCursor.getLong(colIdxContactId);
if (lastContactId != contactId) {
// the previous contact is complete, the following data belong to a new contact
// handle this case ...
lastContactId = contactId;
}
// the mimetype column tells us how to interpret the current row:
switch(mCursor.getString(colIdxMimetype)) {
case ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE: {
// this row is a structured name
String contact_f = mCursor.getString(mCursor
.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME));
String contact_m = mCursor.getString(mCursor
.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME));
String contact_l = mCursor.getString(mCursor
.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME));
// store names somewhere ...
break;
}
case ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE: {
// this row represents a phone number
int phone_type = mCursor.getInt(mCursor
.getColumnIndex(ContactsContract.CommonDataKinds.Phone.TYPE));
// store phone_type somewhere ...
break;
}
case ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE: {
// this row represents an email address
int email_type = mCursor.getInt(mCursor
.getColumnIndex(ContactsContract.CommonDataKinds.Email.TYPE));
// store email_type somewhere ...
break;
}
}
}
You can use this
public void getAllContacts(){
Cursor cursor = null;
ContactPerson contact;
try {
cursor = getActivity().getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null);
int nameIdx = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME);
int phoneNumberIdx = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
int emailAddressIdx = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Email.DATA);
cursor.moveToFirst();
do {
contact = new ContactPerson(cursor.getString(nameIdx),cursor.getString(phoneNumberIdx),cursor.getString(emailAddressIdx));
cpList.add(contact);
// Toast.makeText(getActivity(),cursor.getString(nameIdx)+" "+ cursor.getString(phoneNumberIdx),Toast.LENGTH_SHORT).show();
} while (cursor.moveToNext());
} catch (Exception e) {
e.printStackTrace();
Toast.makeText(getActivity(),e.getMessage(), Toast.LENGTH_LONG).show();
Log.d("Error",e.getMessage());
} finally {
if (cursor != null) {
cursor.close();
}
}
}
I think the URI you are using is wrong.
Try using ContactsContract.Contacts.CONTENT_URI instead of ContactsContract.Data.CONTENT_URI (this is what I use).
In my app, when launched I would like to display all the contacts with search filter option. I have achieved this but the problem is - while loading the contacts, it takes more time. How can I improve the efficiency of the code ? Below code snippet shows how I am retreiving contacts:
public ContactList newContactList(Context ctx)
{
ContactList contacts = new ContactList();
String id = "";
String sortOrder = ContactsContract.Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC";
try
{
this.cur = this.cr.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, sortOrder);
if (this.cur.getCount() > 0) {
while (cur.moveToNext()) {
Contact c = new Contact();
id = cur.getString(cur.getColumnIndex(ContactsContract.Contacts._ID));
if (Integer.parseInt(cur.getString(cur.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER))) > 0) {
c.setId(id);
c.setDisplayName(cur.getString(cur.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME)));
c.setPhone(this.getPhoneNumbers(ctx, id));
c.setEmail(this.getEmailAddresses(id));
c.setNotes(this.getContactNotes(id));
c.setAddresses(this.getContactAddresses(id));
c.setImAddresses(this.getIM(id));
c.setOrganization(this.getContactOrg(id));
//c.setPicture(loadContactPhoto(cr, cur.getLong(cur.getColumnIndex(CommonDataKinds.Photo.CONTACT_ID))));
contacts.addContact(c);
}
}
}
}
catch(Exception e)
{
throw new IllegalStateException(e);
}
finally
{
cur.close();
}
return(contacts);
}
ContactList is a class that returns ArrayList of all contacts, and with this arraylist I am setting it to my custom adapter class. I guess, because of fetching arraylist by itereating through cursor is taking much time. But, I would need this arraylist to filter the contacts based on search criteria and to display the custom contact list. Is there any way that I can improve the performance of the code?
Why don't you use CursorAdapter ?
http://developer.android.com/reference/android/widget/CursorAdapter.html
I have successfully implemented a search in Android to find a contact's name by voice recognition. Now I would like to find the number by voice recognition.
I tried already this How to call Android contacts list?, but it doesn't work with my SGS2. I think the source is old version. That's why it doesn't work.
How can I find the number by name which is result from voice recognition in Android Contacts?
private void getContactData() {
Cursor phoneCursor = null;
contactList = new HashMap<String,String>();
try{
// 주소록이 저장된 URI
Uri uContactsUri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI;
// 주소록의 이름과 전화번호의 열 이름
String strProjection = ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME;
// 주소록을 얻기 위한 쿼리문을 날리고 커서를 리턴
phoneCursor = getContentResolver().query(uContactsUri, null, null, null, strProjection);
phoneCursor.moveToFirst();
String name = "";
String phoneNumber = "";
// 주소록의 이름
int nameColumn = phoneCursor.getColumnIndex(Phone.DISPLAY_NAME);
// 주소록의 전화번호
int phoneColumn = phoneCursor.getColumnIndex(Phone.NUMBER);
while(!phoneCursor.isAfterLast()){
name = phoneCursor.getString(nameColumn);
phoneNumber = phoneCursor.getString(phoneColumn);
// HashMap에 data 넣음
contactList.put(name, phoneNumber);
phoneCursor.moveToNext();
}
}
catch(Exception e){
Log.e("[SmsMain] getContactData", e.toString());
}
finally{
if(phoneCursor != null){
phoneCursor.close();
phoneCursor = null;
}
}
}
This is the source to get a contact DB.
I solved by searching.