Background
Contacts on the address book can have an account data that's attached to them.
Each app can have an account, and then add its own information for the contact.
Apps such as Telegram, WhatsApp, Viber,... - all create an account that adds information and/or actions to contacts.
Here's an example of a contact that has both WhatsApp and Viber accounts for it:
The problem
I'm trying to figure out how to fetch all contacts that have a specified account.
Since WhatsApp is the most popular that I know of, my tests focus on it.
My problem is that some users claim what I did barely returns contacts, and some claim it doesn't show even a single one. It seems to usually work, and in my case it always worked, but something is probably not good on the code.
What I've tried
I got to make the next code, which to me seems to work, getting a map of phone-to-contact-info, of all WhatsApp contacts.
The idea is to get all possible information of WhatsApp contacts, vs all basic contacts data, and merge those that match the same lookup-key.
I tried to use a better query of joining, but I failed. Maybe it is possible too, and might be more efficient.
Here's the code:
/**
* returns a map of lookup-key to contact-info, of all WhatsApp contacts
*/
#NonNull
public HashMap<String, ContactInfo> getAllWhatsAppPhones(Context context) {
ContentResolver cr = context.getContentResolver();
final HashMap<String, ContactInfo> phoneToContactInfoMap = new HashMap<>();
final HashMap<String, String> whatsAppLookupKeyToPhoneMap = new HashMap<>();
final String phoneMimeType = Phone.CONTENT_ITEM_TYPE;
final Cursor whatsAppCursor;
whatsAppCursor = cr.query(Data.CONTENT_URI,
new String[]{Phone.NUMBER, Phone.LOOKUP_KEY},
Phone.MIMETYPE + " = ?", new String[]{WhatsAppStuff.WHATS_APP_MIME_TYPE}, null);
if (whatsAppCursor == null)
return phoneToContactInfoMap;
Cursor contactCursor = cr.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
//null,
new String[]{
Contacts.LOOKUP_KEY, Contacts._ID, Contacts.PHOTO_THUMBNAIL_URI,
ContactsContract.Contacts.DISPLAY_NAME, // ContactsContract.CommonDataKinds.Phone.NUMBER,
},
"(" + Phone.MIMETYPE + " IS NULL OR " + Phone.MIMETYPE + " = '" + phoneMimeType + "') AND ("
+ ContactsContract.RawContacts.ACCOUNT_TYPE + " = 'com.google' OR " + ContactsContract.RawContacts.ACCOUNT_TYPE + " IS NULL)",
null, null);
if (contactCursor == null) {
whatsAppCursor.close();
return phoneToContactInfoMap;
}
int progress = 0;
final int phoneNumberIdx = whatsAppCursor.getColumnIndex(Phone.NUMBER);
final int lookupKeyIdx = whatsAppCursor.getColumnIndex(Phone.LOOKUP_KEY);
while (whatsAppCursor.moveToNext()) {
final String phoneNumberValue = whatsAppCursor.getString(phoneNumberIdx);
final int endIndex = phoneNumberValue.indexOf("#");
if (endIndex < 0)
continue;
String lookupKey = whatsAppCursor.getString(lookupKeyIdx);
final String phone = phoneNumberValue.substring(0, endIndex);
if (!phone.isEmpty() && StringUtil.isAllDigits(phone)) {
//Log.d("AppLog", "whatsApp phone:" + phone + " " + lookupKey);
whatsAppLookupKeyToPhoneMap.put(lookupKey, phone);
}
if (markedToCancel != null && markedToCancel.get()) {
whatsAppCursor.close();
contactCursor.close();
return phoneToContactInfoMap;
}
if (progressListener != null)
progressListener.onProgressUpdate(progress++, maxProgress);
}
whatsAppCursor.close();
if (whatsAppLookupKeyToPhoneMap.isEmpty())
return phoneToContactInfoMap;
//Log.d("AppLog", "getting info about whatsapp contacts");
final int idColIdx = contactCursor.getColumnIndex(Contacts._ID);
final int displayNameColIdx = contactCursor.getColumnIndex(Contacts.DISPLAY_NAME);
final int lookupKeyColIdx = contactCursor.getColumnIndex(Contacts.LOOKUP_KEY);
final int photoColIdx = contactCursor.getColumnIndex(Contacts.PHOTO_THUMBNAIL_URI);
while (contactCursor.moveToNext()) {
String lookupKey = contactCursor.getString(lookupKeyColIdx);
String phoneNumber = whatsAppLookupKeyToPhoneMap.get(lookupKey);
if (phoneNumber == null)
continue;
ContactInfo contactInfo = new ContactInfo();
contactInfo.lookupKey = lookupKey;
contactInfo.displayName = contactCursor.getString(displayNameColIdx);
contactInfo.photoThumbUriStr = contactCursor.getString(photoColIdx);
contactInfo.whatsAppPhoneNumber = phoneNumber;
contactInfo.contactId = contactCursor.getLong(idColIdx);
phoneToContactInfoMap.put(phoneNumber, contactInfo);
if (markedToCancel != null && markedToCancel.get()) {
contactCursor.close();
return phoneToContactInfoMap;
}
if (progressListener != null)
progressListener.onProgressUpdate(progress++, maxProgress);
}
contactCursor.close();
return phoneToContactInfoMap;
}
The question
As I wrote, the above code only usually works.
How come it only usually works? What's missing to fix it?
Should I use Contacts.getLookupUri instead of lookup key? If so, how should I change the code above to use it instead?
I tried to use a URI instead of a lookup-key, but then it didn't find any of them inside the normal contacts.
The main issue I see that can explain why users won't see results from your code, is that you're assuming all the contacts are stored on a Google account.
While this is the default behavior in some devices, it's not the default on all devices, also, users can freely change their contacts storage to any other location (yahoo contacts, MS exchange, phone-only (unsynced), etc.)
Having that said, if your only requirement is to
fetch all contacts that have a specified account.
I think that's a much better alternative then your 2 queries (one of which runs over all contacts, not just the required ones):
// Uri to query contacts that have a RawContact in the desired account
final Uri.Builder builder = Contacts.CONTENT_URI.buildUpon();
builder.appendQueryParameter(RawContacts.ACCOUNT_NAME, whatsappAccountName);
builder.appendQueryParameter(RawContacts.ACCOUNT_TYPE, whatsappAccountType);
Uri uri = builder.build();
String[] projection = new String[]{ Contacts.LOOKUP_KEY, Contacts._ID Contacts.DISPLAY_NAME }; // add more if needed
// boo-yaa!
Cursor cur = cr.query(uri, projection, null, null, null);
Related
But I need this with numbers with country codes (like starting with +123 456 78 90 -that's the way I add the number as a contact)
I need a function/class to check if this number has Whatsapp.
I need this for an .apk I made in Android Studio.
private void getAllWhatsappNumbers()
{
//This class provides applications access to the content model.
ContentResolver cr = context.getContentResolver();
//RowContacts for filter Account Types
Cursor contactCursor = cr.query(
ContactsContract.RawContacts.CONTENT_URI,
new String[]{ContactsContract.RawContacts._ID,
ContactsContract.RawContacts.CONTACT_ID},
ContactsContract.RawContacts.ACCOUNT_TYPE + "= ?",
new String[]{"com.whatsapp"},
null);
if (contactCursor != null) {
if (contactCursor.getCount() > 0) {
if (contactCursor.moveToFirst()) {
do {
//whatsappContactId for get Number,Name,Id ect... from ContactsContract.CommonDataKinds.Phone
String whatsappContactId = contactCursor.getString(contactCursor.getColumnIndex(ContactsContract.RawContacts.CONTACT_ID));
if (whatsappContactId != null) {
//Get Data from ContactsContract.CommonDataKinds.Phone of Specific CONTACT_ID
Cursor whatsAppContactCursor = cr.query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
new String[]{ContactsContract.CommonDataKinds.Phone.CONTACT_ID,
ContactsContract.CommonDataKinds.Phone.NUMBER,
ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME},
ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?",
new String[]{whatsappContactId}, null);
if (whatsAppContactCursor != null) {
whatsAppContactCursor.moveToFirst();
String id = whatsAppContactCursor.getString(whatsAppContactCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.CONTACT_ID));
String name = whatsAppContactCursor.getString(whatsAppContactCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
String number = whatsAppContactCursor.getString(whatsAppContactCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
Log.d("log1","no:"+number+"name:"+name);
whatsAppContactCursor.close();
//Add Number to ArrayList
myWhatsappContactsNumbers.add(number);
}
}
} while (contactCursor.moveToNext());
contactCursor.close();
}
}
}
}
With this function provided, we can get contact list on "com.whatsapp"
We put this contact list in to the array list so we need a function to show us we the number we sent is in the list (true) or not (false). How can we achieve that?
for first thing you need to have contact name and also social media package name and package name mimetype (like whatsapp >> packagename: com.whatsapp, packageMimetype: com.whatsapp)
after that this code return you that this number have selected social media or not with a boolean. :)
fun hasSocialMedia(contactName: String, socialMediaPackage: String, socialMediaMimeType: String, normilizeNumber: String?) : Boolean {
var hasSelectedSocialMedia = false
val cursor1: Cursor = _context.contentResolver.query(
RawContacts.CONTENT_URI,
arrayOf(RawContacts._ID),
RawContacts.ACCOUNT_TYPE + "= ? AND " + ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME_PRIMARY + " = ?",
arrayOf(socialMediaPackage, contactName),
null
)!!
while (cursor1.moveToNext()) {
val rawContactId = cursor1.getString(cursor1.getColumnIndex(RawContacts._ID))
val cursor2: Cursor = _context.contentResolver.query(
ContactsContract.Data.CONTENT_URI,
arrayOf(ContactsContract.Data.DATA3),
ContactsContract.Data.MIMETYPE + " = ? AND " + ContactsContract.Data.RAW_CONTACT_ID + " = ? ",
arrayOf("vnd.android.cursor.item/vnd.$socialMediaMimeType.profile", rawContactId),
null
)!!
while (cursor2.moveToNext()) {
var phoneNumber = cursor2.getString(0)
if (TextUtils.isEmpty(phoneNumber)) continue
phoneNumber = phoneNumber.replace(" ", "")
if (phoneNumber.contains(normilizeNumber.toString(), true)) hasSelectedSocialMedia = true
else continue
}
cursor2.close()
}
cursor1.close()
return hasSelectedSocialMedia;
}
There is no proper way to check whether the contact no is registered on WhatsApp or not. But you can do one thing to check whether no is registered on WhatsApp or not.
Just pass that number to WhatsApp application using explicit intent and after passing to WhatsApp, WhatsApp open one popup which says "The given no is registered on WhatsApp" by this you can get to know whether no is registered or not.
Intent sendIntent = new Intent("android.intent.action.MAIN");
sendIntent.putExtra("jid", whatsAppNumber + "#s.whatsapp.net");
sendIntent.putExtra(Intent.EXTRA_TEXT, "whatsAppMessage");
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
sendIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
sendIntent.setPackage("com.whatsapp.w4b");
sendIntent.setType("text/*");
startActivity(sendIntent);
Here "com.whatsapp.w4b" is used for the Business WhatsApp application if you want to use normal WhatsApp then you just need to change com.whatsapp.w4b to com.whatsapp
whatsAppNumber is a number to which you want to send the WhatsApp a message
Or Second way is to find a contact from internal contact data
ArrayList<String> whatsAppContact = new ArrayList();
Cursor c = getContentResolver().query(
RawContacts.CONTENT_URI,
new String[] { RawContacts.CONTACT_ID, RawContacts.DISPLAY_NAME_PRIMARY },
RawContacts.ACCOUNT_TYPE + "= ?",
new String[] { "com.whatsapp" },
null);
ArrayList<String> myWhatsappContacts = new ArrayList<String>();
int name =
c.getColumnIndex(RawContacts.DISPLAY_NAME_PRIMARY);
while (c.moveToNext())
{
whatsAppContact.add(c.getString(name));
}
I'm getting skype contacts in my android device with this code:
private void getContactList() {
Cursor c = getContentResolver().query(
RawContacts.CONTENT_URI,
new String[] { RawContacts.CONTACT_ID,
RawContacts.DISPLAY_NAME_PRIMARY,
RawContacts.DISPLAY_NAME_ALTERNATIVE },
RawContacts.ACCOUNT_TYPE + "= ?",
new String[] { "com.skype.contacts.sync" }, null);
int contactNameColumn = c
.getColumnIndex(RawContacts.DISPLAY_NAME_ALTERNATIVE);
int count = c.getCount();
skypeName = new String[count];
for (int i = 0; i < count; i++) {
c.moveToPosition(i);
skypeName[i] = c.getString(contactNameColumn);
Log.i("KEY", skypeName[i]);
}
}
This code works fine, but it returns the skype display names.However is there any possibility to get Skype name not the display name so I can call or video call using that Skype name?.
Thanks,
Regards.
You need to query over the Data table not RawContacts, the Data table's data is organized by mimetypes, you just need to find the mimetype containing the info you're looking for, in skype's case it's: "vnd.android.cursor.item/com.skype.android.skypecall.action"
private void getContactList() {
Cursor c = getContentResolver().query(
Data.CONTENT_URI,
new String[] { Data.CONTACT_ID, Data.DATA1 },
Data.MIMETYPE + "= ?",
new String[] { "vnd.android.cursor.item/com.skype.android.skypecall.action" },
null);
while (c != null && c.moveToNext()) {
String contact = c.getLong(0);
String skype = c.getString(1);
Log.i("contact " + contact + " has skype username: " + skype);
}
}
I've typed the code here, so it's not checked and I might have typos
Make sure you ALWAYS close cursors via c.close()
I'm not incredibly familiar with the Android-Skype API, but it looks like this line
.getColumnIndex(RawContacts.DISPLAY_NAME_ALTERNATIVE);
is what pulls the display name instead of the username. You could check the API and see if there's another RawContacts.method that fetches the username instead.
Additional information could be available here:
http://www.programmableweb.com/api/skype
or
http://www.skype.com/en/developer/
Can anybody guide me to find the solution for the following problem.
I have to identify whether the phone contact saved in locally or from Email ?(programatically)
i have read fromgoogle doc that ContactsContract.Groups, which contains information about raw contact groups such as Gmail contact groups. The current API does not support the notion of groups spanning multiple accounts.
Based on that i have tried the following code.
StringBuffer output = new StringBuffer();
final String[] GROUP_PROJECTION = new String[] {
ContactsContract.Groups._ID,
ContactsContract.Groups.TITLE,
ContactsContract.Groups.SUMMARY_WITH_PHONES
};
Cursor c = getContentResolver().query(
ContactsContract.Groups.CONTENT_URI, GROUP_PROJECTION, null,
null, ContactsContract.Groups.TITLE);
int IDX_ID = c.getColumnIndex(ContactsContract.Groups._ID);
int IDX_TITLE = c.getColumnIndex(ContactsContract.Groups.TITLE);
output.append("title"+IDX_TITLE+"\n");
Map<String,GroupInfo> m = new HashMap<String, GroupInfo>();
while (c.moveToNext()) {
output.append("test...\n");
GroupInfo g = new GroupInfo();
g.id = c.getString(IDX_ID);
g.title = c.getString(IDX_TITLE);
output.append("title"+c.getString(IDX_TITLE)+"\n");
int users = c.getInt(c.getColumnIndex(ContactsContract.Groups.SUMMARY_WITH_PHONES));
if (users>0) {
// group with duplicate name?
GroupInfo g2 = m.get(g.title);
if (g2==null) {
m.put(g.title, g);
output.append("title"+g.title+"\n");
groups.add(g);
} else {
g2.id+=","+g.id;
}
}
}
outputText.setText(output);
c.close();
but no hope.
I am posting this answer for future use. We can differentiate the local phone contacts and the sync contacts by using the field called RawContacts.SOURCE_ID
It is described here
SOURCE_ID
read/write
String that uniquely identifies this row to its source account. Typically it is set at the time the raw contact is inserted and never changed afterwards. The one notable exception is a new raw contact: it will have an account name and type (and possibly a data set), but no source id. This indicates to the sync adapter that a new contact needs to be created server-side and its ID stored in the corresponding SOURCE_ID field on the phone.
The sample code is follows, it gives the id for sync contacts and null for others.
private void testContact() {
StringBuffer output = new StringBuffer();
ContentResolver resolver = getContentResolver();
Cursor contacts = resolver.query(Contacts.CONTENT_URI, null,
Contacts.HAS_PHONE_NUMBER + " != 0", null, Contacts._ID
+ " ASC");
Cursor data = resolver.query(Data.CONTENT_URI, null, Data.MIMETYPE
+ "=? OR " + Data.MIMETYPE + "=?", new String[]{
Email.CONTENT_ITEM_TYPE, Phone.CONTENT_ITEM_TYPE},
Data.CONTACT_ID + " ASC");
int idIndex = contacts.getColumnIndexOrThrow(Contacts._ID);
int nameIndex = contacts.getColumnIndexOrThrow(Contacts.DISPLAY_NAME);
int cidIndex = data.getColumnIndexOrThrow(Data.CONTACT_ID);
int data1Index = data.getColumnIndexOrThrow(Data.DATA1);
boolean hasData = data.moveToNext();
while (contacts.moveToNext()) {
long id = contacts.getLong(idIndex);
Uri rawContactUri =
ContentUris.withAppendedId(RawContacts.CONTENT_URI, id);
Uri entityUri =
Uri.withAppendedPath(rawContactUri, Entity.CONTENT_DIRECTORY);
Cursor c =
getContentResolver().query(
entityUri,
new String[] {
RawContacts.ACCOUNT_NAME,
RawContacts.SOURCE_ID, Entity.DATA_ID, Entity.MIMETYPE, Entity.DATA1},
null, null, null);
try {
while (c.moveToNext()) {
String sourceId = c.getString(0);
if (!c.isNull(1)) {
String source_id = c.getString(1);
try {
output.append(c.getString(4)+sourceId+" "+source_id+"\n");
//output.append(datas+ "Sync1 "+ c.getString(4)+" Sync2 "+ c.getString(5)+" Sync3"+ c.getString(6)+" Sync4 "+ c.getString(7)+"\n");
} catch (Exception e) {
e.printStackTrace();
}
//decide here based on mimeType, see comment later
}
}
} finally {
c.close();
}
}
outputText.setText(output);
}
Have a read through this and see if ACCOUNT_TYPE might be able to help you
I'm still very new to android app development, and I have hit a problem I hope u can help me with...
I am trying to retrieve any "Notes" stored against a contact within my phone. I want to check if a contact (current caller) has any notes associated to them, and then either display the contents or do some action depending on the content of them etc...
I have tried the code below as a test to see if the data is retrieved anywhere within my cursor, but although it retrieves some data, I can't see the content of a note - so I guess I'm in the wrong place!
Where I have declared contentID, this is a result of doing a lookup with the code below, and using the id that is recieved.
Uri lookupUri = Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, Uri.encode(inCommingNumber));
String[] mPhoneNumberProjection = { Phone._ID, Phone.NUMBER, Phone.DISPLAY_NAME};
Cursor cur = getContentResolver().query(lookupUri , mPhoneNumberProjection, null, null, null);
private boolean hasNote(String contactID){
Boolean noteFound = false;
Cursor noteCur = getContentResolver().query(ContactsContract.Data.CONTENT_URI, null, null, null, null);
if(noteCur.moveToFirst()) {
int numCols = noteCur.getColumnCount();
if(numCols > 0){
for(int x = 0; x < numCols; x++){
Log.d(APP_TAG, " column " + x + " contains: " + noteCur.getString(x));
}
}
noteFound = true;
} else{
Log.d(APP_TAG, "No Note retrieved");
}
noteCur.close();
return noteFound;
}
Apologies, I cant seem to get the code to display properly in this post!!
Any help would be great
I have tried various things, but can't seem to be able to get this data. I have created the note by simply adding it through the normal contact manager. I'm using Android 2.2.
The following code would display the first note for all contacts on your phone:
Cursor contactsCursor = null;
try {
contactsCursor = getContentResolver().query(RawContacts.CONTENT_URI,
new String [] { RawContacts._ID },
null, null, null);
if (contactsCursor != null && contactsCursor.moveToFirst()) {
do {
String rawContactId = contactsCursor.getString(0);
Cursor noteCursor = null;
try {
noteCursor = getContentResolver().query(Data.CONTENT_URI,
new String[] {Data._ID, Note.NOTE},
Data.RAW_CONTACT_ID + "=?" + " AND "
+ Data.MIMETYPE + "='" + Note.CONTENT_ITEM_TYPE + "'",
new String[] {rawContactId}, null);
if (noteCursor != null && noteCursor.moveToFirst()) {
String note = noteCursor.getString(noteCursor.getColumnIndex(Note.NOTE));
Log.d(APP_TAG, "Note: " + note);
}
} finally {
if (noteCursor != null) {
noteCursor.close();
}
}
} while (contactsCursor.moveToNext());
}
} finally {
if (contactsCursor != null) {
contactsCursor.close();
}
}
If you are storing some sort of structured data on the note to take actions on using the free form note field may not be the best approach. With the new contacts contract model you are able to attach your own data fields (in ContactsContract.Data) to a contact just give them your own unique mimetype.
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.