100bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshi/* 200bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshi * Copyright (C) 2010 The Android Open Source Project 315ef1a8091c1557f175575671a5af62420088944John Evans * 400bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshi * Licensed under the Apache License, Version 2.0 (the "License"); you may not 500bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshi * use this file except in compliance with the License. You may obtain a copy of 600bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshi * the License at 715ef1a8091c1557f175575671a5af62420088944John Evans * 800bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshi * http://www.apache.org/licenses/LICENSE-2.0 915ef1a8091c1557f175575671a5af62420088944John Evans * 1000bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshi * Unless required by applicable law or agreed to in writing, software 1100bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshi * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 1200bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshi * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 1300bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshi * License for the specific language governing permissions and limitations under 1400bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshi * the License. 1500bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshi */ 1600bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshipackage com.example.android.samplesync.syncadapter; 1700bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshi 181cf776aee1b2c0be0d9d7d2fdcb8696beefa5e1dDaniel Lehmannimport com.example.android.samplesync.Constants; 191cf776aee1b2c0be0d9d7d2fdcb8696beefa5e1dDaniel Lehmannimport com.example.android.samplesync.client.NetworkUtilities; 201cf776aee1b2c0be0d9d7d2fdcb8696beefa5e1dDaniel Lehmannimport com.example.android.samplesync.client.RawContact; 211cf776aee1b2c0be0d9d7d2fdcb8696beefa5e1dDaniel Lehmannimport com.example.android.samplesync.platform.ContactManager; 221cf776aee1b2c0be0d9d7d2fdcb8696beefa5e1dDaniel Lehmann 231cf776aee1b2c0be0d9d7d2fdcb8696beefa5e1dDaniel Lehmannimport org.apache.http.ParseException; 241cf776aee1b2c0be0d9d7d2fdcb8696beefa5e1dDaniel Lehmannimport org.apache.http.auth.AuthenticationException; 251cf776aee1b2c0be0d9d7d2fdcb8696beefa5e1dDaniel Lehmannimport org.json.JSONException; 261cf776aee1b2c0be0d9d7d2fdcb8696beefa5e1dDaniel Lehmann 2700bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshiimport android.accounts.Account; 2800bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshiimport android.accounts.AccountManager; 2900bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshiimport android.accounts.AuthenticatorException; 3000bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshiimport android.accounts.OperationCanceledException; 3100bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshiimport android.content.AbstractThreadedSyncAdapter; 3200bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshiimport android.content.ContentProviderClient; 3300bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshiimport android.content.Context; 3400bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshiimport android.content.SyncResult; 3574c1836184adccce4b876ddfb736567eb87c6e1dRobert Lyimport android.os.Build; 3600bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshiimport android.os.Bundle; 3715ef1a8091c1557f175575671a5af62420088944John Evansimport android.text.TextUtils; 3800bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshiimport android.util.Log; 3900bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshi 4000bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshiimport java.io.IOException; 4100bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshiimport java.util.List; 4200bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshi 4300bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshi/** 4400bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshi * SyncAdapter implementation for syncing sample SyncAdapter contacts to the 4515ef1a8091c1557f175575671a5af62420088944John Evans * platform ContactOperations provider. This sample shows a basic 2-way 4615ef1a8091c1557f175575671a5af62420088944John Evans * sync between the client and a sample server. It also contains an 4715ef1a8091c1557f175575671a5af62420088944John Evans * example of how to update the contacts' status messages, which 4815ef1a8091c1557f175575671a5af62420088944John Evans * would be useful for a messaging or social networking client. 4900bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshi */ 5000bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshipublic class SyncAdapter extends AbstractThreadedSyncAdapter { 51c51da235c20bab67ad1ef23884f36bb76740f3a0Megha Joshi 5200bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshi private static final String TAG = "SyncAdapter"; 5315ef1a8091c1557f175575671a5af62420088944John Evans private static final String SYNC_MARKER_KEY = "com.example.android.samplesync.marker"; 5415ef1a8091c1557f175575671a5af62420088944John Evans private static final boolean NOTIFY_AUTH_FAILURE = true; 5500bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshi 5600bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshi private final AccountManager mAccountManager; 57c51da235c20bab67ad1ef23884f36bb76740f3a0Megha Joshi 5800bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshi private final Context mContext; 5900bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshi 6000bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshi public SyncAdapter(Context context, boolean autoInitialize) { 6100bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshi super(context, autoInitialize); 6200bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshi mContext = context; 6300bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshi mAccountManager = AccountManager.get(context); 6400bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshi } 6500bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshi 6600bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshi @Override 6700bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshi public void onPerformSync(Account account, Bundle extras, String authority, 6800bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshi ContentProviderClient provider, SyncResult syncResult) { 69c51da235c20bab67ad1ef23884f36bb76740f3a0Megha Joshi 70c51da235c20bab67ad1ef23884f36bb76740f3a0Megha Joshi try { 7115ef1a8091c1557f175575671a5af62420088944John Evans // see if we already have a sync-state attached to this account. By handing 7215ef1a8091c1557f175575671a5af62420088944John Evans // This value to the server, we can just get the contacts that have 7315ef1a8091c1557f175575671a5af62420088944John Evans // been updated on the server-side since our last sync-up 7415ef1a8091c1557f175575671a5af62420088944John Evans long lastSyncMarker = getServerSyncMarker(account); 7515ef1a8091c1557f175575671a5af62420088944John Evans 7615ef1a8091c1557f175575671a5af62420088944John Evans // By default, contacts from a 3rd party provider are hidden in the contacts 7715ef1a8091c1557f175575671a5af62420088944John Evans // list. So let's set the flag that causes them to be visible, so that users 7815ef1a8091c1557f175575671a5af62420088944John Evans // can actually see these contacts. 7915ef1a8091c1557f175575671a5af62420088944John Evans if (lastSyncMarker == 0) { 8015ef1a8091c1557f175575671a5af62420088944John Evans ContactManager.setAccountContactsVisibility(getContext(), account, true); 8115ef1a8091c1557f175575671a5af62420088944John Evans } 8215ef1a8091c1557f175575671a5af62420088944John Evans 8315ef1a8091c1557f175575671a5af62420088944John Evans List<RawContact> dirtyContacts; 8415ef1a8091c1557f175575671a5af62420088944John Evans List<RawContact> updatedContacts; 8515ef1a8091c1557f175575671a5af62420088944John Evans 8615ef1a8091c1557f175575671a5af62420088944John Evans // Use the account manager to request the AuthToken we'll need 8715ef1a8091c1557f175575671a5af62420088944John Evans // to talk to our sample server. If we don't have an AuthToken 8815ef1a8091c1557f175575671a5af62420088944John Evans // yet, this could involve a round-trip to the server to request 8915ef1a8091c1557f175575671a5af62420088944John Evans // and AuthToken. 9015ef1a8091c1557f175575671a5af62420088944John Evans final String authtoken = mAccountManager.blockingGetAuthToken(account, 9115ef1a8091c1557f175575671a5af62420088944John Evans Constants.AUTHTOKEN_TYPE, NOTIFY_AUTH_FAILURE); 9215ef1a8091c1557f175575671a5af62420088944John Evans 931cf776aee1b2c0be0d9d7d2fdcb8696beefa5e1dDaniel Lehmann // Make sure that the sample group exists 941cf776aee1b2c0be0d9d7d2fdcb8696beefa5e1dDaniel Lehmann final long groupId = ContactManager.ensureSampleGroupExists(mContext, account); 951cf776aee1b2c0be0d9d7d2fdcb8696beefa5e1dDaniel Lehmann 9615ef1a8091c1557f175575671a5af62420088944John Evans // Find the local 'dirty' contacts that we need to tell the server about... 9715ef1a8091c1557f175575671a5af62420088944John Evans // Find the local users that need to be sync'd to the server... 9815ef1a8091c1557f175575671a5af62420088944John Evans dirtyContacts = ContactManager.getDirtyContacts(mContext, account); 9915ef1a8091c1557f175575671a5af62420088944John Evans 10015ef1a8091c1557f175575671a5af62420088944John Evans // Send the dirty contacts to the server, and retrieve the server-side changes 10115ef1a8091c1557f175575671a5af62420088944John Evans updatedContacts = NetworkUtilities.syncContacts(account, authtoken, 10215ef1a8091c1557f175575671a5af62420088944John Evans lastSyncMarker, dirtyContacts); 10315ef1a8091c1557f175575671a5af62420088944John Evans 10415ef1a8091c1557f175575671a5af62420088944John Evans // Update the local contacts database with the changes. updateContacts() 10515ef1a8091c1557f175575671a5af62420088944John Evans // returns a syncState value that indicates the high-water-mark for 10615ef1a8091c1557f175575671a5af62420088944John Evans // the changes we received. 10700bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshi Log.d(TAG, "Calling contactManager's sync contacts"); 10815ef1a8091c1557f175575671a5af62420088944John Evans long newSyncState = ContactManager.updateContacts(mContext, 10915ef1a8091c1557f175575671a5af62420088944John Evans account.name, 11015ef1a8091c1557f175575671a5af62420088944John Evans updatedContacts, 1111cf776aee1b2c0be0d9d7d2fdcb8696beefa5e1dDaniel Lehmann groupId, 11215ef1a8091c1557f175575671a5af62420088944John Evans lastSyncMarker); 11315ef1a8091c1557f175575671a5af62420088944John Evans 11415ef1a8091c1557f175575671a5af62420088944John Evans // This is a demo of how you can update IM-style status messages 11515ef1a8091c1557f175575671a5af62420088944John Evans // for contacts on the client. This probably won't apply to 11615ef1a8091c1557f175575671a5af62420088944John Evans // 2-way contact sync providers - it's more likely that one-way 11715ef1a8091c1557f175575671a5af62420088944John Evans // sync providers (IM clients, social networking apps, etc) would 11815ef1a8091c1557f175575671a5af62420088944John Evans // use this feature. 11974c1836184adccce4b876ddfb736567eb87c6e1dRobert Ly 12015ef1a8091c1557f175575671a5af62420088944John Evans ContactManager.updateStatusMessages(mContext, updatedContacts); 12115ef1a8091c1557f175575671a5af62420088944John Evans 12274c1836184adccce4b876ddfb736567eb87c6e1dRobert Ly // This is a demo of how you can add stream items for contacts on 12374c1836184adccce4b876ddfb736567eb87c6e1dRobert Ly // the client. This probably won't apply to 12474c1836184adccce4b876ddfb736567eb87c6e1dRobert Ly // 2-way contact sync providers - it's more likely that one-way 12574c1836184adccce4b876ddfb736567eb87c6e1dRobert Ly // sync providers (IM clients, social networking apps, etc) would 12674c1836184adccce4b876ddfb736567eb87c6e1dRobert Ly // use this feature. This is only supported in ICS MR1 or above. 12774c1836184adccce4b876ddfb736567eb87c6e1dRobert Ly 12874c1836184adccce4b876ddfb736567eb87c6e1dRobert Ly if (Build.VERSION.SDK_INT >= 12974c1836184adccce4b876ddfb736567eb87c6e1dRobert Ly Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) { 13074c1836184adccce4b876ddfb736567eb87c6e1dRobert Ly ContactManager.addStreamItems(mContext, updatedContacts, 13174c1836184adccce4b876ddfb736567eb87c6e1dRobert Ly account.name, account.type); 13274c1836184adccce4b876ddfb736567eb87c6e1dRobert Ly } 13374c1836184adccce4b876ddfb736567eb87c6e1dRobert Ly 13415ef1a8091c1557f175575671a5af62420088944John Evans // Save off the new sync marker. On our next sync, we only want to receive 13515ef1a8091c1557f175575671a5af62420088944John Evans // contacts that have changed since this sync... 13615ef1a8091c1557f175575671a5af62420088944John Evans setServerSyncMarker(account, newSyncState); 13715ef1a8091c1557f175575671a5af62420088944John Evans 13815ef1a8091c1557f175575671a5af62420088944John Evans if (dirtyContacts.size() > 0) { 13915ef1a8091c1557f175575671a5af62420088944John Evans ContactManager.clearSyncFlags(mContext, dirtyContacts); 14015ef1a8091c1557f175575671a5af62420088944John Evans } 14115ef1a8091c1557f175575671a5af62420088944John Evans 14200bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshi } catch (final AuthenticatorException e) { 14300bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshi Log.e(TAG, "AuthenticatorException", e); 14415ef1a8091c1557f175575671a5af62420088944John Evans syncResult.stats.numParseExceptions++; 14500bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshi } catch (final OperationCanceledException e) { 14600bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshi Log.e(TAG, "OperationCanceledExcetpion", e); 14700bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshi } catch (final IOException e) { 14800bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshi Log.e(TAG, "IOException", e); 14900bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshi syncResult.stats.numIoExceptions++; 15000bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshi } catch (final AuthenticationException e) { 15100bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshi Log.e(TAG, "AuthenticationException", e); 15215ef1a8091c1557f175575671a5af62420088944John Evans syncResult.stats.numAuthExceptions++; 15300bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshi } catch (final ParseException e) { 15400bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshi Log.e(TAG, "ParseException", e); 15500bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshi syncResult.stats.numParseExceptions++; 15615ef1a8091c1557f175575671a5af62420088944John Evans } catch (final JSONException e) { 15700bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshi Log.e(TAG, "JSONException", e); 15815ef1a8091c1557f175575671a5af62420088944John Evans syncResult.stats.numParseExceptions++; 15915ef1a8091c1557f175575671a5af62420088944John Evans } 16015ef1a8091c1557f175575671a5af62420088944John Evans } 16115ef1a8091c1557f175575671a5af62420088944John Evans 16215ef1a8091c1557f175575671a5af62420088944John Evans /** 16315ef1a8091c1557f175575671a5af62420088944John Evans * This helper function fetches the last known high-water-mark 16415ef1a8091c1557f175575671a5af62420088944John Evans * we received from the server - or 0 if we've never synced. 16515ef1a8091c1557f175575671a5af62420088944John Evans * @param account the account we're syncing 16615ef1a8091c1557f175575671a5af62420088944John Evans * @return the change high-water-mark 16715ef1a8091c1557f175575671a5af62420088944John Evans */ 16815ef1a8091c1557f175575671a5af62420088944John Evans private long getServerSyncMarker(Account account) { 16915ef1a8091c1557f175575671a5af62420088944John Evans String markerString = mAccountManager.getUserData(account, SYNC_MARKER_KEY); 17015ef1a8091c1557f175575671a5af62420088944John Evans if (!TextUtils.isEmpty(markerString)) { 17115ef1a8091c1557f175575671a5af62420088944John Evans return Long.parseLong(markerString); 17200bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshi } 17315ef1a8091c1557f175575671a5af62420088944John Evans return 0; 17415ef1a8091c1557f175575671a5af62420088944John Evans } 17515ef1a8091c1557f175575671a5af62420088944John Evans 17615ef1a8091c1557f175575671a5af62420088944John Evans /** 17715ef1a8091c1557f175575671a5af62420088944John Evans * Save off the high-water-mark we receive back from the server. 17815ef1a8091c1557f175575671a5af62420088944John Evans * @param account The account we're syncing 17915ef1a8091c1557f175575671a5af62420088944John Evans * @param marker The high-water-mark we want to save. 18015ef1a8091c1557f175575671a5af62420088944John Evans */ 18115ef1a8091c1557f175575671a5af62420088944John Evans private void setServerSyncMarker(Account account, long marker) { 18215ef1a8091c1557f175575671a5af62420088944John Evans mAccountManager.setUserData(account, SYNC_MARKER_KEY, Long.toString(marker)); 18300bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshi } 18400bf0f0296b6691a9ed93fa46eccf316f7b0222eMegha Joshi} 18574c1836184adccce4b876ddfb736567eb87c6e1dRobert Ly 186