Android contacts: How does the lookup key works? - android

On top of contacts id, Android also got LOOK_UP key. Since id of contact can change, you can obtain user uri, using LOOK_UP key.
public static Uri lookupContactUri(String lookup, Context context){
ContentResolver contentResolver = context.getContentResolver();
Uri lookupUri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_LOOKUP_URI, lookup);
return ContactsContract.Contacts.lookupContact(contentResolver, lookupUri);
}
But how does it work? The source code of the Contacts.lookupContact doesn't tell much about the actual implementation. So can anyone explain how does they manage to pull this up?
/**
* Computes a content URI (see {#link #CONTENT_URI}) given a lookup URI.
* <p>
* Returns null if the contact cannot be found.
*/
public static Uri lookupContact(ContentResolver resolver, Uri lookupUri) {
if (lookupUri == null) {
return null;
}
Cursor c = resolver.query(lookupUri, new String[]{Contacts._ID}, null, null, null);
if (c == null) {
return null;
}
try {
if (c.moveToFirst()) {
long contactId = c.getLong(0);
return ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
}
} finally {
c.close();
}
return null;
}
Another thing I tested, is merging two contacts using ContactsContract.AggregationExceptions and then quarrying for contact uri. Both of the LOOK_UP keys yield with the same contact uri as expected.
So how are they doing it?

Since contact ids can change from time to time (e.g. when contacts sync is corrupted and contacts needs to be resynced from server), Android introduced the concept of LookupKeys and LookupUris.
A LookupKey is an opaque value, that internally can be translated by the Contacts framework to a set of fields: contact-id, raw-contact-ids, primary-display-names, etc.
Whenever you try to access a contact via a LookupUri, the system, extracts the LookupKey from the Uri, tries to access the contact-id, and compares the other fields (raw-ids, names, etc.) to the found contact, if it seems the right contact, it returns it.
If the contact-id wasn't found, or the system detects it's the wrong contact, a query is made over all contacts to find the right one (using the auxiliary fields stored on that key).
So the LookupKey acts as a quick method to either return the contact-id, or search for it in case something bad happened.

Related

How do i check if a phone number is already saved on my android phone or not?

I am building an app that enables users to save and manage a list of people, with important dates such as birthdays, anniversaries, Etc. for each person.
The goal, is to send them a personalized PDF image (with their name and details), on the important day.
The problem I am having, is what to do if the user saves a person on my app, that isn't in their contact list.
In such a case, I can't send him a whatsapp message.
So, I want to add the phone number to the phone's contacts list, only if the phone number isn't already saved in the user's regular contacts list.
How do I check if phone number XXX-XXX-XXXX exists in user's contact list or not ?
String res = null;
try {
ContentResolver resolver = ctx.getContentResolver();
Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(phoneNumber));
Cursor c = resolver.query(uri, new String[]{PhoneLookup.DISPLAY_NAME}, null, null, null);
if (c != null) { // cursor not null means number is found contactsTable
if (c.moveToFirst()) { // so now find the contact Name
res = c.getString(c.getColumnIndex(CommonDataKinds.Phone.DISPLAY_NAME));
}
c.close();
}
} catch (Exception ex) {
/* Ignore */
}
return res;

How can show device primary contact viewer to show ONE contact with phone number

