title: Syncing android contacts with server
body: <p>I am creating an android app that keeps track of all contacts saved on the device (the ones that appear in the default android contacts app) and saves them in a MySQL table on the server.</p>
  
  <p>I managed to read all contact data using <code>ContectResolver</code>:</p>
  
  <pre><code>    //to read only android address book
      String where = ContactsContract.Contacts.IN_VISIBLE_GROUP + " = '1'";
  
      String[] projection = new String[] { ContactsContract.Contacts._ID, ContactsContract.Contacts.LOOKUP_KEY, ContactsContract.Contacts.HAS_PHONE_NUMBER};
  
      ContentResolver cr = context.getContentResolver();
      Cursor cursor = cr.query(ContactsContract.Contacts.CONTENT_URI, projection, where, null, null);
  </code></pre>
  
  <p>Then, using the <code>ContactsContract.Contacts._ID</code> I query additional contact data:</p>
  
  <pre><code>// Perform a query to retrieve the contact's name parts
          String[] nameProjection = new String[] {
                  ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME,
                  ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME,
                  ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME,
                  ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME
          };
  
          Cursor nameCursor = cr.query(
                  ContactsContract.Data.CONTENT_URI,
                  nameProjection,
                  ContactsContract.Data.MIMETYPE + " = '" +
                          ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE + "' AND " +
                          ContactsContract.CommonDataKinds.StructuredName.CONTACT_ID
                          + " = ?", new String[] { id }, null);
  
          // Retrieve the name parts
          String firstName = "", middleName = "", lastName = "", displayName = "";
          if(nameCursor.moveToNext()) {
              firstName = nameCursor.getString(nameCursor.getColumnIndex(
                      ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME));
              middleName = nameCursor.getString(nameCursor.getColumnIndex(
                      ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME));
              lastName = nameCursor.getString(nameCursor.getColumnIndex(
                      ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME));
              displayName = nameCursor.getString(nameCursor.getColumnIndex(
                      ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME));
          }
  </code></pre>
  
  <p>I then send all this data (including the contact's email addresses and phone numbers) to the server. </p>
  
  <p>I want to be able to periodically backup the device's contacts like this, but to  be able to detect changes in contact data, by comparing the old data to the new data. But to do this, i need to have some sort of link between the contacts in the android device, and my DB in which i save the contact data.</p>
  
  <p>After some searching i found 3 options:</p>
  
  <pre><code>ContactsContract.Contacts._ID
  ContactsContract.Contacts.LOOKUP_KEY
  ContactsContract.RawContacts._ID
  </code></pre>
  
  <p>But from what I found, all of them are not reliable, and can change in certain situations - invalidating the link between my DB to the device's contact list.</p>
  
  <p>I have considered the option to use <code>ContentObserver</code> to detect contact changes. But, I want to be able to detect contact changes even if my app has been uninstalled, then some contacts have changed and then my app has been reinstalled.</p>
  
  <p><strong>Is there a reliable identification key per contact that I can use, to know when a certain contact has been changed or deleted?</strong></p>
  
  <p>EDIT:</p>
  
  <p>I now found this variable exists: <code>ContactsContract.ContactsColumns.CONTACT_LAST_UPDATED_TIMESTAMP</code></p>
  
  <p>The problem is that it was introduces only in API level 18. I am working on min API 15. Is there something that could replace it for the missing API levels?</p>