I am trying to show contact pictures in my application but I am getting pictures of those who were added manually only and not those which are synced with facebook. How to w
There is definitely no way to do this in a standard way. So, we must use an SQL Injection 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.
I have tried every solution found on Stack Overflow at the time of writing this and nothing will correctly retrieve a photo that came from Facebook through the official Facebook app account sync adapter.
To be clear, people posting solutions who "think" they work are probably using HTC sense phones that come with a Facebook sync adapter written by HTC that doesn't have the same security requirements as the official Facebook app.
It's a security thing and one of the methods did manage to try to access the bytes of the facebook photo and you will end up with an SqliteException saying that the content requested is restricted.
Same code run as a system process will pull the photo fine. It is just not possible as of right now.
I get the picture for all the contacts even some that are synced from facebook with this code:
* @param context
* The context used to retrieve the image.
* @return The image of the user that is saved in the address book or null if
* the user does not exists or has no image.
*/
public Bitmap getContactPhoto(Context context) {
ContentResolver contentResolver = context.getContentResolver();
Uri photoUri = getCurrentUri(contentResolver);
if (photoUri != null) {
InputStream input = ContactsContract.Contacts.openContactPhotoInputStream(
contentResolver, photoUri);
if (input != null) {
return BitmapFactory.decodeStream(input);
}
} else {
Log.d(getClass().getSimpleName(), "No photo Uri");
}
return null;
}
private Uri getCurrentUri(ContentResolver contentResolver) {
Cursor contact = contentResolver.query(lookUpUri,
new String[] { ContactsContract.Contacts._ID }, null, null, null);
if (contact.moveToFirst()) {
long userId = contact.getLong(contact.getColumnIndex(ContactsContract.Contacts._ID));
return ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, userId);
}
return null;
}
Maybe have a look at the code again that creates your photoId. Don't use the Id of an photo in the data table to create the Uri. I also tried that and retrieving the photo worked for me only if I use an uri that links directly to the merged user. You will then get the default image for this person regardless from where it was synced.
The code you have given should only access the default photo. Also, you should be appending the contact ID to that URI, not the photo ID (assuming you're using the photo's id from the data table).
If there are multiple photos you might want to try accessing them directly from the Data table. You'll need to parse a database cursor and convert the raw byte data into a bitmap manually as shown below:
String[] projection = {ContactsContract.CommonDataKinds.Photo.PHOTO};
Uri uri = Uri. ContactsContract.Data.CONTENT_URI;
String where = ContactsContract.Data.MIMETYPE
+ "=" + ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE + " AND "
+ ContactsContract.Data.CONTACT_ID + " = " + mContactId;
Cursor cursor = context.getContentResolver().query(uri, projection, null, null, null);
if(cursor!=null&&cursor.moveToFirst()){
do{
byte[] photoData = photoCursor.getBlob(0);
Bitmap photo = BitmapFactory.decodeByteArray(photoData, 0,
photoData.length, null);
//Do whatever with your photo here...
}while(cursor.moveToNext());
}
You would want mContactId to correspond with the contact that you want photos for.
If you want to limit to only facebook photos, you'll need to use ContactsContract.Data.RAW_CONTACT_ID instead, which you should get from the RawContacts table using your contact Id and a filter based on the facebook account (assuming you know what account you're looking for... that can vary by sync provider implementation...)
It doesn't work for contacts that are synced from FB only. You'll need to use the FB graph API and fetch the photo from there; and you need to know the contacts facebook name.
Bitmap contactPhoto = getImageBitmap("http://graph.facebook.com/mathiaslin/picture?type=square");
final private static Bitmap getImageBitmap(String url) {
Bitmap bm = null;
try {
URLConnection conn = new URL(url).openConnection();
conn.connect();
InputStream is = conn.getInputStream();
BufferedInputStream bis = new BufferedInputStream(is);
bm = BitmapFactory.decodeStream(bis);
bis.close();
is.close();
} catch (IOException e) {
Log.e(TAG, "Error getting bitmap", e);
}
return bm;
}