I am making a communication app that has been set up with a specific Account type in the AccountManager and has a SyncAdapter attached to it. Several of my contacts have a RawContact which has a MIMETYPE of my new account and some associated data.
I am looking for the correct way to query a list/cursor of every contact in a phones contact list that has one of these new accounts attached to it. I want to get the whole contact so i can do things like retrieve their phone numbers, display names and uris for pictures, but i only need the contacts that have my new type of account attached.
I have a feeling i need to join two tables together with some projection or selection parameters to get all of this data in one cursor object. Im just not sure how exactly to do this, though ive tried a bunch of different things already, namely being able to get all the contacts, or all the RawContact rows for my specific account type, but not both together.
Thanks
Get Raw_Id's of all contacts matching the account type and name:
getContentResolver().query(ContactsContract.RawContacts.CONTENT_URI,
new String[]{ContactsContract.RawContacts._ID,
},
ContactsContract.RawContacts.ACCOUNT_NAME + " = ? AND " +
ContactsContract.RawContacts.ACCOUNT_TYPE + " = ? ",
new String[]{mAcccountName,mAccountType},null
);
For each _ID, fetch all rows from ContactsContract.Data , ContactsContract.RawContacts._ID matches ContactsContract.Data.RAW_CONTACT_ID here.
These rows have all the data related to that contact's Raw_id.
Refer to the documentation for the structure of ContactsContract.Data table, and what data it holds.
Related
I'm currently doing development on an Android app that requires me to read all the contacts on a device and select only specific contacts based on criteria (only contacts that have at least one valid mobile number and all email addresses linked to that contact).
I've tried the recommended approach at https://stackoverflow.com/a/19563999/3262731, but on a test device with approximately 800 contacts, retrieving all the records and then filtering takes about 17-20 seconds.
Ideally I'd love to build the criteria into a query that joins the contacts, phone, and email store tables in the contacts db as opposed to filtering in my code.
Does anyone have any suggestions please?
The android documentation seems to contain information in what you're looking for found here.
private static final String[] PROJECTION =
{
/*
* The detail data row ID. To make a ListView work,
* this column is required.
*/
Data._ID,
// The primary display name
Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ?
Data.DISPLAY_NAME_PRIMARY :
Data.DISPLAY_NAME,
// The contact's _ID, to construct a content URI
Data.CONTACT_ID
// The contact's LOOKUP_KEY, to construct a content URI
Data.LOOKUP_KEY (a permanent link to the contact
};
return new CursorLoader(getActivity(), contentUri, PROJECTION, SELECTION, SELECTION_ARGS, SORT_ORDER);
More details on how to define your criteria in the documentation. I would think this would be faster than using a ContentResolver as well.
According to http://developer.android.com/reference/android/provider/ContactsContract.Contacts.html
Query
If you need to read an individual contact, consider using CONTENT_LOOKUP_URI instead of CONTENT_URI.
If you need to look up a contact by the phone number, use PhoneLookup.CONTENT_FILTER_URI, which is optimized for this purpose.
If you need to look up a contact by partial name, e.g. to produce filter-as-you-type suggestions, use the CONTENT_FILTER_URI URI.
If you need to look up a contact by some data element like email address, nickname, etc, use a query against the ContactsContract.Data
table. The result will contain contact ID, name etc.
I am building a custom phonebook application. As part of this, I need to add new contacts, or additional info (phone numbers or emails) under existing contacts. While this works well under simple test cases, there are certain situations (and it's not obvious when) where I end up with two issues:
Multiple copies of a contact get created, often hundreds! These are all blank, and simply share the same display name with the original contact that was modified
Multiple numbers (exactly the same) under the contact. The really unusual thing is that these numbers have the same _ID in the contactContracts.Data table, so can't understand how there can be multiple instances with the same unique ID. I checked to see if it's a display issue, but doesn't seem to be
Both of the above makes me question if I am inserting information correctly. So what I want to know is:
When creating a new contact, is there a default account_name and account_type I should be specifying? I am currently using null for both
When I add a new phone number to an existing contact, what is the right approach to inserting data into the contactContracts.Data table? I am currently inserting this number with rawContactID as a reference ID, and I am getting this rawContactID from the contactContracts.RawContacts table, by using my contact's contactID as reference. Note that because I often get multiple raw contacts, I end up picking up the last one (arbitrary choice) to do my phone number insert.
You can use AccountManager Class for getting account name & account type information.
final AccountManager accountTypes = AccountManager.get(getApplicationContext());
final Account accounts[] = accountTypes.getAccounts();
for (final Account account: accounts) {
Log.i(TAG, account.name);
}
I'm trying to insert and update a piece of information on an existing contact so I've created a sample application in order to develop the functionality. All I want my sample app to do is to insert (or if present) update an email address on a contact.
I'm selecting a contact through the system Intent like so:
startActivityForResult(new Intent(Intent.ACTION_PICK, Contacts.CONTENT_URI), PICK_CONTACT_REQUEST);
The URI which is returned is that of the Contact (RawContact?) which was selected and comes in this form:
content://com.android.contacts/contacts/lookup/0r2-2A90214945/2.
I can pull back all of the Data (RawContact?) items on this by performing the following code:
Cursor cursor = contentResolver.query(mContactUri, null, null, null, null);
try {
if (cursor.moveToFirst()) {
for(int i=0; i < cursor.getColumnCount(); i++) {
String message = cursor.getColumnName(i);
Log.v("", message);
}
}
} finally {
cursor.close();
}
From this I should be able to determine if the contact already has an CommonDataTypes.Email Data member:
cursor.getColumnIndex(CommonDataKinds.Email.CONTENT_ITEM_TYPE) != -1;
And then perform one of the following to either Insert or Update the Data:
ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
ops.add(ContentProviderOperation.newInsert(mContactUri)
.withValue(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE)
.withValue(Email.DISPLAY_NAME, "somebody#android.com")
.withValue(Email.TYPE, Email.TYPE_HOME)
.build());
getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);
But this gives me an exception:
java.lang.UnsupportedOperationException: URI: content://com.android.contacts/contacts/lookup/0r2-2A90314945/2, calling user:
Hopefully someone can see what I've missed.
PS, I'm using these permissions:
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
The Android people need to update their documentation. It actually served to make me know less about what was happening than I would have gotten from guessing. It suggests that you can pull back a Contact, which will contain many RawContacts which will contain Data.
That interpretation is completely wrong. ContactContracts data is instead three normal average everyday database tables*:
ContactContract Tables
Table: Contacts
Access URI: Contacts.CONTENT_URI
Primary Key**: Data._ID
Description:
This table contains information about a Contact (when was it added, what's is it's user icon, does it have a custom ringtone).
Relationship: It has a 1-to-many relationship with the RawContact table.
Table: RawContacts
Access URI: RawContacts.CONTENT_URI
Primary Key: Data._ID
Foreign Key**: Data.CONTACT_ID
Description:
This table contains information about a related set of Data items. A RawContact could contain Email Type, Email Display Name, Phone Number, Phone Display Name, etc. A RawContact can be aggregated with other RawContacts to make a Contact as a user sees it. A Contact could contain just one RawContact.
Relationship: It has a 1-to-many relationship with the Data table.
Table: Data
Access URI: Data.CONTENT_URI
Primary Key: Data._ID
Foreign Key: Data.RAW_CONTACT_ID
Description:
This table contains a single field of information. An email address, A phone number, A phone number type (home/work), A nickname, A display name.
In answer to the question
I've uploaded the entire sample project to GitHub in order to allow others to see how to query, update and insert records using ContactContract.
You can find the project to download here:
https://github.com/gwoodhouse/ContactContractSample
If you just want to look at the java code performing the query/update/insert here is the class file:
https://github.com/gwoodhouse/ContactContractSample/blob/master/ContactsIntegration/src/com/woodhouse/example/activity/ContactsIntegrationActivity.java
Hope this helps!
*Not a table, but a ContentProvider
** not strictly true.
I'm building an app where I need the user to select some "favorite" contacts and a phone number. I'm able to select a contact using
startActivityForResult(new Intent(Intent.ACTION_PICK, Contacts.CONTENT_URI), PICK_CONTACT_REQUEST);
and I'm able to extract all the information I need.
I then proceed to save the _id of the contact into my own database. My plan is to later list all the "favorited" contacts and display the name and phonenumber in a listview.
I want to save the contact id instead of the name and number so my listview will reflect any changes the user makes to his or her contacts.
But now I'm stuck. I don't know how to transform my table with contacts ids into a table with contact names.
I would like to something like this
my_table LEFT OUTER JOIN contacts_table ON (my_table.contact_id = contacts_table._id)
What you will want to do here is to store those IDs then when you want to pull the names from the user's address book, you'll have to cross reference the IDs with the contacts intent you have store in order to filter them out.
Basically you cant join a non-existent table (contacts_table).
You could probably just save the contect IDs however you wish, and then loop through the contact IDs and look up the contacts one-by-one extracting the data you need. If that's quick enough you wouldn't need any SQL joins ("optimize by need").
Perhaps its even quicker to use some Google API and use HTTP/XML to fetch the contacts (perhaps cache them for a while as well).
I read that the different entries in different tables are linked via the _ID column in that table. For example a contact might have an _ID = 1 I get via
ContactsContract.Contacts._ID
and now I want to read the phone number of that contact using
Cursor phoneCursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = " + contactId , null, null);
//...
String number = phoneCursor.getString(phoneCursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.NUMBER));
String id = phoneCursor.getString(phoneCursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone._ID));
This works fine, but what I would expect is that if the _ID of the contact is 1 that the _ID of the phone number is as well one since they belong together, but they are not equal. So the question is how does Android match these entries?
Thanks, A.
A contact corresponds to 1 or more raw contacts. The actual data for the contact is stored as
ContactsContract.Contacts.Data
Each data item contains the id of the raw contact it belongs to.
Each raw contact contains the id of the contact it belongs to.
So, given a Contact id you can find what raw contacts it represents. Get the raw contact ids and then find what data is within this contact.
you could get more details when you see the database tables
as far as my understanding goes this is the information I got from viewing android contacts2.db file using sqlite3 db browser
I created some 6 contacts for testing my android contacts
in contacts table the _id and raw_contact_id is actually same (it means that ContactsContract.Contacts provider )
in data table we get the real details of the person like phone number and email and firstname and last name using mimetype Id as the where condition
when considering data table use raw_contact_id as the where condition to get a particular records of the contact
ex my raw_contact_id is 1
I got 3 rows which consists of email, phone and display name in data1 column
you may ask that what if we want only phone or email or display name ....there you will need the mimetype_id as the where condition
to get phonenumber mimetype_id is 5
to get display_name mimetype_id is 6
to get email mimetype_id is 1
you may not understand all these stuff if you are a beginner ...but once if u see the internal tables u get every thing which I told you
access contact details only by contact_id or raw_contact_id and not by _id which is in every table ..that column is for different purpose ..
you can have a look at what Im saying in this image
http://img94.imageshack.us/i/tablesxa.jpg