I've implemented a contacts app, and I would like my application's contact names to be displayed in the device's call log history (Phone app) in case I receive/make a call to these numbers. How could I achieve that?
The CallLog.Calls table contains fields for caching names, because these are cached names, they're not expected to always be true, and are refreshed from time to time.
Usually, in most Phone/Call-log apps, when you open the call-log it'll display the calls list along with their cached names stored in the Calls table, and then spin up a background service that refreshes cached names, adding names to numbers recently saved as contacts, or updating names that had recently changed.
So if your app stored some number from the call log as a contact, if you then launch the call log app you should see the updated name appearing within a second or two.
If you want to store that name programatically in your code, you can do that easily:
String someNumber = "+12125551234";
String aName = "Jane Addams";
int numberType = Phone.TYPE_MOBILE; // see https://developer.android.com/reference/android/provider/ContactsContract.CommonDataKinds.Phone#column-aliases
final ContentValues values = new ContentValues(2);
values.put(Calls.CACHED_NAME, aName);
values.put(Calls.CACHED_NUMBER_TYPE, numberType);
// on Lollipop+ device, you can also set Calls.CACHED_LOOKUP_URI and Calls.CACHED_FORMATTED_NUMBER
getContentResolver().update(Calls.CONTENT_URI, values, Calls.NUMBER + "='" + someNumber + "'", null);
Thank you #PedroHawk. I found the answer in the link you provided. More specifically, I will create an Account of my app in the Device Accounts and then use a SyncAdapter to sync the contact data from my web service to the ContactsProvider of the device.
Related
So I'm reading in these contacts using Android's native implementation, using a test phone with about 1,056 contacts on it. The problem I'm having is that it's rather slow, so I'm logging the outcomes of my cursor.moveToNext(), and seeing that it's reading in contacts past the last in an overlapping, duplicating fashion. It does, however, have previous logic implemented so the app compares the hashed version of this list of entries to the previously saved one every second... which eventually brings the entries back to the correct value, order, and contents. However, using the code below, at worst it's pulling the entire contact list AGAIN, and then putting it in there with the same list (essentially reads the same address book twice and reads it in, doubling entries). This gets worse the larger your contact list is (smaller books, like this Galaxy I have with 9 contacts, don't seem to be affected; whereas my phone with about 106 is slightly, and this tester with 1,053 significantly) with the larger phones I've tested taking upwards of a minute and a half to two minutes before it's fully updated, accurate, and done.
What's confusing is that it somehow, even after all this duplication, the check manages to come back and be exactly what it was supposed to be (i.e 1,053 contact phone I see it adding the 2,106th row, then it immediately loads the proper 1,053 contact book).
Here's how my cursor is defined:
Cursor c = mContext.getContentResolver().query(addressBookURI,
CONTACTS_QUERY_PROJECTION, CONTACTS_QUERY_SELECTION, null, CONTACTS_QUERY_SORT);
Here's each of the components of said Cursor "c":
private static final Uri addressBookURI = ContactsContract.CommonDataKinds.Phone.CONTENT_URI.buildUpon().appendQueryParameter(ContactsContract.REMOVE_DUPLICATE_ENTRIES, "1").build();
private static final String[] CONTACTS_QUERY_PROJECTION = {
ContactsContract.Data.CONTACT_ID,
ContactsContract.Contacts.HAS_PHONE_NUMBER,
ContactsContract.Contacts.DISPLAY_NAME,
ContactsContract.Data.DATA1,
ContactsContract.Data.DATA2,
"account_name"
};
private static final String CONTACTS_QUERY_SELECTION = "in_default_directory=1 and has_phone_number=1 and mimetype='vnd.android.cursor.item/phone_v2'";
private static final String CONTACTS_QUERY_SORT = "contact_id ASC";
Processing the Cursor "c"'s reads:
List<String[]> entries = new ArrayList<>();
while (c.moveToNext()) {
String id = c.getString(0);
String name = c.getString(2);
String phone = c.getString(3);
String type = c.getString(4);
String account_name = c.getString(5);
Log.d(sTag, "contact:row:" + id + ":" + name + ":" + phone + ":" + type + ":" +
account_name);
entries.add(new String[]{id, name, phone, type});
It's in that Log.d() statement where I can see duplicates of contacts, wherein the only difference between the original contact and follow-up duplicates is the "id" variable (keeps increasing, period).
For example (if it happened with smaller amounts of contacts; all values are made up):
contact:row:1:Maurine Lastpass:4145737719:3:D8:0B:9A:CC:88:BF
contact:row:2:Blondell Sosig:4013008122:3:D8:0B:9A:CC:88:BF
contact:row:3:Amber Altingle:8885554422:2:D8:0B:9A:CC:88:BF
contact:row:4:Frank Helgenson:8885554422:2:D8:0B:9A:CC:88:BF
contact:row:5:Hiro Xin:8885554422:2:D8:0B:9A:CC:88:BF
contact:row:6:Baley Balix:6316773675:2:D8:0B:9A:CC:88:BF
contact:row:7:Henry Halgor:6316773675:2:D8:0B:9A:CC:88:BF
contact:row:8:Hammy Xevronic:6316773675:2:D8:0B:9A:CC:88:BF
contact:row:9:Maurine Lastpass:4145737719:3:D8:0B:9A:CC:88:BF
contact:row:10:Blondell Sosig:4013008122:3:D8:0B:9A:CC:88:BF
contact:row:11:Amber Altingle:8885554422:2:D8:0B:9A:CC:88:BF
contact:row:12:Frank Helgenson:8885554422:2:D8:0B:9A:CC:88:BF
contact:row:13:Hiro Xin:8885554422:2:D8:0B:9A:CC:88:BF
contact:row:14:Baley Balix:6316773675:2:D8:0B:9A:CC:88:BF
contact:row:15:Henry Halgor:6316773675:2:D8:0B:9A:CC:88:BF
contact:row:16:Hammy Xevronic:6316773675:2:D8:0B:9A:CC:88:BF
I've tried storing the very first contact and letting it read from the Cursor until it matches up with that contact, but since the "id" variable keeps increasing, it makes the contact different enough to be ignored. I removed the ID portion of the contact, changed the URI from the default to one with an explicit REMOVE_DUPLICATE_ENTRIES = 1 parameter on it, and I tried to bring in the "real" count using ContactsContract.Data._COUNT in order to check the number of entries on the cursor against it, but that just crashes with "no such column _COUNT".
Is there any reason why the cursor would be pulling duplicate contacts like this? Is there something wrong with the structure of my query that is causing this sort of duplication?
let's recap the way ContactsContract DB is organized:
Contacts table - contains one row per contact, but hardly any information
RawContacts table - can have multiple rows, each assigned to a single contact-ID (from the previous table), contains a logical group of multiple Data rows usually for a single SyncProvider such as Google.
Data table - contains the actual data of a RawContact, each row has a MIMETYPE column which states what kind of data this row is about (phone, email, name, etc.) + 15 data columns to hold the information itself.
There are also pseudo tables, such as ContactsContract.CommonDataKinds.Phone which you are querying in your code, which basically queries over the Data table but with a specific MIMETYPE value, for example Phone.CONTENT_ITEM_TYPE.
In your code you are querying over CommonDataKinds.Phone.CONTENT_URI which means you are asking for all the different phone numbers in the DB.
So if a single contact has 3 phone numbers, you will get 3 rows for that contact (each with a different phone number).
However looking at your output, it seems like every contact has a single phone number in the DB, but it looks like the contacts themselves are duplicated.
So for example Amber Altingle has contact ID 3 and also 11, which means you have two separate contacts named Amber Altingle.
This is not duplication in the query code, but possibly duplication in the contact creation code.
I am developing an app to get CallLogs. From CallLogs i get Cached Name of the person calling. Now if i change the name of the person in the contact List. it is not reflected in CallLogs. How to handle it
The CallLog.Calls table contains fields for caching names, because these are cached names, they're not expected to always be true, and are refreshed from time to time.
Most Phone/Call-log apps will display the calls list along with their cached names stored in the Calls table, and then spin-up a background thread to refresh those cached names columns - adding names to numbers recently saved as contacts, or updating names that had recently changed.
So if you've updated a contact's name, and then launch the call-log app you should see the updated name appearing within a second or two.
If you want to update the cached name programatically in your code, you can do that easily:
String someNumber = "+12125551234"; // this is format-sensitive, so it must be exactly as it appears in the call-log
String aName = "Jane Addams";
final ContentValues values = new ContentValues(2);
values.put(Calls.CACHED_NAME, aName);
// on Lollipop+ device, you can also set Calls.CACHED_LOOKUP_URI and Calls.CACHED_FORMATTED_NUMBER
getContentResolver().update(Calls.CONTENT_URI, values, Calls.NUMBER + "='" + someNumber + "'", null);
My problem is that I try to disallow to native Android Contacts application to delete my application's contacts (which are specified by my application account type) from device.
For that I've specified my own SyncAdapter with it's own Service and it's meta-data described in syncadapter.xml. The value of supportsUploading is set to false (this way I say that contacts created by my application are read-only)
However, when I try to delete a contact of my application from standard Contacts app I get the message which says:
You can't delete contacts from read-only accounts, but you can hide
them in your contacts list
Everything seems fine until I try to get data of the contact which I previously deleted ("hide") from standard "Contacts" in my own application.
The returned cursor is null because there isn't any row in Data table associated with this contact's RAW_CONTACT_ID. I also check if the contact exists on the device looking for
it's DELETED flag value in RawContacts table and observed that it has been set to 1 which means that contact has been deleted.
As official documentation describes:
Some sync adapters are read-only, meaning that they only sync
server-side changes to the phone, but not the reverse. If one of those
raw contacts is marked for deletion, it will remain on the phone.
However it will be effectively invisible, because it will not be part
of any aggregate contact.
So the problem is that I can't display this contact's data in my application any more because I don't know how to retrieve them from contacts database. If somebody knows how to handle this situation I would appreciate any advice. Thanks
So after more meticulous search I found the way to retrieve data for any RAW_CONTACT in my application independently of was it deleted from some other application or not.
Using of RawContacts.Entity API does this job.
Previously I tried to retrieve contact's data using such logic:
public Cursor getContactData(long rawContactId) {
return getContentResolver().query(ContactsContract.Data.CONTENT_URI, null,
ContactsContract.Data.RAW_CONTACT_ID + "=" + rawContactId, null);
}
And this method always returned null for deleted contact.
But using RawContacts.Entity such way:
public Cursor getContactData(long rawContactId) {
Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, id);
rawContactUri = Uri.withAppendedPath(rawContactUri, RawContacts.Entity.CONTENT_DIRECTORY);
return getResolver().query(rawContactUri, null, null, null);
}
allows to fetch contact's data inside application with appropriate authority regardless was it deleted by 3d-party application or not.
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.
My goal is to collect all unknown phone numbers from the Call Log.
This can be achieved by the following code:
private static final String[] CALLOG_PROJECTION = {CallLog.Calls._ID,
CallLog.Calls.CACHED_NAME, CallLog.Calls.NUMBER};
private static final String CALLOG_WHERE = CallLog.Calls.CACHED_NAME + " is null";
Cursor c = getContentResolver().query(CallLog.Calls.CONTENT_URI, CALLOG_PROJECTION,
CALLOG_WHERE, null, CallLog.Calls.DATE + " DESC");
This works well, but after i've created a contact from an unknown number, the result of the query still contains the unknown number.
If i open the Call Log activity window, i can see as the number changes to the name of the contact within a few seconds. So, the activity refreshes the CACHED_NAME column.
My question is, how can i programatically refresh (actualize) the Call Log ?
I landed on this question looking for ways to optimize what you're trying to do. Instead of using cached_name, I queried the Phone content provider for every number to get the actual name, but this led to performance issues.
I noticed that the stock application refreshes the list when a change occurred, as you did.
I'm going to implement a ContentObserver on the Phones table and update my name when that happens, I'll post if it works.
EDIT
I was checking the (Google's) Contacts app source code and they basically show the list with the CACHED_NAME first and, after the list is displayed, they check (on the background) if there were any changes to the contacts details. If there were any, they update the CallLog record and the list.
Basically, I've implemented something similar and it works. There were some performance issues when you scrolled the list while it was checking on the background for changes, because in a CallLog there are a lot of repeated numbers. So basically you just have to verify if those numbers are already being checked out.
Hope it helps!