I'm writing an app and I need to show a contact to user. I have the phone number and I can make a query to get the contact detail. Like this:
ContentResolver cr = context.getContentResolver();
Uri uri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI,
Uri.encode(phoneNumber));
Cursor cursor = cr.query(uri,
new String[]{ContactsContract.PhoneLookup.DISPLAY_NAME}, null, null, null);
if (cursor == null) {
return null;
}
String contactName = phoneNumber;
if (cursor.moveToFirst()) {
contactName = cursor.getString(cursor
.getColumnIndex(ContactsContract.PhoneLookup.DISPLAY_NAME));
}
BUT I need to show the DEVICE primary contact viewer and I do't know How?
Like this:
This is all documented in official Android docs, Common Intents, chapter "Contacts/People App".
EDIT
But unfortunately I can not access to your link , nor google documents also in my area !!
Here're quotes from linked docs:
View a contact
To display the details for a known contact, use the ACTION_VIEW action and specify the contact with a content: URI as the intent data.
There are primarily two ways to initially retrieve the contact's URI:
Use the contact URI returned by the ACTION_PICK, shown in the previous section (this approach does not require any app permissions).
Access the list of all contacts directly, as described in Retrieving a List of Contacts (this approach requires the READ_CONTACTS permission).
Example:
public void viewContact(Uri contactUri) {
Intent intent = new Intent(Intent.ACTION_VIEW, contactUri);
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
}
}
Edit an existing contact
To edit a known contact, use the ACTION_EDIT action, specify the contact with a content: URI as the intent data, and include any known contact information in extras specified by constants in ContactsContract.Intents.Insert.
There are primarily two ways to initially retrieve the contact URI:
Use the contact URI returned by the ACTION_PICK, shown in the previous section (this approach does not require any app permissions).
Access the list of all contacts directly, as described in Retrieving a List of Contacts (this approach requires the READ_CONTACTS permission).
Note: Extras - One or more of the extras defined in ContactsContract.Intents.Insert so you can populate fields of the contact details.
public void editContact(Uri contactUri, String email) {
Intent intent = new Intent(Intent.ACTION_EDIT);
intent.setData(contactUri);
intent.putExtra(Intents.Insert.EMAIL, email);
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
}
}
For more information about how to edit a contact, read Modifying Contacts Using Intents.

How to detect a duplicate contact when Inserting a new one?

For a Contacts backup app, I save all the information to a CSV file, and then I need to restore it back. It works great, however if I press restore twice, it duplicates all the contacts.
I tried the following code to remove duplicates, it does work but fails in certain cases.
Basically it fails when there is no explicit DISPLAY_NAME, for e.g. if a contact seems to only have a phone number and the DISPLAY_NAME is the phone number, or same for an email address. I cannot understand why it wont always work since it does seem that the DISPLAY_NAME field contains phonenumber/email address.
Here is the code that I used:
private boolean contactExists(String displayname, String emailstring, String phonestring){
Cursor crsr = BA.applicationContext.getContentResolver().query(
ContactsContract.Contacts.CONTENT_URI,
new String[] { "display_name", "_id"},
"display_name = ? ",
new String[] {displayname},
null);
while (crsr.moveToNext()){
HashMap m = new HashMap();
for (int col = 0; col < crsr.getColumnCount(); col++) {
m.put(crsr.getColumnName(col), Integer.valueOf(col));
}
int id = crsr.getInt(((Integer)m.get("_id")).intValue());
String emails = GetEmails(id);
String phones = GetPhones(id);
if (emails.contentEquals(emailstring) && phones.contentEquals(phonestring))
{
crsr.close();
return true;
}
}
crsr.close();
return false;
}
UPDATE:
I tried with DISPLAY_NAME_PRIMARY with the same results.
However what I noticed is that, if I create the contacts on the same device/emulator, the duplicate is detected, when I re-restore the same contacts.
On going across devices, it seems that one reason it does not work is that at some point the special characthers are removed.
For e.g. the display name "John.Doe" is read from the CSV, but when it gets inserted, it becomes "John Doe". I cannot see where in the code the "." is ever stripped out.
What happens depends on the version of Android the device is running. If the version is Honeycomb (3.0) or later, the contact will always have a name. The name field is DISPLAY_NAME_PRIMARY, and if there's no name in any of the raw contacts, this field is set to a phone number or email address.
It's hard to know exactly what's going on with your code, because I can't tell how you're calling contactExists in all cases. But my guess is that you're looking at DISPLAY_NAME, when you may want to look at DISPLAY_NAME_PRIMARY.
As a side comment, what you're trying to do here is fraught with peril. The contacts provider is a complex system, and backing it up to a CSV may cause a lot of problems down the road. A much better strategy is to run a sync between the contacts provider and the cloud-based Google Contacts app.
Here is the code which finds duplicate contact. You need to pass the "NAME" as string and it will look for duplicate. It works in ICS but didn't check in GB, so basically you need to try your luck.
/**
* #param name
* #param context
* #return
*/
public boolean isContactExist(String name) {
boolean result = false;
try {
ContentResolver contentResolver = getContentResolver();
Uri uri = Data.CONTENT_URI;
String[] projection = new String[] { PhoneLookup._ID,
PhoneLookup.LOOKUP_KEY };
String selection = StructuredName.DISPLAY_NAME + " = ?";
String[] selectionArguments = { name };
Cursor cursor = contentResolver.query(uri, projection, selection,
selectionArguments, null);
if (cursor != null) {
while (cursor.moveToNext()) {
/*
* Log.i(TAG, "KEY = " + cursor.getString(cursor
* .getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY)));
*/
result = true;
}
}
cursor.close();
} catch (Exception e) {
result = false;
e.printStackTrace();
}
return result;
}

