16

I would like to clone Android Contacts Phone into my own SQLite db. In order to save time, the cloning should be triggered when the single contact is newly created or being updated in the Android system. Thus, I want to have "last modified time" of each contact.

For API level 18 or above, it seems that i would get the last modified time of a single person contact by using ContactsContract.Contacts.CONTACT_LAST_UPDATED_TIMESTAMP. However for API Level 17 or below, it seems that there are some discussions before which suggested the use of "ContactsContract.RawContacts.VERSION" or "CONTACT_STATUS_TIMESTAMP".

For "CONTACT_STATUS_TIMESTAMP", it always returns ZERO or null. For "ContactsContract.RawContacts.VERSION", the version remained the same when i updated the photo, phone number or email of a person's contact.

Glad if someone would point out the mistakes i have made...

Reference: How to get the last modification date for Contacts list (Add/Delete/Modify)

Community
  • 1
  • 1
Antoine Murion
  • 773
  • 1
  • 14
  • 26
  • 2
    Did you get a solution? – Sanyasirao Mopada May 23 '17 at 07:03
  • I have implemented same feature in my code, and there is a scenario where even version doesn't gets updated(check by updating number and name one at a time) so I am checking display-name and phone number everytime and if it's changed then I mark my database row as dirty. – Sarthak Mittal Mar 15 '18 at 07:04

1 Answers1

5

You can get notified when something changes using a ContentObserver. You will then need to retrieve the correct contact yourself. Of course that means having to have your app open when the contact changes or (more reasonably) running a background service.

In your service, create a content observer:

myObserver = new ContentObserver(new Handler()) {

    @Override
    public void onChange(boolean selfChange) {
        super.onChange(selfChange);
        if (!selfChange) {
            //Note: when this (older) callback is used you need to loop through 
            //and find the contact yourself (by using the dirty field)
        }
    }

    @Override
    public void onChange(boolean selfChange, Uri uri) {
        super.onChange(selfChange, uri);
        if (!selfChange) {
            //Note: if you receive a uri, it has contact id
            long rawContactId = ContentUris.parseId(uri);
            //Note: be careful which thread you are on here (dependent on handler)
        }
    }

};

//NOTE: Then you need to remember to register and unregister the observer.
//getContentResolver().registerContentObserver(ContactsContract.Contacts.CONTENT_URI, true, myObserver);
//getContentResolver().unregisterContentObserver(myObserver);

Your suggestion to use dirty is not a good solution alone, as this only temporarily indicates that the aggregate contact (owner) should be updated because something in the RawContact changed. This means if the contact gets synced before your app gets opened, dirty is already false (0) again.

Also note that the documentation for the column mentions that it was added in API 18, so as you are aware, it is only below 18 that you need a workaround. so the first step is to make sure you are using the column when you can

if (Build.VERSION.SDK_INT >=  Build.VERSION_CODES.JELLY_BEAN_MR2) {
    //continue with existing code
} else {
    //use workaround (start service, or at least register observer)
}
Nick Cardoso
  • 20,807
  • 14
  • 73
  • 124
  • Your answer looks right solution. But how can I register contentObserver for contacts? – Ranjithkumar Mar 21 '18 at 04:51
  • It's the code i commented out at the bottom. I suggest you create a service and inside the service when it binds, use the `getContentResolver().registerContentObserver(ContactsContract.Contacts.CONTENT_URI, true, myObserver);` line – Nick Cardoso Mar 21 '18 at 08:06