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();
}
Related
I'm new to android and i'm working with native contact.
So my app is let user put contact display name and their number for edit/delete.
In case the contact have more that one number.
I tried a lot but still have no luck, the app still doesn't update the number or it crashes.
What I'm going to do as my understanding is:
Find name in contact that matched name user inserted and use that to get contact_id that represent this contact datagroup.
Use contact_id in 1. and the number user input to find ._ID that represent the specific row id.
Do task with ._ID we get from 2.
This is 1. code to get contact_id:
public String getPeopleUniqueID(String name, Context context) {
String s = null;
String selection = ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME+" like'%" + name +"%'";
String[] projection = new String[] {ContactsContract.Data.CONTACT_ID};
Cursor c = context.getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
projection, selection, null, null);
if(c.moveToFirst()) {
s = c.getString(c.getColumnIndex(ContactsContract.Data.CONTACT_ID));
}
c.close();
return s;
}
This is 2. code to get ._ID (num is number user inserted and name is from 1. > the contact_id)
public String checkPhoneNumber(String num, String name, Context context) {
String s = null;
String selection = ContactsContract.CommonDataKinds.Phone.NUMBER + "=?" + " AND "+ContactsContract.Data.CONTACT_ID+ "=?";
String[] projection = new String[] {ContactsContract.Data._ID};
Cursor c = context.getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
projection, selection, new String[]{u,name}, null);
if(c.moveToFirst()) {
s=c.getString(c.getColumnIndex(ContactsContract.Data._ID));
}
c.close();
if (s==null){
s = "null";
}
return s;
}
To do something like editing (num is _.ID we get from 2. and newnum is new number user want to change into).
public void editNumber(String num , String newnum) {
ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
ops.add(ContentProviderOperation.newUpdate(Data.CONTENT_URI)
.withSelection(Data._ID + "=? AND " +
Data.MIMETYPE + "='" +
CommonDataKinds.Phone.CONTENT_ITEM_TYPE + "'",
new String[]{num})
.withValue(Data.DATA1, newnum)
.build());
try{
getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);}
catch (RemoteException e){e.printStackTrace();}catch (OperationApplicationException e) {e.printStackTrace();}
}
And well it crashes when I call editNumber().
Can you help me fix my code and my understanding?
And another question, can I edit/insert group for the contact programatically, like I want to add this contact to family friend or co-worker group (the default group that we can set at contact edit page)?
Use ContactsContract.Contacts.CONTENT_FILTER_URI for searching a contact based on name - to get Id or anything else. The like operator cannot handle all cases which the CONTENT_FILTER_URI does handle - For various languages, special characters etc.
http://developer.android.com/reference/android/provider/ContactsContract.Contacts.html#CONTENT_FILTER_URI
Use following uri to lookup a contact from phone number - you can get person id or anything else :
Uri lookupUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI,
Uri.encode(phoneNumber));
In the set query you can also use contactId in the condition
For groups you can use custom mimetypes if the default one does not suit you (which is still very primitive for groups across different account types)
I am trying to get the contact's phone number after I have retrieved their ID number from the built-in activity. However, whenever I query the database using the cursor in my code below -- I get zero rows returned even though there is a mobile number for the contact I have selected.
Can anyone point me in a better direction or show an example of how to get the contact's phone number AFTER getting their userID?
My code:
private Runnable getSMSRunnable() {
return new Runnable() {
public void run() {
Intent i = new Intent(Intent.ACTION_PICK,
ContactsContract.CommonDataKinds.Phone.CONTENT_URI);
startActivityForResult(i, CONTACTS_REQUEST_CODE);
}
};
}
Returns the Log output
content://com.android.contacts/data/6802
From which i pass the ID (6802) into a method which is designed to return the phone number from the ID with the given type (in this case ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE)
public static String getContactPhoneNumberByPhoneType(Context context, long contactId, int type) {
String phoneNumber = null;
String[] whereArgs = new String[] { String.valueOf(contactId), String.valueOf(type) };
Log.d(TAG, String.valueOf(contactId));
Cursor cursor = context.getContentResolver().query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ? and "
+ ContactsContract.CommonDataKinds.Phone.TYPE + " = ?", whereArgs, null);
int phoneNumberIndex = cursor
.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.NUMBER);
Log.d(TAG, String.valueOf(cursor.getCount()));
if (cursor != null) {
Log.v(TAG, "Cursor Not null");
try {
if (cursor.moveToNext()) {
Log.v(TAG, "Moved to first");
Log.v(TAG, "Cursor Moved to first and checking");
phoneNumber = cursor.getString(phoneNumberIndex);
}
} finally {
Log.v(TAG, "In finally");
cursor.close();
}
}
Log.v(TAG, "Returning phone number");
return phoneNumber;
}
Which returns null for a phone number -- which means it cannot find the row that I am trying to access -- which means that something is wrong with my query -- however if I check a contact that has a mobile phone number -- how could I get a 0 row query?
Any help would be greatly appreciated. Thank you so much!
I found the answer.
The reason I was not getting any rows from the cursor was because I was using the line
ContactsContract.CommonDataKinds.Phone.CONTACT_ID
"The id of the row in the Contacts table that this data belongs to."
Since I was getting the URI from contacts table anyways -- this was not needed and the following should have been substituted. The ID was the one corresponding to the contact in the phone table not the raw contact.
ContactsContract.CommonDataKinds.Phone._ID
Exchanging the lines returned the correct results in the query. Everything seems to be working well at the moment.
This should work, (maybe try losing the type)
Phone numbers are stored in their own table and need to be queried separately. To query the phone number table use the URI stored in the SDK variable ContactsContract.CommonDataKinds.Phone.CONTENT_URI. Use a WHERE conditional to get the phone numbers for the specified contact.
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
}
pCur.close();
}
Perform a second query against the Android contacts SQLite database. The phone numbers are queried against the URI stored in ContactsContract.CommonDataKinds.Phone.CONTENT_URI. The contact ID is stored in the phone table as ContactsContract.CommonDataKinds.Phone.CONTACT_ID and the WHERE clause is used to limit the data returned.
I am trying to find out if the caller id is in the contacts list. So the code I am using for that:
ContentResolver resolver = this.service.getContentResolver();
String number = PhoneLookup.NUMBER;
String[] projections = { number };
String selectionClause = number + " = ?";
String[] selectionClauseArgs = { callerId };
Cursor people = resolver.query(
ContactsContract.Contacts.CONTENT_URI, projections,
selectionClause, selectionClauseArgs, null);
return people.getCount() > 0 ? true : false;
I am giving the filtering functionality to the ContentResolver itself instead of fetching everything, iterating over them and checking one by one.
But I receive the following error for something reason (There are many codes but they are working just fine, since they are just subscribing to the telephony events in order to receive caller id when some one is calling!")
08-28 19:20:05.903: E/AndroidRuntime(737): java.lang.IllegalArgumentException: Invalid column number
I don't what is invalid. I am using right column name which is PhoneLookup.NUMBER constant. A similar question was asked here before: How to read contacts on Android 2.0 which I followed.
There's no phone numbers in this table. You should query it from another place.
Here's fast scratch (I belive there's better way to do it):
boolean hasPhoneNumber = (people.getInt(people.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER)) > 0);
if (hasPhoneNumber) {
String id = people.getString(people.getColumnIndex(ContactsContract.Contacts._ID));
String num = getNumber(id);
}
public String getNumber(String id) {
String number = null;
String name = null;
Cursor pCur = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?", new String[] { id }, null);
if (pCur.moveToFirst()) {
number = pCur.getString(pCur.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
name = pCur.getString(pCur.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
}
pCur.close();
return number;
}
I have the following code to get details of a contact.
"data": is the Uri I get back after selecting the contact.
I need to be sure that I will get to the right contact in the future so what should I be saving for future use? Is it "lookupUri" or "lookupKey"?
Cursor c = activity.managedQuery(data, null, null, null, null);
c.moveToFirst();
String lookupKey = c.getString(c.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY ));
c.close();
// Next use that key to access the details of the contact in order to get the name and the photo
// Also, save it for future use.
// It will be used when we fetch the details from the database since the photo itself is not saved.
Uri lookupUri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_LOOKUP_URI,lookupKey);
Uri uri = ContactsContract.Contacts.lookupContact(activity.getContentResolver(), deviceDetails.lookupUri);
LookupKey is the unique identifier that you want to store.
FYI; There is a bug in 2.1 where un-synced contacts LookupKey changes when the name is changed.
This works. My example looks up the name; add or remove the fields you want in the projection.
private String getContactNameFromAndroidKey (String key)
{
// Run query
Uri uri = Uri.parse (ContactsContract.Contacts.CONTENT_LOOKUP_URI + "/" + key);
String[] projection = new String[] {
Contacts._ID,
Contacts.DISPLAY_NAME,
};
Cursor cursor = context.getContentResolver().query (
uri,
projection,
null,
null,
null);
if (!cursor.moveToNext()) // move to first (and only) row.
throw new IllegalStateException ("contact no longer exists for key");
String name = cursor.getString(1);
cursor.close();
return name;
}
I am trying to get the contact's phone number after I have retrieved their ID number from the built-in activity. However, whenever I query the database using the cursor in my code below -- I get zero rows returned even though there is a mobile number for the contact I have selected.
Can anyone point me in a better direction or show an example of how to get the contact's phone number AFTER getting their userID?
My code:
private Runnable getSMSRunnable() {
return new Runnable() {
public void run() {
Intent i = new Intent(Intent.ACTION_PICK,
ContactsContract.CommonDataKinds.Phone.CONTENT_URI);
startActivityForResult(i, CONTACTS_REQUEST_CODE);
}
};
}
Returns the Log output
content://com.android.contacts/data/6802
From which i pass the ID (6802) into a method which is designed to return the phone number from the ID with the given type (in this case ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE)
public static String getContactPhoneNumberByPhoneType(Context context, long contactId, int type) {
String phoneNumber = null;
String[] whereArgs = new String[] { String.valueOf(contactId), String.valueOf(type) };
Log.d(TAG, String.valueOf(contactId));
Cursor cursor = context.getContentResolver().query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ? and "
+ ContactsContract.CommonDataKinds.Phone.TYPE + " = ?", whereArgs, null);
int phoneNumberIndex = cursor
.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.NUMBER);
Log.d(TAG, String.valueOf(cursor.getCount()));
if (cursor != null) {
Log.v(TAG, "Cursor Not null");
try {
if (cursor.moveToNext()) {
Log.v(TAG, "Moved to first");
Log.v(TAG, "Cursor Moved to first and checking");
phoneNumber = cursor.getString(phoneNumberIndex);
}
} finally {
Log.v(TAG, "In finally");
cursor.close();
}
}
Log.v(TAG, "Returning phone number");
return phoneNumber;
}
Which returns null for a phone number -- which means it cannot find the row that I am trying to access -- which means that something is wrong with my query -- however if I check a contact that has a mobile phone number -- how could I get a 0 row query?
Any help would be greatly appreciated. Thank you so much!
I found the answer.
The reason I was not getting any rows from the cursor was because I was using the line
ContactsContract.CommonDataKinds.Phone.CONTACT_ID
"The id of the row in the Contacts table that this data belongs to."
Since I was getting the URI from contacts table anyways -- this was not needed and the following should have been substituted. The ID was the one corresponding to the contact in the phone table not the raw contact.
ContactsContract.CommonDataKinds.Phone._ID
Exchanging the lines returned the correct results in the query. Everything seems to be working well at the moment.
This should work, (maybe try losing the type)
Phone numbers are stored in their own table and need to be queried separately. To query the phone number table use the URI stored in the SDK variable ContactsContract.CommonDataKinds.Phone.CONTENT_URI. Use a WHERE conditional to get the phone numbers for the specified contact.
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
}
pCur.close();
}
Perform a second query against the Android contacts SQLite database. The phone numbers are queried against the URI stored in ContactsContract.CommonDataKinds.Phone.CONTENT_URI. The contact ID is stored in the phone table as ContactsContract.CommonDataKinds.Phone.CONTACT_ID and the WHERE clause is used to limit the data returned.