Is there an accepted/received way of checking to see if a person (phone number) is in the Contact List?

Is there an accepted/received way of checking to see if a person (phone number) is in the Contact List?
I'm hoping there's something I can call like this:
bool bInContactList = InContactList("1415922353");
It is not as simple as you want but you can query a Contacts content provider for the contact associated with a phone number:
Uri lookupUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(phoneNumber));
Cursor c = getContentResolver().query(lookupUri, new String[] {PhoneLookup._ID}, null, null, null);
if (c.getCount() > 0) {
// there is some contact
} else {
// there is no contacts with phoneNumber
}
The application needs android.permission.READ_CONTACTS permission to access contacts data.
You can check Android Developer site for further references about content providers and android.providers package documentation for the list of available standard providers in Android.
I am afraid you did not provide enough information about the language you are talking about. If you use Java -for example- you have a contains method (common to all collections) so, if you want to know if a certain String is contained in a collection you could do it by invoking this method:
boolean found = someCollection.conmtains("1415922353");

Extract a contact's photo

Gah, another scenario here something that should be simple is proving to be very time-consuming and painful.
I'm using this to query the contacts provider:
private Cursor getContacts(){
Uri uri = ContactsContract.Contacts.CONTENT_URI;
String[] projection = new String[] {
ContactsContract.Contacts._ID,
ContactsContract.Contacts.DISPLAY_NAME,
ContactsContract.Contacts.PHOTO_ID
};
......
return managedQuery(uri, projection, selection, selectionArgs, sortOrder);
}
This works fine and retrieves contact names, and on a handful of contacts it shows a numeric ID for the PHOTO_ID field, which I assume is the PHOTO_ID I'm requesting. But then I push that ID into this method to extract the bitmap, it fails on every contact and the stream is null every time. I'm testing against a set of contacts that includes some with Android contact photos (I know there are some issues extracting photos from Facebook contacts).
private Bitmap loadContactPhoto(long id) {
Uri uri = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, id);
InputStream input = ContactsContract.Contacts.openContactPhotoInputStream(contentResolver, uri);
if (input == null) return null;
Bitmap bitmap = BitmapFactory.decodeStream(input);
return bitmap;
}
What have I missed?
openContactPhotoInputStream() takes the uri of the contact, try calling it with the ContactsContract.Contacts._ID column instead of the PHOTO_ID column and you should see better results.
There's a bunch of relevant discussion here with some code to check out:
How do I load a contact Photo?
Note that in some cases you'll see a photo in the native contacts app which won't load through the content resolver. Some sync info, like Facebook for example, is flagged to be used only by the contacts app itself and doesn't get exported to other apps :-(
However, using the contactUri should take care of at least some of your issues.

Categories

Resources