I am making a Android application which uses Contacts. The good thing is I managed somehow to make it work with Contacts.Phones as seen on many tutorials. The problem is that Contacts.Phones is deprecated and is replaced by ContactsContract. My application needs to work starting from Android 1.5+.
I need to do some simple operations like:
- query all contacts
- query for a specific contact
- backup all contacts
What is the best way to achieve this, considering I need to have the application working on all versions of android. Do I need to check for current api level on the phone and have 2 code blocks, one before api 5 one after ?
Here is an optional solution
int apiVersion = android.os.Build.VERSION.SDK_INT;
if(apiVersion < 5) {
ContentResolver cr = getContentResolver();
Cursor cur = cr.query(People.CONTENT_URI,
null, null, null, null);
if (cur.getCount() > 0) {
while (cur.moveToNext()) {
String id = cur.getString(cur.getColumnIndex(People._ID));
String name = cur.getString(cur.getColumnIndex(People.DISPLAY_NAME));
}
}
} else {
String columns[] = new String[]{ ContactsContract.Contacts._ID,
ContactsContract.Contacts.DISPLAY_NAME };
Cursor cursor = getContentResolver().query(ContactsContract.Contacts.CONTENT_URI,
columns,
null,
null,
ContactsContract.Data.DISPLAY_NAME + " COLLATE LOCALIZED ASC");
if (cursor.getCount() > 0) {
while (cursor.moveToNext()) {
long id = Long.parseLong(cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID)));
String displayName = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME)).trim();
}
}
}
And here an tutorial to make application Supporting the old and new APIs in the same application this must help you.
Using ContentResolver. Try this code:
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));
if (Integer.parseInt(cur.getString(cur.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER))) > 0) {
//Query phone here. Covered next
}
}
}
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 retrieve >10000 contacts from the android device. To fetch that much contact it takes about 8-10 min. Is there any other possible way to do this. I have implemented a method its working fine but when it comes to large number of contacts it taking it time to fetch the contacts.
ContentResolver cr = getActivity().getApplication().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));
if (Integer.parseInt(cur.getString(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);
while (pCur.moveToNext()) {
int phoneType = pCur.getInt(pCur.getColumnIndex(
ContactsContract.CommonDataKinds.Phone.TYPE));
String phoneNumber = pCur.getString(pCur.getColumnIndex(
ContactsContract.CommonDataKinds.Phone.NUMBER));
phoneNumber = phoneNumber.replace(" ","");
phoneNumber = phoneNumber.replace("-","");
boolean addNumber = stringCheck(phoneNumber,symbols);
if (!addNumber){
if (phoneNumber.length() == 10){
addContact(phoneNumber,phoneType,name);
}else if (phoneNumber.length() == 11){
phoneNumber = phoneNumber.substring(1);
addContact(phoneNumber,phoneType,name);
}else if (phoneNumber.length() == 12){
phoneNumber = phoneNumber.substring(2);
addContact(phoneNumber,phoneType,name);
}else if (phoneNumber.length() == 13){
phoneNumber = phoneNumber.substring(3);
addContact(phoneNumber,phoneType,name);
}
}
}
pCur.close();
}
}
}
If 900 out of the 1000 contacts have phone numbers, you're currently querying the DB 901 times, you can reduce it to only two queries:
Give me all contacts information
Give me all phone numbers
Then you use the contact-id field on both to match the phone to the right contacts.
Also, as noted in other answers, you really should add projection to all your queries to improve performance.
Another improvement you can make is to avoid runtime cur.getColumnIndex() calls, if you have a projection, you should already know the index - so use it hard-coded
Map<Long, List<String>> phones = new HashMap<>();
ContentResolver cr = getActivity().getApplication().getContentResolver();
// First build a mapping: contact-id > list of phones
Cursor cur = cr.query(Phone.CONTENT_URI, new String[] { Phone.CONTACT_ID, Phone.Number }, null, null, null);
while (cur != null && cur.moveToNext()) {
long contactId = cur.getLong(0);
String phone = cur.getString(1);
List list;
if (phones.contains(contactId)) {
list = phones.get(contactId);
} else {
list = new ArrayList<String>();
phones.put(contactId, list);
}
list.add(phone);
}
cur.close();
// Next query for all contacts, and use the phones mapping
cur = cr.query(Contacts.CONTENT_URI, new String[] { Contacts._ID, Contacts.DISPLAY_NAME }, null, null, null);
while (cur != null && cur.moveToNext()) {
long id = cur.getLong(0);
String name = cur.getString(1);
List<String> contactPhones = phones.get(id);
addContact(id, name, contactPhones);
}
Keep your fetching process in doInBackground method of AyncTask and then display it. And get only those info which is required first, eg, Contact Name and ID.
refer this ans for more clarification:
Fetching a large number of contacts
In order to fetch large number of contacts you must be fetching the contacts in the form of pages by using rest API.
For example page size of contacts is 100.
One possible approach:
If you are showing the contacts in the listview then you can fetch the data while user is scrolling the list. For that You have to set the threshold position of the list i.e. 80, So when user reaches at 80th position while scrolling you can hit your rest API for next page to fetch the new contacts and add those contacts in the listview.
From your comments i can see you want all the contacts in order to move forward.For this you can start an intent service when app is launched. This service will fetch all the contacts and store them. You can use them as per your needs
Here some optimization for speed up
String[] select = new String[]{ContactsContract.Contacts._ID, ContactsContract.Contacts.DISPLAY_NAME, ContactsContract.Contacts.HAS_PHONE_NUMBER};
Cursor cur = cr.query(ContactsContract.Contacts.CONTENT_URI, select, null, null, null);
String[] selectOnly = new String[]{ContactsContract.CommonDataKinds.Phone.NUMBER, ContactsContract.CommonDataKinds.Phone.TYPE};
Cursor pCur = cr.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
selectOnly,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID +" = ?",
new String[]{id}, null);
Do your number check, length check in server side if possible. Do it in Async task.
I want to filter what numbers I am getting from Android based on which contacts the user chose to display in his contact book. (For example only contacts saved on the device when all other contacts are excluded)
I read here that you can do this by using
Uri queryUri = ContactsContract.Contacts.CONTENT_URI;
I use following code to read the contacts and I allways get every contact, phone, SIM, etc..
//https://stackoverflow.com/questions/16651609/how-to-display-phone-contacts-only-exclude-sim-contacts
// http://www.higherpass.com/Android/Tutorials/Working-With-Android-Contacts/1/
ContentResolver cr = currentActivity.getContentResolver();
Uri queryUri = ContactsContract.Contacts.CONTENT_URI;
Cursor cur = cr.query(queryUri,
null, null, null, null);
if (cur.getCount() > 0) {
while (cur.moveToNext()) { //Are there still contacts?
//See if the contact has at least one number
if (Integer.parseInt(cur.getString(cur.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER))) > 0) {
String id = cur.getString( cur.getColumnIndex(ContactsContract.Contacts._ID) );
String name = cur.getString( cur.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME) );
ArrayList<String> numbers = new ArrayList<String>();
//Read numbers:
Cursor pCur = cr.query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID +" = ?",
new String[]{id}, null);
while (pCur.moveToNext()) {
numbers.add( pCur.getString(
pCur.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)) );
Log.e("Contactbook", "The latest added number is: " + numbers.get(numbers.size()-1) );
}
pCur.close();
}
}
}
What am I missing? This code still gives me both SIM and phone contacts to the log.
Edit: To clarifify, in the contactbook you got the "Display options". In there ist the "select contacts to display"-option, and I want to read the contacts that are shown based on the users choice there. So if a user choses to show only SIM-contacts, read only SIM-contacts, if he choses to only show Phone-Contacts, show onyl phone contacts etc...
Try with following "selection".
String selection = ContactsContract.Contacts.IN_VISIBLE_GROUP + " = ?";
From Android docs:
public static final String IN_VISIBLE_GROUP: An indicator of whether this contact is supposed to be visible in the UI. "1" if the contact has at least one raw contact that belongs to a visible group; "0" otherwise.
This should be your "selection" argument in query API.
Update: I tried below code on Android-2.3(I know it is old device, but right now Don't have newer device with me).
final ContentResolver cr = getContentResolver();
String[] projection = new String[] { ContactsContract.Contacts._ID, ContactsContract.Contacts.DISPLAY_NAME };
String selection = ContactsContract.Contacts.IN_VISIBLE_GROUP + " = ?" ;
String[] Args = { "1" };
final Cursor contacts = cr.query(
ContactsContract.Contacts.CONTENT_URI, projection,
selection,Args ,
null);
This could be a long operation (depending on no. of contacts), hence you should use CursorLoader class (A loader that queries the ContentResolver and returns a Cursor) for this.
cursorLoader.loadInBackground();
This will be called on a worker thread to perform the actual load and to return the result of the load operation.
You can easily create two Arrays for each kind of contacts
ArrayList<String> simContacts = new ArrayList<>();
//get all sim contacts
Uri simUri = Uri.parse("content://icc/adn");
Cursor cursorSim = getContext().getContentResolver().query(simUri, null, null, null, null);
while (cursorSim.moveToNext()) {
simContacts.add(cursorSim.getString(cursorSim.getColumnIndex("name")));
}
}
ArrayList<String> allContacts = new ArrayList<>();
//get all contacts
Cursor cur = cr.query(ContactsContract.Contacts.CONTENT_URI,
null, null, null, null);
if (cursor != null && cursor.getCount() > 0) {
while (!cursor.isAfterLast()) {
String phoneNumber = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.NUMBER));
String displayNameColumn = Utils.hasHoneycomb() ? ContactsContract.Contacts.DISPLAY_NAME_PRIMARY : ContactsContract.Contacts.DISPLAY_NAME;
String displayName = cursor.getString(cursor.getColumnIndexOrThrow(displayNameColumn));
//check if simContacts Array contains this particular name
if (!simContacts.contains(displayNameColumn){
allContacts.add(displayNameColumn);
}
}
}
This is just a working example,of course you can modify to your needs.You can parse more contact fields and make more complication queries.
i have created one listview.now i want to show data(contact no.,name) from contactlist of android(Phonebook) on my listview.
i got it for android 1.5 but i want to do it for android2.1updated.
How to do it?can any one guide me or give some sample code?
Thanks in advance---
If you have achieved it in 1.5 then there can be a small difference of URI. You should go for updated/changed URI of Contacts in 2.1. For further understanding here is a sample code for 2.1updated:
ContentResolver cr = getContentResolver();
Cursor cur = cr.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
while (cur.moveToNext())
{
String id = cur.getString(cur.getColumnIndex(ContactsContract.Contacts._ID));
//---------------------------------------- Contact Names
//------------------------------------------------------
String DisplayName = cur.getString(cur.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
Cursor names = cr.query(ContactsContract.Data.CONTENT_URI, null,ContactsContract.Data.CONTACT_ID + " = " + id, null, null);
names.moveToNext();
String GivenName = names.getString(names.getColumnIndex(ContactsContract.Data.DATA2));
String FamilyName = names.getString(names.getColumnIndex(ContactsContract.Data.DATA3));
String MiddleName = names.getString(names.getColumnIndex(ContactsContract.Data.DATA5));
names.close();
//----------------------------------------- Phone Numbers
//-------------------------------------------------------
String hasPhoneNumber = cur.getString(cur.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER));
if (hasPhoneNumber.equals("1"))
{
Cursor phones = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, ContactsContract.CommonDataKinds.Phone.CONTACT_ID +" = "+ id,null, null);
while (phones.moveToNext())
{
......
}
}
} // AND SO ON.... Get which ever data you need.
so above is just a sample code that I used for my task. You can get what you need by making changes accordingly. I hope it helps.
This will help you getting contacts.
Cursor contactList = getContentResolver().query(ContactsContract.Contacts.CONTENT_URI,null, null,null, null);
For getting phone number of a particular contact,you need to use:
String id = contactList.getString(cur.getColumnIndex(ContactsContract.Contacts._ID));
Cursor phoneCursor = getContentResolver().query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
new String[]{ContactsContract.CommonDataKinds.Phone.NUMBER},
ContactsContract.CommonDataKinds.Phone.CONTACT_ID +" = ?",
new String[]{id}, null);
I want to store contact information in an arraylist hash map so that i writ that information in .csv file.
I am able to get contact information but how to make consistency means if i store information "name" in an array and contactno also in array than if any name not contains any contactno then inconsistency would occurs so please tell me how to overcome this.
My code is:
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));
name = cur.getString(cur.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
cur.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
if (Integer.parseInt(cur.getString(cur.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER))) > 0) {
//Query phone here. Covered next
if (Integer.parseInt(cur.getString(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);
while (pCur.moveToNext()) {
// Do something with phones
String phoneno = pCur.getString(pCur.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
//Log.i("phonenos",phoneno);
}
pCur.close();
}
}
}
}
I don't quite get the question, could you explain it a little better, than I might be able to help.
You want 2 Hashmaps, 1 with contact his name, one with their number, and then you have problems when a contact hasn't got a number or..?
edit:
If you're looking for exporting into an csv, you should see this related topic
Android - Generate CSV file from table values
Hope this helps.