Alright, so the book I'm going through wants me to call a specific person.
I can retrieve the contact just fine, it's the whole "getting contact ID to find the phone number bit" that I'm having trouble with.
The app is crashing every time I select a contact.
I suppose I'm not sure how to properly navigate to the contact ID.
I am fairly new to Android and now I'm just getting completely lost, I haven't be able to find a solution on here that helps me.
Relevant Code:
}else if(requestCode == REQUEST_CONTACT) {
Uri contactsURI = data.getData();
String[] queryFields = new String[]{
ContactsContract.Contacts.DISPLAY_NAME
};
//Perform your query - the contactURI is like a "where"
//clause here
Cursor c = getActivity().getContentResolver().query(contactsURI, queryFields, null, null, null);
//Double-check that you actually got results
if (c.getCount() == 0) {
c.close();
return;
}
contactID = c.getString(c.getColumnIndex(ContactsContract.Contacts._ID));
//Pull out the first column of the first row of data
//that is your suspects name
c.moveToFirst();
String suspect = c.getString(0);
mCrime.setmSuspect(suspect);
mSuspectButton.setText(suspect);
c.close();
}
}
I am crashing because of this part of the above code:
contactID = c.getString(c.getColumnIndex(ContactsContract.Contacts._ID));
You have to first call
c.moveToFirst();
Then
contactID = c.getString(c.getColumnIndex(ContactsContract.Contacts._ID))
Related
I'm using the following code segment to get phone numbers of a contact by contact ID
private static ArrayList<PhoneName> getPhonesFromID(Context context,
String contactID, String column) {
// Run query
Uri uri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI;
String[] projection = new String[] {
ContactsContract.CommonDataKinds.Phone._ID,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID,
ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME,
ContactsContract.CommonDataKinds.Phone.NUMBER,
ContactsContract.CommonDataKinds.Phone.STARRED,
ContactsContract.CommonDataKinds.Phone.TYPE };
String selection = column + " = '" + contactID + "'";
String[] selectionArgs = null;
String sortOrder = ContactsContract.CommonDataKinds.Phone.NUMBER
+ " COLLATE LOCALIZED ASC";
Cursor cursor = context.getContentResolver().query(uri, projection,
selection, selectionArgs, sortOrder);
if (cursor == null)
return null;
int numberIndex = cursor
.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
int nameIndex = cursor
.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME);
int typeIndex = cursor
.getColumnIndex(ContactsContract.CommonDataKinds.Phone.TYPE);
int favIndex = cursor
.getColumnIndex(ContactsContract.CommonDataKinds.Phone.STARRED);
ArrayList<PhoneName> phones = new ArrayList<PhoneName>();
while (cursor.moveToNext()) {
PhoneName pn = new PhoneName();
pn.name = cursor.getString(nameIndex);
pn.number = cursor.getString(numberIndex);
pn.starred = cursor.getString(favIndex);
pn.state = DataHelper.getInstance(context)
.getSubscriptionStateByNumber(pn.number);
Log.d("Number", pn.number);
int type = cursor.getInt(typeIndex);
if (type == ContactsContract.CommonDataKinds.Phone.TYPE_HOME) {
pn.type = "Home";
} else if (type == ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE) {
pn.type = "Mobile";
} else if (type == ContactsContract.CommonDataKinds.Phone.TYPE_WORK) {
pn.type = "work";
} else {
pn.type = "other";
}
phones.add(pn);
}
cursor.close();
return phones;
}
Here's PhoneName is my self-defined class to store contact information of that specific contact. This function is called like:
getPhonesFromID(context, _id,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID);
I'm getting the contact information correctly but here's the problem, sometimes this function returns null when I make frequent queries. When this problem occurs, default people application of my android don't show any contact too. All contacts are like vanished. Then if I close my application and restart then it shows all contacts like before. Then again when I go to the contact details of a contact from my application it works fine several times and then all contacts are gone again.
This is a weird problem and I don't know actually what is happening here. I double checked my queries and couldn't figure out what's wrong in here.
Finally, this problem got solved.
Like I said, my application is like Phonebook and I had to show a favourite icon for each contact in the list which is my favourite contact. In my case, I had a function like isFavourite() to determine if a contact is in my favourite list or not and this is called in every single time when a contact row is loading.
Each time I open a cursor to get the query result and it was found that the cursor was not closed anywhere. So basically, closing the cursor properly solved the problem! Cheers!
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.
There have been several questions on SO regarding getting Contacts numbers using the Contacts API but I'd like to know if there is a way to identify that the number retrieved is a mobile number.
The following code is often shown as a way to get a Contact's phone numbers, as it gets a list of one or more phone numbers:
String[] projection = {ContactsContract.Contacts._ID, ContactsContract.Contacts.DISPLAY_NAME, ContactsContract.Contacts.HAS_PHONE_NUMBER};
String selection = ContactsContract.Contacts.HAS_PHONE_NUMBER + "=1";
Cursor cursor = null;
Cursor phones = null;
try
{
cursor = managedQuery(intent.getData(), projection, selection, null, null);
while (cursor.moveToNext())
{
String contactId = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID));
String name = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.Contacts.DISPLAY_NAME));
phones = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null,ContactsContract.CommonDataKinds.Phone.CONTACT_ID +" = "+ contactId,null, null);
while (phones.moveToNext())
{
String pdata = phones.getString(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DATA));
Log.v("DATA",pdata);
}
}
}
catch (NullPointerException npe)
{
Log.e(TAG, "Error trying to get Contacts.");
}
finally
{
if (phones != null)
{
phones.close();
}
if (cursor != null)
{
cursor.close();
}
}
Whilst, this works okay, is there any way to easily identify that the phone number is a mobile type (apart from trying to pattern match with Regex).
I suspect there must be a related piece of data, so that native apps can classify the phone number - as in the picture below:
I stumbled upon a blog article which gives a pretty good explanation of using the ContactsContract api here.
So, in my example above, I changed part of my code above to this:
while (phones.moveToNext())
{
int phoneType = phones.getInt(phones.getColumnIndex(Phone.TYPE));
if (phoneType == Phone.TYPE_MOBILE)
{
phoneNumber = phones.getString(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DATA));
phoneNumber = phoneNumber.replaceAll("\\s", "");
break;
}
}
This loops around all the phones for an individual contact and if the type is Phone.TYPE_MOBILE then it takes this one.
Hope this helps someone with the same issue I had.
The original query cursor = managedQuery(intent.getData(), projection, selection, null, null); should be able to handle filter for you. I am trying to do the same thing. I will post my findings when complete. If anyone knows how to do this using the selection clause in the third parameter. I would love to know the answer.
I created an app. It is working on htc desire hd, but when i tested it on ZTE Blade, a strange problem appeared. In my app when user selects a contact name from a spinner a menu appears. In the menu the user can send sms to the user, call him/her or just look at his/her contact info. On HTC Desire HD, everything is working fine. On ZTE there seems to be an exasperating problem with the contact info button: In certain cases when user selects a contact and wants to see his contact info, some other contact's info is shown. So I select Pete from my spinner but I get Dave's contact info. In other cases I select Tom from the spinner and I get Tom's contact info. The problem is not existing on my HTC. I couldn't figure out what causes the problem. By the way, my contact list on HTC is populated also from gmail and facebook and the app still working fine, while the contact list of the ZTE has never seen any gmail or facebook accounts (i am not entirely sure about this).
This is the code i am using to get to the contact info:
infobtn.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
ContentResolver cr = getContentResolver();
Cursor cur = cr.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
if (cur.getCount() > 0)
{
while (cur.moveToNext()) {
id_contact = cur.getString(cur.getColumnIndex(ContactsContract.Contacts._ID));
name_contact = cur.getString(cur.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
if (name_contact.equals(name1))
{
Cursor pCur = cr.query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID +" = ?", new String[]{id_contact}, null);
id_contact2 = id_contact;
while (pCur.moveToNext()){
}
pCur.close();
}
}
Intent intent_contacts = new Intent(Intent.ACTION_VIEW, Uri.parse("content://contacts/people/" + id_contact2));
startActivity(intent_contacts);
}
}
});
I have similar problems in my application. The app is a widget that give possibility to use favorites contacts in fast way. User creates new widget and assigns contact to it. When he clicks on widget he is able to make common contact actions - call, sms, edit, etc.
Some users send report like this one:
"I select about 4 contacts and the app shows 4 but only one of them right 3 others is same contact which I didnt even picked".
"Why does the wrong contact show when picked? On my droid incredible"
I can't reproduce the problem on my side. I have tried several different devices, tried to make import/export contacts - no results.
Anyway, i have some ideas about source of the problem.
My application uses lookup keys to store contacts, according to sdk. So, when user picks up a contact, application stores contact lookup key. Then it uses this lookup key to get Contact ID for this contact. After that, it uses ONLY received Contact ID in all other functions. So does your function - it uses only Contact ID in sub-query.
The question is: is it possible that different contacts (with different lookup keys) have same Contact ID? It looks like it's possible on some devices in rare cases... If so, we need to use Contact ID + lookup key always together to identify contact. (update: the suggestion was incorrect. Lookup key can be changed after contact info modification, you won't find contact).
As I understand, you are able to reproduce the problem on your side. I would suggest to change your code in such way:
infobtn.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
ContentResolver cr = getContentResolver();
Cursor cur = cr.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
if (cur.getCount() > 0)
{
while (cur.moveToNext()) {
id_contact = cur.getString(cur.getColumnIndex(ContactsContract.Contacts._ID));
name_contact = cur.getString(cur.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
String lookup_key = cur.getString(cur.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY));
if (name_contact.equals(name1))
{
Cursor pCur = cr.query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID +" = ? AND " + ContactsContract.CommonDataKinds.Phone.LOOKUP_KEY + " = ?", new String[]{id_contact, lookup_key}, null);
id_contact2 = id_contact;
while (pCur.moveToNext()){
}
pCur.close();
}
}
Intent intent_contacts = new Intent(Intent.ACTION_VIEW, Uri.parse("content://contacts/people/" + id_contact2));
startActivity(intent_contacts);
}
}
});
and try to reproduce the problem again.
You can see this link, It's very good: http://www.vtutorials.info/2014/08/working-with-android-contacts-how-to.html
private Map<Long, ArrayList<LocalContactInfo>> queryAllPhones(ContentResolver contentresolver)
{
HashMap<Long, ArrayList<LocalContactInfo>> hashmap = new HashMap<Long, ArrayList<LocalContactInfo>>();
Uri PhoneCONTENT_URI = ContactsContract.CommonDataKinds.Phone.CONTENT_URI;
String Phone_CONTACT_ID = ContactsContract.CommonDataKinds.Phone.CONTACT_ID;
String NUMBER = ContactsContract.CommonDataKinds.Phone.NUMBER;
Cursor cursor = contentresolver.query(PhoneCONTENT_URI, null, null, null, null);
do
{
if (!cursor.moveToNext())
{
cursor.close();
return hashmap;
}
long l = cursor.getLong(cursor.getColumnIndex(Phone_CONTACT_ID));
String s = cursor.getString(cursor.getColumnIndex(NUMBER));
LocalContactInfo temp = new LocalContactInfo(String.valueOf(l), "", s, "", 1);
if (hashmap.containsKey(Long.valueOf(l))){
((List<LocalContactInfo>)hashmap.get(Long.valueOf(l))).add(temp);
} else{
ArrayList<LocalContactInfo> arraylist = new ArrayList<LocalContactInfo>();
arraylist.add(temp);
hashmap.put(Long.valueOf(l), arraylist);
}
} while (true);
}
And:
public List<LocalContactInfo> doloadContacts() {
List<LocalContactInfo> _return = new ArrayList<LocalContactInfo>();
Uri CONTENT_URI = ContactsContract.Contacts.CONTENT_URI;
String _ID = ContactsContract.Contacts._ID;
String DISPLAY_NAME = ContactsContract.Contacts.DISPLAY_NAME;
String HAS_PHONE_NUMBER = ContactsContract.Contacts.HAS_PHONE_NUMBER;
ContentResolver contentResolver = mContext.getContentResolver();
Map<Long, ArrayList<LocalContactInfo>> map = queryAllPhones(contentResolver);
Cursor cursor = contentResolver.query(CONTENT_URI, null,null, null, null);
if(cursor == null || cursor.getCount()==0){
return _return;
}
if (cursor.getCount() > 0) {
while (cursor.moveToNext()) {
Long contact_id = cursor.getLong(cursor.getColumnIndex( _ID ));
String fullname = cursor.getString(cursor.getColumnIndex( DISPLAY_NAME )).trim();
int hasPhoneNumber = Integer.parseInt(cursor.getString(cursor.getColumnIndex( HAS_PHONE_NUMBER )));
if (hasPhoneNumber > 0 && map.get(contact_id) != null){
Object obj = ((List<LocalContactInfo>)map.get(contact_id)).get(0);
if(obj instanceof LocalContactInfo){
LocalContactInfo _temp = (LocalContactInfo)obj;
_return.add(new LocalContactInfo(String.valueOf(contact_id), fullname, _temp.getNumberPhone(), "",hasPhoneNumber));
}
}
}
}
return _return;
}
instead of using "contacts/people/xxx", try using contactsContract.Contacts.CONTENT_URI.
basically, you want to replace.
Uri contactUri = Uri.parse("content://contacts/people/" + id_contact2));
with
Uri contactUri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI,
id_contact2);
Another thing to note is that "id_contact2" may be changed if a sync occurs before you use it (like when google sync merges a contact). To prevent this, it might be better to use a LookupKey instead.
ContactsContract.Contacts.LOOKUP_KEY
and retrieve using
Uri contactUri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_LOOKUP_URI,
yourContactLookupKey);
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.