I am working on a requirement, where I need to identify all Google's contact saved/synced with Android device's phonebook. Then I have to fetch unique contact Id (Google's unique contact id)of each contact which will be same on other devices and other platform.
I have read Android developer's documentation regarding RAW_CONTACT_ID. Also, tried to get raw contact id, but I am getting different value of raw contact id on other devices.
If anyone can put me on right direction, it will really helpful.
If require more information, please ask.
Try using ContactsContract.PhoneLookup
A table that represents the result of looking up a phone number, for example for caller ID. To perform a lookup you must append the number you want to find to CONTENT_FILTER_URI. This query is highly optimized.
Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(phoneNumber));
resolver.query(uri, new String[]{PhoneLookup.DISPLAY_NAME,...
where
PhoneLookup._ID
is what you're looking for.
You may also try the solution provided in this post:
public static int getContactIDFromNumber(String contactNumber,Context context)
{
contactNumber = Uri.encode(contactNumber);
int phoneContactID = new Random().nextInt();
Cursor contactLookupCursor = context.getContentResolver().query(Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI,contactNumber),new String[] {PhoneLookup.DISPLAY_NAME, PhoneLookup._ID}, null, null, null);
while(contactLookupCursor.moveToNext()){
phoneContactID = contactLookupCursor.getInt(contactLookupCursor.getColumnIndexOrThrow(PhoneLookup._ID));
}
contactLookupCursor.close();
return phoneContactID;
}
All _ID values in Android's Contacts are local, they are usually incremental, and are not synced between devices.
The values that might get synced by the app's SyncAdapter (in this case Google's SyncAdapter) are SYNC1, SYNC2, SYNC3, SYNC4.
However, note that these fields are not guaranteed to do anything, and the SyncAdapter may use them for whatever purpose it needs, usually, one of them is used as a "server identifier" you just need to print them, and check manually which one.
Related
I need to query the contacts from an Android device for a project I'm working on and I need to save them in a way I can link between the instance in the app to the contact in the phonebook.
I found that the CONTACT_ID (which is a reference to _ID) of each contact might change between devices, so if I switch to other Android device that ID will not be valid.
A temp solution was using the contact's SOURCE_ID, which is a String that uniquely identifies this row to its source account. The solution was pretty good, because if the contact came from (for example) the Google account, it will stay the exact same ID on every device I'll have. The problem is - not every contact has a SOURCE_ID.
It is also possible to query a specific contact using it's data as filters, which may work as a unique ID, such as his phone number, etc... However every piece of data has a flaw. For example: A contact may have multiple phone numbers (which is still ok) and the numbers can be varied (for example: 202-555-0105 is the same as +1-202-555-0105 which is also the same as (202) 555 0105 and also 2025550105).Edit: Also not every contact has a phone number, so then what?
So after given the problem -
How can I get a unique ID for the contacts in the Android phonebook so they'll be the same cross-device?
Note: It's possible on IOS by default (see documentation) -
Contacts in different accounts that represent the same person may be automatically linked together. Linked contacts are displayed in OS X and iOS apps as unified contacts. A unified contact is an in-memory, temporary view of the set of linked contacts that are merged into one contact.
By default the Contacts framework returns unified contacts. Each fetched unified contact (CNContact) object has its own unique identifier that is different from any individual contact’s identifier in the set of linked contacts. A refetch of a unified contact should be done with its identifier.
What you are looking for is LOOKUP_KEY
An opaque value that contains hints on how to find the contact if its row id changed as a result of a sync or aggregation.
To get the contact LOOKUP_KEY loop through your contacts, here's an example:
Note: <uses-permission android:name="android.permission.READ_CONTACTS" /> is required.
val contentUri = ContactsContract.Contacts.CONTENT_URI
val cursor = context?.contentResolver?.query(contentUri, null, null, null, null)
if (cursor != null) {
while (cursor.moveToNext()) {
val id = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID))
val name =
cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME_PRIMARY))
val lookupKey =
cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY))
}
}
cursor?.close()
Here is how you would go about retrieving the contact with the LOOKUP_KEY:
val lookupKey = "0r1-3C263544104632"
val lookupUri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_LOOKUP_URI, lookupKey)
val uri = ContactsContract.Contacts.lookupContact(contentResolver, lookupUri)
val cursor = context?.contentResolver?.query(uri, null, null, null, null)
if (cursor != null) {
while (cursor.moveToNext()) {
val id = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID))
val name =
cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME_PRIMARY))
}
}
cursor?.close()
}
Please note that contacts have to be synchronised in order for the LOOKUP_KEY not to be re-generated.
The LOOKUP_KEY is guaranteed to be generated for each contact, however, if the account is not set up and not synchronised then the LOOKUP_KEY will be re-generated whenever the contact gets modified.
With that in mind, you'll always have a unique LOOKUP_KEY if the device is synchronised. The LOOKUP_KEY is relying on Google Cloud which may be the same solution that Apple uses.
It will be very unlikely that an Android device will not have a google account since most Android users rely on Google services.
I am afraid this is the best way to have a unique identifier, however, if you'd like, you could hash user phone number combined with other contact details, but this method can not be guaranteed to work as contacts may change. If your users are registered and you'll have their information then you could check on the backend which hash values match your expectation and then work based on your own synchronisation.
If you want to play around, I have created a sample app with the implementation where you can look through your contacts and find the lookup key as well as retrieve the contact with the lookup key.
I would also recommend you to take a look at SyncAdapter.
I am developing an android application and I need to know all the information about phone contacts.
I developed a function to get the name and number of all the contacts, but I need all the information about particular contact such as email, date, favorite or not, image, social links if available.
I got id, name and number from following:
String id = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID));
String name = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
String number = pCur.getString(pCur.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
I used ContactsContract.Contacts to get _ID and DISPLAY_NAME, but
ContactsContract.CommenDataKinds.Phone to get the NUMBER. Is it correct?
Please explain the difference between the two methods.
Is the _ID a unique ID for all the contacts?
After a long discussion with #pskink I finally found the solution to list all related information for each contact in the directory.
First of all, create a cursor:
Cursor cursor = cr.query(ContactsContract.Data.CONTENT_URI, null, null, null, null);
And after that, you can dumb the cursor to show all the informations and see each contact and keywords it needs to use, like (custom_ringtone, display_name, photo_uri, is_primary, ..) by using this line of code:
DatabaseUtils.dumpCursor(cursor);
Special thanks to #pskink
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 did a lot of research but couldn't find anything to help;
my problem is: i need to create an application where the user can select some of the contacts on his phone to be added to this application where he/she can later communicate with them via sms in a special template. but the user need to select only one phone number to be active to this user on this application. this choice must be caved for later logons.
i was able to retrieve contacts and their phone numbers using lookupkey (which will be saved in my application as a reference for preselected users), but i couldn't figure out how to tag the needed phone number, i was thinking of adding a flag to the phone number but i dont know how, i dont know if this is the right way to do it, i thought of setting the selected phone number as primary then query t when needed... or simply save the phone number id (but i am not sure if saving the id is safe in case user changed the phone numer)...
thx for any help...
After a long period of trial and error I found the solution to my problem. I will be using the contacts lookup key to store the contact and the phone id to store the phone number...as follows:
String selection = ContactsContract.CommonDataKinds.Phone.LOOKUP_KEY + " = '" + lookupkey+ "' and "+Phone._ID+"='"+phoneid+"'";
String[] projection =new String[] {Phone._ID, Phone.DISPLAY_NAME, Phone.NUMBER};
Cursor managedCursor = getContentResolver()
.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
projection, selection, null, Phone.DISPLAY_NAME + " ASC");
I'm a bit new to all this stuff. As I understand it, an id match is fast, but may not be stable. If a key+id match fails, you should also try a slower match using only the key - Jeffrey Scofield's SQL selection string could be changed to try id-and-key OR key-only (trusting the query optimiser to prioritize the id match).
I haven't had much luck finding information concerning the wisdom of storing the key long term.
I'm trying to figure out if there is a property in Android contacts that states if a contact has been modified.
I read that it was possible to read a contact's version via the field ContactsContract.Contacts.Entity.VERSION, but I wasn't able to retrieve its value. I always get an IllegalArgumentException.
Does someone know how to get the contact's version or if there is another way to find out if a contact has been modified since the last scanning of the address book?
Heres the code, I havent tested it, as I was busy with my work.
public void getVersion() {
//specify which fields of RawContacts table to be retrieved.
String[] projection = new String[] {RawContacts.VERSION};
//query the RawContacts.CONTENT_URI
Cursor cur = mContext.getContentResolver().query(RawContacts.CONTENT_URI, projection,null,null,null);
while(cur.moveToNext()){
String version = cur.getString(cur.getColumnIndex(RawContacts.VERSION));
Log.i("VersionRetriever", version);
}
//Always remember to close the cursor. Otherwise it leads to side-effects.
cur.close();
}
try RawContacts.CONTACT_STATUS_TIMESTAMP instead of RawContacts.VERSION.
If you want to know if a specific data type (like phone or name), was changed you can check Data.DATA_VERSION (but on Data.CONTENT_URI).