what is the difference between RawContacts.CONTENT_URI and ContactsContract.Contacts.CONTENT_URI in android?
I am trying to write a service which listens to changes in native address book. So, which one to use?
thanks
Niz
A Contact (as the user perceives it) is an aggregate set of RawContact's
A RawContact being some details associated with a certain account or protocol
The ContactsContract.Contacts.CONTENT_URI is generally the correct one to use as it brings back the correct display name from the entire set of matching RawContacts and can help you join all those accounts that are linked
The RawContacts.CONTENT_URI can be used if you want to match a specific [set of] contact relating to an account/protocol
and then there are various data types / directories to bring back even broader ranges of data.
The question of which one should you use comes down to what data you are watching and if/how you are displaying it, as well as how many rows you are trying to watch. I would say more often than not it's correct to use the ContactsContract.
It also depends how granular you want your 'notifications'. I.e. a content observer callback can give you a URL to show you what changed, but nothing more, which means if you want to only act on the specific row that you know has changed then you're going to have to observe different things than if you just want to know some change has occurred and that it's time to notifyDataSetChanged!
Related
Background
I work on an app that needs to get information of "other contacts" as they appear on the address book page on "Contacts" website of Google:
The problem
The Contacts API is becoming deprecated, and instead we need to use People API.
For this, I use the Java library (here) which makes it easier to reach the various functions.
Sadly, according to the docs (here), querying the list of items from "other contacts" you will get only up to 3 possible fields:
emailAddresses
names
phoneNumbers
Indeed, when using it, that's what I got. I also got fields of "etag" and "resourceName", but that's it. No photos, no cover photos, no nothing else...
The code to do it is very short (after you set everything up) :
val otherContactsResponse =
otherContacts.list().setReadMask("emailAddresses,names,phoneNumbers")
.setPageSize(itemsCountToRequest).setPageToken(nextPageToken).execute()
val result=otherContactsResponse.otherContacts
What I've tried
I tried to see if I can query by "reourceName" (example this one), but it seems that "other contacts" are handled differently than normal ones, so this won't work:
result.forEach { personBasic: Person ->
val test: Person? = peopleService.get(personBasic.resourceName!!)
.setPersonFields(
"addresses,ageRanges,birthdays,coverPhotos,emailAddresses,genders,metadata,names,nicknames,occupations,organizations,phoneNumbers,photos,urls")
.execute()
Log.d("AppLog", "$test")
}
Sadly there is no similar query for "other contacts" part to search (other than here, which gives you again the same 3 fields).
Tried to find if there are other fields that I missed that could be used somewhere else. I don't even know if resourceName or etag are reliable and unique for querying.
Tried to add more fields to the query, despite the docs talking about just 3 fields. Failed, of course...
There is a function called copyOtherContactToMyContactsGroup (here) which seems that it will copy contacts to the main group of contacts. I guess that if I use this and then query the contacts themselves (not just "other contacts"), I could get the needed information. But this is a waste of time and can pollute the user's address book, even temporarily. I would have to make sure I delete the contacts from there right after I add them...
EDIT: tried this too, and while it seems to work, I don't want to use it as it pollutes the address book with contacts that the users hasn't added. Plus I got sometimes an image which is simply the letter of the person with a background. This is the code of it:
result.firstOrNull { !it.names.isNullOrEmpty()&&!it.emailAddresses.isNullOrEmpty() }?.let { person ->
val request =
CopyOtherContactToMyContactsGroupRequest().setCopyMask(
"emailAddresses,names,phoneNumbers")
.setReadMask(
"addresses,ageRanges,birthdays,coverPhotos,emailAddresses,genders,metadata,names,nicknames,occupations,organizations,phoneNumbers,photos,urls")
val copyResult: Person? =
services!!.otherContacts.copyOtherContactToMyContactsGroup(
person.resourceName, request).execute()
Log.d("AppLog", "success $copyResult")
}
Also, this seems to be extremely slow compared to simple queries, and there is no batch operation for it either. In addition, testing it out, I think it can cause various server issues related to quota being reached (about 180 per minute or so).
I think that as the website shows the photos and can show you per-contact information, we should have this API too, no?
I thought that maybe I could create a new group (label) , copy the "other contacts" to there, and then fetch the information from there (as it allows to get more fields), but the docs (here) say that it was possible before, and not anymore:
The only system contact groups that can have members added are contactGroups/myContacts and contactGroups/starred. Other system contact groups are deprecated and can only have contacts removed.
This is a bit better solution (if it worked) as I can remove groups safely without affecting actual contacts.
The question
What's the best way that I should use in order to get all kinds of information (like normal contacts) about each item in the "other contacts" list? Is it possible without copying to the contacts list and without getting "fake" photos (of the contact initial letter) ?
OK so sadly for now I got a workaround.
First I copy the various fields to a new Person object, one for each of the results of the query, then I add them all using a batchCreateContacts (here, max items per batch is ), and then I deleted them all using batchDeleteContacts (here , max items per batch is 500)
Sadly this still pollutes the address book, but it's much faster than what I've found, and it's quite temporary too.
Weird things I've found :
API usage doesn't seem to show the exact same items as on the website. I know because I tried to find names and emails on the website that I've found from the API but it didn't show them. Even weirder: The count of items seems the same between them
Some items just have names, which is weird because: how did they get there?
"Other" contacts are different from "normal" contacts and have less information available, but why is this? I looked into this a little and found the following:
Save contact info when you interact with people
When you interact with people on Google products, you can automatically save their contact info, including names, email addresses, and phone numbers. When this setting is turned on, you'll keep the contact info for:
People you've shared something with, like a document in Drive
People who share content with you, like shared albums in Google Photos
People included in events or groups you're in
I think that these are the "other" contacts that become a sort of staging ground with minimal collected information.
I have also found that, on the Google Contacts web page that whenever I add information to an "other contact," the contact automatically disappears from the "other" section and appears with the regular contacts.
So, from the web interface, it looks like "other" contacts should only have the minimal information such as name, email address and phone numbers and should be promoted to the regular contact list if information is added/updated. This scenario will explain why "other" contacts are treated differently.
If you are seeing "other" contacts with more than the minimal information, maybe that is the problem.
Update: Well, it turns out that any "normal" contact can be hidden in the web interface and all the information is transferred with the contact to the "other contacts" list, so "other" contacts can have more than the minimal information.
This looks like an unintended state of affairs to me.
I am creating an android contacts app, so I regularly read user's contacts and store them in my app. To do this, I need to rely on some kind of ID so that I know which contact I should update (or add/delete) in my app and Contacts Provider supplies several of them:
CONTACT_ID is the aggregate contact id,
each aggregate contact consists of one or more Raw Contacts, each with its own RAW_CONTACT_ID,
and most important, each Raw Contact has a SOURCE_ID, which is supposed to be the server id, i.e. the id this contact has in this account's server.
I 've chosen to rely on SOURCE_ID, since this sounds like the most stable one. E.g. when user removes and re-adds the same account in their device, I wouldn't want this account's contacts to get different IDs, because I wouldn't be able to match them in my app.
However, only Gmail sync adapter seems to keep the promise documented below. Exchange sync adapters unfortunately do not, SOURCE_ID changes, and it's definitely not any server id, since it has a small number like 23:4.
Question: Any ideas how to overcome this problem? Am I using the right ID for the intended use? Does exchange adapter stores the "permanent server id" in some other field?
Documentation: The SOURCE_ID must be unique for each account type and should be stable across syncs:
Unique: Each raw contact for an account must have its own source id. If you don't enforce this, you'll cause problems in the contacts
application. Notice that two raw contacts for the same account type
may have the same source id. For example, the raw contact "Thomas
Higginson" for the account emily.dickinson#gmail.com is allowed to
have the same source id as the raw contact "Thomas Higginson" for the
account emilyd#gmail.com.
Stable: Source ids are a permanent part of the online service's data for the raw contact. For example, if the user clears Contacts Storage
from the Apps settings and re-syncs, the restored raw contacts should
have the same source ids as before. If you don't enforce this,
shortcuts will stop working.
LOOKUP_KEY is what you're looking for.
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.
You should use a pair of <CONTACT_ID, LOOKUP_KEY> to keep track of contacts.
In normal use, use the CONTACT_ID value, but if your code gets a hint that the CONTACT_ID has changed (either missing, or unexpected contact name), you can use the LOOKUP_KEY to find the new contact-id.
Or you can use Contacts.getLookupUri() to get a URI you can always use to quickly find a contact no matter what its CONTACT_ID or LOOKUP_KEY actual values are.
First thing, it's not a good idea to store the Id in your application and hoping that the Id won't change over time and be consistent. You are right about 'SOURCE_ID' column that it's more consistent compared to the other two (CONTACT_ID is the most brittle while 'RAW_CONTACT_ID' persists at least until user logs out of an account and logs in again).
We had a syncable account with contacts and we used to keep the unique id in one of the general purpose columns in the 'raw_contacts' table (SYNC1 - SYNC10). So although Google suggests that account providers use the database columns in a certain manner but it's upto the provider totally.
General rule to follow is, never use those id's for long term persistence and if you do so, expect them to change. Also since you are making a contact App, you obviously need some kind of reference key. In such case, don't go by the rule that all account providers will put their key in the same column. It's brittle but that's the way it is.
Edit - You should use ContactsColumns.LOOKUP_KEY (previous answer also cited the same). As per the Google Documentation -
LOOKUP_KEY
Added in API level 5 String 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.
Constant Value: "lookup"
https://developer.android.com/reference/android/provider/ContactsContract.ContactsColumns.html#LOOKUP_KEY
You can fetch the lookup key if you have the contact Id using API's provided. Look here - https://developer.android.com/reference/android/provider/ContactsContract.Contacts.html
Hi everyone i'am currently developing an android game, it uses your phone number when you first use the game(it is for an identification of the user).
It is an social game at some time when the user requests it returns a list of people who are currently online(playing the game),as json array(there phone number,name,photo)and from the data received you need to filter it out and display in two list views. The filtering done by this method:
1)Each element from JSON data(each person)if there phone number is found in user's contact list then is displayed in the first listview
2)if the number is not found in users contact list it is displayed in second listview.
But my doubt is that if the JSON array returns details of 10 people, you need to check for this 10 people whether they are in user's contact list.And does this kind of a technique takes a lot of time and creates delay in the UI..?
If yes please suggest me some other method.
Also please tell me how can I check whether a number is in user's contact list(I make the users upload there number to server without any country code).
So please in the number finding method you need to end the checking one a number has been fully completed.(starting from the end.
eg : 8086934565 (no saved in database) +918086934565(no in contact list)
then we need to start from last so that
5==5 then continue
6==6 then continue
till any of the no (+918086934565 or 8086934565) ends without breaking any equivalent condition.
eg for numbers +918086934565 and 8086933365
checking from the last
5==5,6==6, 5!=3 there the equivalent condition goes wrong and hence it should get out from the loop with a flag ie the numbers are not equal.
Please write me the function to do this or tell me an alternative method.
Any help would be appreciable.
Thanks.
Ugh, that's painful to read...haha...You're suggesting using phone numbers to identify users in a multiplayer matchmaking scenario? That's taboo...If you return a JSON array of users phone numbers, names, and photos, your app will almost certainly be permanently banned from the market and possibly your entire account. That is about the biggest security risk that I can possibly think of, you might as well have a textfield where everybody types in their social security number and it posts it to craigslist. Basically any of your users could just repeatedly initiate games with people and get names photos and phone numbers of 10 random people at a time by capturing the plain text JSON data coming back. If I were you I'd look into the facebook sdk for authentication or Google Play Game Services for multiplayer matchmaking unless the core mechanics of your game rely on phone numbers specifically, in which case by it's very nature is just not going to fly with users. Asking someone to write you a function to do that is most likely never going to happen on stackoverflow. I'm not even going to get into the last part of the question...This has to be a troll attempt...lol
I am interested in your opinions on the best way to keep a robust reference to a contact starting android 2.0. I have read a lot regarding this issue but i don't know which technique is advisable and if there's any new ones. Below are the 3 options i found so far:
1- Don't use your own db. Extend the contacts.contract to write your data
2 - Use ContentObserver to keep track of any changes made to the contacts. This technique would however require your application to be running
3- Store all RAW contact IDs related to a CONTACT_ID in your db and infer the content of the aggregate contact from all its constituent RAW contacts
In my case i need to keep a separate db and i was wondering if there's any other new technique of keeping the CONTACT_ID current after aggregations and dis-aggregations...
Please advise
I am playing with the new Contacts API at the moment, writing an app which is also a source of contact data. I can't recommend #3 at all. To get all of "my" contact data I simply store them with a magic value in the RawContacts.SYNC1 field and query the contacts content provider for that when my app starts up.
Re. your question, don't use CONTACT_IDs, as they are volatile. Use the lookup API, i.e. if you have a raw contact ID, use RawContacts.getContactLookupUri() to get to the Contact. Likewise if some API gives you a contact ID, use Contacts.getLookupUri() to get a persistent identifier you can use later on.
I'm pretty sure it's not viable to do what I'd like to based on some initial research, but I figured it couldn't hurt to ask the community of experts here in case someone knows a way.
I'd like to create a custom field for contacts that the user is able to edit from the main Contacts app; however, the user should only be allowed to select from a list of four specific values. A short list of string values would be ideal, but an int with a min/max range would suffice.
I'm interested in knowing if it's possible either way, but also wondering if it make sense to go this route performance wise. More specifically, would it be better to look up a contact (based on a phone number) each time a call or SMS message is received or better to store my own set of data (consisting of name, numbers, and the custom field) and just syncing contact info in a thread every so often? Or syncing contacts the first time the app is run and then registering for changes using ContentObserver?
Here is a similar question with an answer that explains how to add a custom field to a contact.
Thanks in advance.
I don't see the purpose to have your own set of data against contacts stored in your separate database, as you obviously will run into sync issues. You can use the mimetype and store whatever you want against the contact, in the manner you linked.
Whenever you want to looup contacts you can do that by using your custom mimetype. You get the contact id (from ContactsContract.Data.CONTENT_URI), and then you run another query to get the contact details (from ContactsContract.Contacts.CONTENT_URI). Please note these are different tables.
I'd like to create a custom field for contacts that the user is able to edit from the main Contacts app
I don't see that possible, editable from the main app, when you use your custom mimetypes, and you don't have much options here. The main contact app will display only the fields that are in SDK. You can store details against contacts but they won't show up in the inbuilt edit contact screen.