New version of SampleSyncAdapter sample code that allows local editing
The changes made were pretty sweeping. The biggest addition was to allow on-device contact creation/editing, and supporting 2-way sync to the sample server that runs in Google App Engine. The client-side sample code also includes examples of how to support the user of AuthTokens (instead of always sending username/password to the server), how to change a contact's picture, and how to set IM-style status messages for each contact. I also greatly simplified the server code so that instead of mimicking both an addressbook and an IM-style status update system for multiple users, it really just simulates an addressbook for a single user. The server code also includes a cron job that (once a week) blows away the contact database, so that it's relatively self-cleaning. Change-Id: I017f1d3f9320a02fe05a20f1613846963107145e
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
@@ -24,12 +24,12 @@ import android.content.ContentProviderClient;
|
||||
import android.content.Context;
|
||||
import android.content.SyncResult;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import com.example.android.samplesync.Constants;
|
||||
import com.example.android.samplesync.client.NetworkUtilities;
|
||||
import com.example.android.samplesync.client.User;
|
||||
import com.example.android.samplesync.client.User.Status;
|
||||
import com.example.android.samplesync.client.RawContact;
|
||||
import com.example.android.samplesync.platform.ContactManager;
|
||||
|
||||
import org.apache.http.ParseException;
|
||||
@@ -37,23 +37,27 @@ import org.apache.http.auth.AuthenticationException;
|
||||
import org.json.JSONException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* SyncAdapter implementation for syncing sample SyncAdapter contacts to the
|
||||
* platform ContactOperations provider.
|
||||
* platform ContactOperations provider. This sample shows a basic 2-way
|
||||
* sync between the client and a sample server. It also contains an
|
||||
* example of how to update the contacts' status messages, which
|
||||
* would be useful for a messaging or social networking client.
|
||||
*/
|
||||
public class SyncAdapter extends AbstractThreadedSyncAdapter {
|
||||
|
||||
private static final String TAG = "SyncAdapter";
|
||||
private static final String SYNC_MARKER_KEY = "com.example.android.samplesync.marker";
|
||||
private static final boolean NOTIFY_AUTH_FAILURE = true;
|
||||
|
||||
private final AccountManager mAccountManager;
|
||||
|
||||
private final Context mContext;
|
||||
|
||||
private Date mLastUpdated;
|
||||
|
||||
public SyncAdapter(Context context, boolean autoInitialize) {
|
||||
super(context, autoInitialize);
|
||||
mContext = context;
|
||||
@@ -64,42 +68,101 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter {
|
||||
public void onPerformSync(Account account, Bundle extras, String authority,
|
||||
ContentProviderClient provider, SyncResult syncResult) {
|
||||
|
||||
List<User> users;
|
||||
List<Status> statuses;
|
||||
String authtoken = null;
|
||||
try {
|
||||
// use the account manager to request the credentials
|
||||
authtoken =
|
||||
mAccountManager
|
||||
.blockingGetAuthToken(account, Constants.AUTHTOKEN_TYPE, true /* notifyAuthFailure */);
|
||||
// fetch updates from the sample service over the cloud
|
||||
users = NetworkUtilities.fetchFriendUpdates(account, authtoken, mLastUpdated);
|
||||
// update the last synced date.
|
||||
mLastUpdated = new Date();
|
||||
// update platform contacts.
|
||||
// see if we already have a sync-state attached to this account. By handing
|
||||
// This value to the server, we can just get the contacts that have
|
||||
// been updated on the server-side since our last sync-up
|
||||
long lastSyncMarker = getServerSyncMarker(account);
|
||||
|
||||
// By default, contacts from a 3rd party provider are hidden in the contacts
|
||||
// list. So let's set the flag that causes them to be visible, so that users
|
||||
// can actually see these contacts.
|
||||
if (lastSyncMarker == 0) {
|
||||
ContactManager.setAccountContactsVisibility(getContext(), account, true);
|
||||
}
|
||||
|
||||
List<RawContact> dirtyContacts;
|
||||
List<RawContact> updatedContacts;
|
||||
|
||||
// Use the account manager to request the AuthToken we'll need
|
||||
// to talk to our sample server. If we don't have an AuthToken
|
||||
// yet, this could involve a round-trip to the server to request
|
||||
// and AuthToken.
|
||||
final String authtoken = mAccountManager.blockingGetAuthToken(account,
|
||||
Constants.AUTHTOKEN_TYPE, NOTIFY_AUTH_FAILURE);
|
||||
|
||||
// Find the local 'dirty' contacts that we need to tell the server about...
|
||||
// Find the local users that need to be sync'd to the server...
|
||||
dirtyContacts = ContactManager.getDirtyContacts(mContext, account);
|
||||
|
||||
// Send the dirty contacts to the server, and retrieve the server-side changes
|
||||
updatedContacts = NetworkUtilities.syncContacts(account, authtoken,
|
||||
lastSyncMarker, dirtyContacts);
|
||||
|
||||
// Update the local contacts database with the changes. updateContacts()
|
||||
// returns a syncState value that indicates the high-water-mark for
|
||||
// the changes we received.
|
||||
Log.d(TAG, "Calling contactManager's sync contacts");
|
||||
ContactManager.syncContacts(mContext, account.name, users);
|
||||
// fetch and update status messages for all the synced users.
|
||||
statuses = NetworkUtilities.fetchFriendStatuses(account, authtoken);
|
||||
ContactManager.insertStatuses(mContext, account.name, statuses);
|
||||
long newSyncState = ContactManager.updateContacts(mContext,
|
||||
account.name,
|
||||
updatedContacts,
|
||||
lastSyncMarker);
|
||||
|
||||
// This is a demo of how you can update IM-style status messages
|
||||
// for contacts on the client. This probably won't apply to
|
||||
// 2-way contact sync providers - it's more likely that one-way
|
||||
// sync providers (IM clients, social networking apps, etc) would
|
||||
// use this feature.
|
||||
ContactManager.updateStatusMessages(mContext, updatedContacts);
|
||||
|
||||
// Save off the new sync marker. On our next sync, we only want to receive
|
||||
// contacts that have changed since this sync...
|
||||
setServerSyncMarker(account, newSyncState);
|
||||
|
||||
if (dirtyContacts.size() > 0) {
|
||||
ContactManager.clearSyncFlags(mContext, dirtyContacts);
|
||||
}
|
||||
|
||||
} catch (final AuthenticatorException e) {
|
||||
syncResult.stats.numParseExceptions++;
|
||||
Log.e(TAG, "AuthenticatorException", e);
|
||||
syncResult.stats.numParseExceptions++;
|
||||
} catch (final OperationCanceledException e) {
|
||||
Log.e(TAG, "OperationCanceledExcetpion", e);
|
||||
} catch (final IOException e) {
|
||||
Log.e(TAG, "IOException", e);
|
||||
syncResult.stats.numIoExceptions++;
|
||||
} catch (final AuthenticationException e) {
|
||||
mAccountManager.invalidateAuthToken(Constants.ACCOUNT_TYPE, authtoken);
|
||||
syncResult.stats.numAuthExceptions++;
|
||||
Log.e(TAG, "AuthenticationException", e);
|
||||
syncResult.stats.numAuthExceptions++;
|
||||
} catch (final ParseException e) {
|
||||
syncResult.stats.numParseExceptions++;
|
||||
Log.e(TAG, "ParseException", e);
|
||||
} catch (final JSONException e) {
|
||||
syncResult.stats.numParseExceptions++;
|
||||
} catch (final JSONException e) {
|
||||
Log.e(TAG, "JSONException", e);
|
||||
syncResult.stats.numParseExceptions++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This helper function fetches the last known high-water-mark
|
||||
* we received from the server - or 0 if we've never synced.
|
||||
* @param account the account we're syncing
|
||||
* @return the change high-water-mark
|
||||
*/
|
||||
private long getServerSyncMarker(Account account) {
|
||||
String markerString = mAccountManager.getUserData(account, SYNC_MARKER_KEY);
|
||||
if (!TextUtils.isEmpty(markerString)) {
|
||||
return Long.parseLong(markerString);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save off the high-water-mark we receive back from the server.
|
||||
* @param account The account we're syncing
|
||||
* @param marker The high-water-mark we want to save.
|
||||
*/
|
||||
private void setServerSyncMarker(Account account, long marker) {
|
||||
mAccountManager.setUserData(account, SYNC_MARKER_KEY, Long.toString(marker));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user