summaryrefslogtreecommitdiff
path: root/src/com/android/providers/contacts/ContactsSyncAdapter.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/providers/contacts/ContactsSyncAdapter.java')
-rw-r--r--src/com/android/providers/contacts/ContactsSyncAdapter.java1295
1 files changed, 0 insertions, 1295 deletions
diff --git a/src/com/android/providers/contacts/ContactsSyncAdapter.java b/src/com/android/providers/contacts/ContactsSyncAdapter.java
deleted file mode 100644
index c15f2da..0000000
--- a/src/com/android/providers/contacts/ContactsSyncAdapter.java
+++ /dev/null
@@ -1,1295 +0,0 @@
-/*
-** Copyright 2007, 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,
-** See the License for the specific language governing permissions and
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** limitations under the License.
-*/
-
-package com.android.providers.contacts;
-
-import com.google.android.collect.Sets;
-import com.google.android.gdata.client.AndroidGDataClient;
-import com.google.android.gdata.client.AndroidXmlParserFactory;
-import com.google.android.providers.AbstractGDataSyncAdapter;
-import com.google.wireless.gdata.client.GDataServiceClient;
-import com.google.wireless.gdata.client.QueryParams;
-import com.google.wireless.gdata.client.HttpException;
-import com.google.wireless.gdata.contacts.client.ContactsClient;
-import com.google.wireless.gdata.contacts.data.ContactEntry;
-import com.google.wireless.gdata.contacts.data.ContactsElement;
-import com.google.wireless.gdata.contacts.data.EmailAddress;
-import com.google.wireless.gdata.contacts.data.GroupEntry;
-import com.google.wireless.gdata.contacts.data.GroupMembershipInfo;
-import com.google.wireless.gdata.contacts.data.ImAddress;
-import com.google.wireless.gdata.contacts.data.Organization;
-import com.google.wireless.gdata.contacts.data.PhoneNumber;
-import com.google.wireless.gdata.contacts.data.PostalAddress;
-import com.google.wireless.gdata.contacts.parser.xml.XmlContactsGDataParserFactory;
-import com.google.wireless.gdata.data.Entry;
-import com.google.wireless.gdata.data.ExtendedProperty;
-import com.google.wireless.gdata.data.Feed;
-import com.google.wireless.gdata.data.MediaEntry;
-import com.google.wireless.gdata.parser.ParseException;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import android.content.ContentProvider;
-import android.content.ContentResolver;
-import android.content.ContentUris;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.SyncContext;
-import android.content.SyncResult;
-import android.content.SyncableContentProvider;
-import android.database.Cursor;
-import android.database.DatabaseUtils;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.SystemProperties;
-import android.provider.Contacts;
-import android.provider.Contacts.ContactMethods;
-import android.provider.Contacts.Extensions;
-import android.provider.Contacts.GroupMembership;
-import android.provider.Contacts.Groups;
-import android.provider.Contacts.Organizations;
-import android.provider.Contacts.People;
-import android.provider.Contacts.Phones;
-import android.provider.Contacts.Photos;
-import android.provider.SubscribedFeeds;
-import android.provider.SyncConstValue;
-import android.text.TextUtils;
-import android.util.Config;
-import android.util.Log;
-import android.accounts.AccountManager;
-import android.accounts.Account;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.UnsupportedEncodingException;
-import java.net.URLEncoder;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Implements a SyncAdapter for Contacts
- */
-public class ContactsSyncAdapter extends AbstractGDataSyncAdapter {
-
- private static final String USER_AGENT_APP_VERSION = "Android-GData-Contacts/1.1";
-
- private static final String CONTACTS_FEED_URL = "http://www.google.com/m8/feeds/contacts/";
- private static final String GROUPS_FEED_URL = "http://www.google.com/m8/feeds/groups/";
- private static final String PHOTO_FEED_URL = "http://www.google.com/m8/feeds/photos/media/";
-
- private final ContactsClient mContactsClient;
-
- private static final String[] sSubscriptionProjection =
- new String[] {
- SubscribedFeeds.Feeds._SYNC_ACCOUNT,
- SubscribedFeeds.Feeds.FEED,
- SubscribedFeeds.Feeds._ID};
-
- private static final HashMap<Byte, Integer> ENTRY_TYPE_TO_PROVIDER_PHONE;
- private static final HashMap<Byte, Integer> ENTRY_TYPE_TO_PROVIDER_EMAIL;
- private static final HashMap<Byte, Integer> ENTRY_TYPE_TO_PROVIDER_IM;
- private static final HashMap<Byte, Integer> ENTRY_TYPE_TO_PROVIDER_POSTAL;
- private static final HashMap<Byte, Integer> ENTRY_TYPE_TO_PROVIDER_ORGANIZATION;
- private static final HashMap<Integer, Byte> PROVIDER_TYPE_TO_ENTRY_PHONE;
- private static final HashMap<Integer, Byte> PROVIDER_TYPE_TO_ENTRY_EMAIL;
- private static final HashMap<Integer, Byte> PROVIDER_TYPE_TO_ENTRY_IM;
- private static final HashMap<Integer, Byte> PROVIDER_TYPE_TO_ENTRY_POSTAL;
- private static final HashMap<Integer, Byte> PROVIDER_TYPE_TO_ENTRY_ORGANIZATION;
-
- private static final HashMap<Byte, Integer> ENTRY_IM_PROTOCOL_TO_PROVIDER_PROTOCOL;
- private static final HashMap<Integer, Byte> PROVIDER_IM_PROTOCOL_TO_ENTRY_PROTOCOL;
-
- private static final int MAX_MEDIA_ENTRIES_PER_SYNC = 10;
-
- // Only valid during a sync operation.
- // If set then a getServerDiffs() was performed during this sync.
- private boolean mPerformedGetServerDiffs;
-
- // Only valid during a sync. If set then this sync was a forced sync request
- private boolean mIsManualSync;
-
- private int mPhotoDownloads;
- private int mPhotoUploads;
-
- private static final String IMAGE_MIME_TYPE = "image/*";
-
- static {
- HashMap<Byte, Integer> map;
-
- map = new HashMap<Byte, Integer>();
- map.put(ImAddress.PROTOCOL_AIM, ContactMethods.PROTOCOL_AIM);
- map.put(ImAddress.PROTOCOL_GOOGLE_TALK, ContactMethods.PROTOCOL_GOOGLE_TALK);
- map.put(ImAddress.PROTOCOL_ICQ, ContactMethods.PROTOCOL_ICQ);
- map.put(ImAddress.PROTOCOL_JABBER, ContactMethods.PROTOCOL_JABBER);
- map.put(ImAddress.PROTOCOL_MSN, ContactMethods.PROTOCOL_MSN);
- map.put(ImAddress.PROTOCOL_QQ, ContactMethods.PROTOCOL_QQ);
- map.put(ImAddress.PROTOCOL_SKYPE, ContactMethods.PROTOCOL_SKYPE);
- map.put(ImAddress.PROTOCOL_YAHOO, ContactMethods.PROTOCOL_YAHOO);
- ENTRY_IM_PROTOCOL_TO_PROVIDER_PROTOCOL = map;
- PROVIDER_IM_PROTOCOL_TO_ENTRY_PROTOCOL = swapMap(map);
-
- map = new HashMap<Byte, Integer>();
- map.put(EmailAddress.TYPE_HOME, ContactMethods.TYPE_HOME);
- map.put(EmailAddress.TYPE_WORK, ContactMethods.TYPE_WORK);
- map.put(EmailAddress.TYPE_OTHER, ContactMethods.TYPE_OTHER);
- map.put(EmailAddress.TYPE_NONE, ContactMethods.TYPE_CUSTOM);
- ENTRY_TYPE_TO_PROVIDER_EMAIL = map;
- PROVIDER_TYPE_TO_ENTRY_EMAIL = swapMap(map);
-
- map = new HashMap<Byte, Integer>();
- map.put(PhoneNumber.TYPE_HOME, Phones.TYPE_HOME);
- map.put(PhoneNumber.TYPE_MOBILE, Phones.TYPE_MOBILE);
- map.put(PhoneNumber.TYPE_PAGER, Phones.TYPE_PAGER);
- map.put(PhoneNumber.TYPE_WORK, Phones.TYPE_WORK);
- map.put(PhoneNumber.TYPE_HOME_FAX, Phones.TYPE_FAX_HOME);
- map.put(PhoneNumber.TYPE_WORK_FAX, Phones.TYPE_FAX_WORK);
- map.put(PhoneNumber.TYPE_OTHER, Phones.TYPE_OTHER);
- map.put(PhoneNumber.TYPE_NONE, Phones.TYPE_CUSTOM);
- ENTRY_TYPE_TO_PROVIDER_PHONE = map;
- PROVIDER_TYPE_TO_ENTRY_PHONE = swapMap(map);
-
- map = new HashMap<Byte, Integer>();
- map.put(PostalAddress.TYPE_HOME, ContactMethods.TYPE_HOME);
- map.put(PostalAddress.TYPE_WORK, ContactMethods.TYPE_WORK);
- map.put(PostalAddress.TYPE_OTHER, ContactMethods.TYPE_OTHER);
- map.put(PostalAddress.TYPE_NONE, ContactMethods.TYPE_CUSTOM);
- ENTRY_TYPE_TO_PROVIDER_POSTAL = map;
- PROVIDER_TYPE_TO_ENTRY_POSTAL = swapMap(map);
-
- map = new HashMap<Byte, Integer>();
- map.put(ImAddress.TYPE_HOME, ContactMethods.TYPE_HOME);
- map.put(ImAddress.TYPE_WORK, ContactMethods.TYPE_WORK);
- map.put(ImAddress.TYPE_OTHER, ContactMethods.TYPE_OTHER);
- map.put(ImAddress.TYPE_NONE, ContactMethods.TYPE_CUSTOM);
- ENTRY_TYPE_TO_PROVIDER_IM = map;
- PROVIDER_TYPE_TO_ENTRY_IM = swapMap(map);
-
- map = new HashMap<Byte, Integer>();
- map.put(Organization.TYPE_WORK, Organizations.TYPE_WORK);
- map.put(Organization.TYPE_OTHER, Organizations.TYPE_OTHER);
- map.put(Organization.TYPE_NONE, Organizations.TYPE_CUSTOM);
- ENTRY_TYPE_TO_PROVIDER_ORGANIZATION = map;
- PROVIDER_TYPE_TO_ENTRY_ORGANIZATION = swapMap(map);
- }
-
- private static <A, B> HashMap<B, A> swapMap(HashMap<A, B> originalMap) {
- HashMap<B, A> newMap = new HashMap<B,A>();
- for (Map.Entry<A, B> entry : originalMap.entrySet()) {
- final B originalValue = entry.getValue();
- if (newMap.containsKey(originalValue)) {
- throw new IllegalArgumentException("value " + originalValue
- + " was already encountered");
- }
- newMap.put(originalValue, entry.getKey());
- }
- return newMap;
- }
-
- protected ContactsSyncAdapter(Context context, SyncableContentProvider provider) {
- super(context, provider);
- mContactsClient = new ContactsClient(
- new AndroidGDataClient(context, USER_AGENT_APP_VERSION),
- new XmlContactsGDataParserFactory(new AndroidXmlParserFactory()));
- }
-
- protected GDataServiceClient getGDataServiceClient() {
- return mContactsClient;
- }
-
- @Override
- protected Entry newEntry() {
- throw new UnsupportedOperationException("this should never be used");
- }
-
- protected String getFeedUrl(Account account) {
- throw new UnsupportedOperationException("this should never be used");
- }
-
- protected Class getFeedEntryClass() {
- throw new UnsupportedOperationException("this should never be used");
- }
-
- protected Class getFeedEntryClass(String feed) {
- if (feed.startsWith(rewriteUrlforAccount(getAccount(), GROUPS_FEED_URL))) {
- return GroupEntry.class;
- }
- if (feed.startsWith(rewriteUrlforAccount(getAccount(), CONTACTS_FEED_URL))) {
- return ContactEntry.class;
- }
- return null;
- }
-
- @Override
- public void getServerDiffs(SyncContext context, SyncData baseSyncData,
- SyncableContentProvider tempProvider,
- Bundle extras, Object syncInfo, SyncResult syncResult) {
- mPerformedGetServerDiffs = true;
- GDataSyncData syncData = (GDataSyncData)baseSyncData;
-
- ArrayList<String> feedsToSync = new ArrayList<String>();
-
- if (extras != null && extras.containsKey("feed")) {
- feedsToSync.add((String) extras.get("feed"));
- } else {
- feedsToSync.add(getGroupsFeedForAccount(getAccount()));
- addContactsFeedsToSync(getContext().getContentResolver(), getAccount(), feedsToSync);
- feedsToSync.add(getPhotosFeedForAccount(getAccount()));
- }
-
- for (String feed : feedsToSync) {
- context.setStatusText("Downloading\u2026");
- if (getPhotosFeedForAccount(getAccount()).equals(feed)) {
- getServerPhotos(context, feed, MAX_MEDIA_ENTRIES_PER_SYNC, syncData, syncResult);
- } else {
- final Class feedEntryClass = getFeedEntryClass(feed);
- if (feedEntryClass != null) {
- getServerDiffsImpl(context, tempProvider, feedEntryClass,
- feed, null, getMaxEntriesPerSync(), syncData, syncResult);
- } else {
- if (Config.LOGD) {
- Log.d(TAG, "ignoring sync request for unknown feed " + feed);
- }
- }
- }
- if (syncResult.hasError()) {
- break;
- }
- }
- }
-
- /**
- * Look at the groups sync settings and the overall sync preference to determine which
- * feeds to sync and add them to the feedsToSync list.
- */
- public static void addContactsFeedsToSync(ContentResolver cr, Account account,
- Collection<String> feedsToSync) {
- boolean shouldSyncEverything = getShouldSyncEverything(cr, account);
- if (shouldSyncEverything) {
- feedsToSync.add(getContactsFeedForAccount(account));
- return;
- }
-
- Cursor cursor = cr.query(Contacts.Groups.CONTENT_URI, new String[]{Groups._SYNC_ID},
- "_sync_account=? AND _sync_account_type=? AND should_sync>0",
- new String[]{account.name, account.type}, null);
- try {
- while (cursor.moveToNext()) {
- feedsToSync.add(getContactsFeedForGroup(account, cursor.getString(0)));
- }
- } finally {
- cursor.close();
- }
- }
-
- private static boolean getShouldSyncEverything(ContentResolver cr, Account account) {
- // TODO(fredq) should be using account instead of null
- String value = Contacts.Settings.getSetting(cr, null, Contacts.Settings.SYNC_EVERYTHING);
- return !TextUtils.isEmpty(value) && !"0".equals(value);
- }
-
- private void getServerPhotos(SyncContext context, String feedUrl, int maxDownloads,
- GDataSyncData syncData, SyncResult syncResult) {
- final ContentResolver cr = getContext().getContentResolver();
- final Account account = getAccount();
- Cursor cursor = cr.query(
- Photos.CONTENT_URI,
- new String[]{Photos._SYNC_ID, Photos._SYNC_VERSION, Photos.PERSON_ID,
- Photos.DOWNLOAD_REQUIRED, Photos._ID}, ""
- + "_sync_account=? AND _sync_account_type=? AND download_required != 0",
- new String[]{account.name, account.type}, null);
- try {
- int numFetched = 0;
- while (cursor.moveToNext()) {
- if (numFetched >= maxDownloads) {
- break;
- }
- String photoSyncId = cursor.getString(0);
- String photoVersion = cursor.getString(1);
- long person = cursor.getLong(2);
- String photoUrl = feedUrl + "/" + photoSyncId;
- long photoId = cursor.getLong(4);
-
- try {
- context.setStatusText("Downloading photo " + photoSyncId);
- ++numFetched;
- ++mPhotoDownloads;
- InputStream inputStream = mContactsClient.getMediaEntryAsStream(
- photoUrl, getAuthToken());
- savePhoto(person, inputStream, photoVersion);
- syncResult.stats.numUpdates++;
- } catch (IOException e) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.d(TAG, "error downloading " + photoUrl, e);
- }
- syncResult.stats.numIoExceptions++;
- return;
- } catch (HttpException e) {
- switch (e.getStatusCode()) {
- case HttpException.SC_UNAUTHORIZED:
- if (Config.LOGD) {
- Log.d(TAG, "not authorized to download " + photoUrl, e);
- }
- syncResult.stats.numAuthExceptions++;
- return;
- case HttpException.SC_FORBIDDEN:
- case HttpException.SC_NOT_FOUND:
- final String exceptionMessage = e.getMessage();
- if (Config.LOGD) {
- Log.d(TAG, "unable to download photo " + photoUrl + ", "
- + exceptionMessage + ", ignoring");
- }
- ContentValues values = new ContentValues();
- values.put(Photos.SYNC_ERROR, exceptionMessage);
- Uri photoUri = Uri.withAppendedPath(
- ContentUris.withAppendedId(People.CONTENT_URI, photoId),
- Photos.CONTENT_DIRECTORY);
- cr.update(photoUri, values, null /* where */, null /* where args */);
- break;
- default:
- if (Config.LOGD) {
- Log.d(TAG, "error downloading " + photoUrl, e);
- }
- syncResult.stats.numIoExceptions++;
- return;
- }
- }
- }
- final boolean hasMoreToSync = numFetched < cursor.getCount();
- GDataSyncData.FeedData feedData =
- new GDataSyncData.FeedData(0 /* no update time */,
- numFetched, hasMoreToSync, null /* no lastId */,
- 0 /* no feed index */);
- syncData.feedData.put(feedUrl, feedData);
- } finally {
- cursor.close();
- }
- }
-
- @Override
- protected void getStatsString(StringBuffer sb, SyncResult result) {
- super.getStatsString(sb, result);
- if (mPhotoUploads > 0) {
- sb.append("p").append(mPhotoUploads);
- }
- if (mPhotoDownloads > 0) {
- sb.append("P").append(mPhotoDownloads);
- }
- }
-
- @Override
- public void sendClientDiffs(SyncContext context, SyncableContentProvider clientDiffs,
- SyncableContentProvider serverDiffs, SyncResult syncResult,
- boolean dontSendDeletes) {
- initTempProvider(clientDiffs);
-
- sendClientDiffsImpl(context, clientDiffs, new GroupEntry(), null /* no syncInfo */,
- serverDiffs, syncResult, dontSendDeletes);
-
- // lets go ahead and commit what we have if we successfully made a change
- if (syncResult.madeSomeProgress()) {
- return;
- }
-
- sendClientPhotos(context, clientDiffs, null /* no syncInfo */, syncResult);
-
- // lets go ahead and commit what we have if we successfully made a change
- if (syncResult.madeSomeProgress()) {
- return;
- }
-
- sendClientDiffsImpl(context, clientDiffs, new ContactEntry(), null /* no syncInfo */,
- serverDiffs, syncResult, dontSendDeletes);
- }
-
- protected void sendClientPhotos(SyncContext context, ContentProvider clientDiffs,
- Object syncInfo, SyncResult syncResult) {
- Entry entry = new MediaEntry();
-
- GDataServiceClient client = getGDataServiceClient();
- String authToken = getAuthToken();
- ContentResolver cr = getContext().getContentResolver();
- final Account account = getAccount();
-
- Cursor c = clientDiffs.query(Photos.CONTENT_URI, null /* all columns */,
- null /* no where */, null /* no where args */, null /* default sort order */);
- try {
- int personColumn = c.getColumnIndexOrThrow(Photos.PERSON_ID);
- int dataColumn = c.getColumnIndexOrThrow(Photos.DATA);
- int numRows = c.getCount();
- while (c.moveToNext()) {
- if (mSyncCanceled) {
- if (Config.LOGD) Log.d(TAG, "stopping since the sync was canceled");
- break;
- }
-
- entry.clear();
- context.setStatusText("Updating, " + (numRows - 1) + " to go");
-
- cursorToBaseEntry(entry, account, c);
- String editUrl = entry.getEditUri();
-
- if (TextUtils.isEmpty(editUrl)) {
- if (Config.LOGD) {
- Log.d(TAG, "skipping photo edit for unsynced contact");
- }
- continue;
- }
-
- // Send the request and receive the response
- InputStream inputStream = null;
- byte[] imageData = c.getBlob(dataColumn);
- if (imageData != null) {
- inputStream = new ByteArrayInputStream(imageData);
- }
- Uri photoUri = Uri.withAppendedPath(People.CONTENT_URI,
- c.getString(personColumn) + "/" + Photos.CONTENT_DIRECTORY);
- try {
- if (inputStream != null) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "Updating photo " + entry.toString());
- }
- ++mPhotoUploads;
- client.updateMediaEntry(editUrl, inputStream, IMAGE_MIME_TYPE, authToken);
- } else {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "Deleting photo " + entry.toString());
- }
- client.deleteEntry(editUrl, authToken);
- }
-
- // Mark that this photo is no longer dirty. The next time we sync (which
- // should be soon), we will get the new version of the photo and whether
- // or not there is a new one to download (e.g. if we deleted our version
- // yet there is an evergreen version present).
- ContentValues values = new ContentValues();
- values.put(Photos.EXISTS_ON_SERVER, inputStream == null ? 0 : 1);
- values.put(Photos._SYNC_DIRTY, 0);
- if (cr.update(photoUri, values,
- null /* no where */, null /* no where args */) != 1) {
- Log.e(TAG, "error updating photo " + photoUri + " with values " + values);
- syncResult.stats.numParseExceptions++;
- } else {
- syncResult.stats.numUpdates++;
- }
- continue;
- } catch (ParseException e) {
- Log.e(TAG, "parse error during update of " + ", skipping");
- syncResult.stats.numParseExceptions++;
- } catch (IOException e) {
- if (Config.LOGD) {
- Log.d(TAG, "io error during update of " + entry.toString()
- + ", skipping");
- }
- syncResult.stats.numIoExceptions++;
- } catch (HttpException e) {
- switch (e.getStatusCode()) {
- case HttpException.SC_UNAUTHORIZED:
- if (syncResult.stats.numAuthExceptions == 0) {
- if (Config.LOGD) {
- Log.d(TAG, "auth error during update of " + entry
- + ", skipping");
- }
- }
- syncResult.stats.numAuthExceptions++;
- AccountManager.get(getContext()).invalidateAuthToken(
- "com.google", authToken);
- return;
-
- case HttpException.SC_CONFLICT:
- if (Config.LOGD) {
- Log.d(TAG, "conflict detected during update of " + entry
- + ", skipping");
- }
- syncResult.stats.numConflictDetectedExceptions++;
- break;
- case HttpException.SC_BAD_REQUEST:
- case HttpException.SC_FORBIDDEN:
- case HttpException.SC_NOT_FOUND:
- case HttpException.SC_INTERNAL_SERVER_ERROR:
- default:
- if (Config.LOGD) {
- Log.d(TAG, "error " + e.getMessage() + " during update of "
- + entry.toString() + ", skipping");
- }
- syncResult.stats.numIoExceptions++;
- }
- }
- }
- } finally {
- c.close();
- }
- }
-
- @Override
- protected Cursor getCursorForTable(ContentProvider cp, Class entryClass) {
- return getCursorForTableImpl(cp, entryClass);
- }
-
- protected static Cursor getCursorForTableImpl(ContentProvider cp, Class entryClass) {
- if (entryClass == ContactEntry.class) {
- return cp.query(People.CONTENT_URI, null, null, null, null);
- }
- if (entryClass == GroupEntry.class) {
- return cp.query(Groups.CONTENT_URI, null, null, null, null);
- }
- throw new IllegalArgumentException("unexpected entry class, " + entryClass.getName());
- }
-
- @Override
- protected Cursor getCursorForDeletedTable(ContentProvider cp, Class entryClass) {
- return getCursorForDeletedTableImpl(cp, entryClass);
- }
-
- protected static Cursor getCursorForDeletedTableImpl(ContentProvider cp, Class entryClass) {
- if (entryClass == ContactEntry.class) {
- return cp.query(People.DELETED_CONTENT_URI, null, null, null, null);
- }
- if (entryClass == GroupEntry.class) {
- return cp.query(Groups.DELETED_CONTENT_URI, null, null, null, null);
- }
- throw new IllegalArgumentException("unexpected entry class, " + entryClass.getName());
- }
-
- @Override
- protected String cursorToEntry(SyncContext context, Cursor c, Entry baseEntry,
- Object syncInfo) throws ParseException {
- return cursorToEntryImpl(getContext().getContentResolver(), c, baseEntry, getAccount());
- }
-
- static protected String cursorToEntryImpl(ContentResolver cr, Cursor c, Entry entry,
- Account account) throws ParseException {
- cursorToBaseEntry(entry, account, c);
- String createUrl = null;
- if (entry instanceof ContactEntry) {
- cursorToContactEntry(account, cr, c, (ContactEntry) entry);
- if (entry.getEditUri() == null) {
- createUrl = getContactsFeedForAccount(account);
- }
- } else if (entry instanceof MediaEntry) {
- if (entry.getEditUri() == null) {
- createUrl = getPhotosFeedForAccount(account);
- }
- } else {
- cursorToGroupEntry(c, (GroupEntry) entry);
- if (entry.getEditUri() == null) {
- createUrl = getGroupsFeedForAccount(account);
- }
- }
-
- return createUrl;
- }
-
- private static void cursorToGroupEntry(Cursor c, GroupEntry entry) throws ParseException {
- if (!TextUtils.isEmpty(c.getString(c.getColumnIndexOrThrow(Groups.SYSTEM_ID)))) {
- throw new ParseException("unable to modify system groups");
- }
- entry.setTitle(c.getString(c.getColumnIndexOrThrow(Groups.NAME)));
- entry.setContent(c.getString(c.getColumnIndexOrThrow(Groups.NOTES)));
- entry.setSystemGroup(null);
- }
-
- private static void cursorToContactEntry(Account account, ContentResolver cr, Cursor c,
- ContactEntry entry)
- throws ParseException {
- entry.setTitle(c.getString(c.getColumnIndexOrThrow(People.NAME)));
- entry.setContent(c.getString(c.getColumnIndexOrThrow(People.NOTES)));
- entry.setYomiName(c.getString(c.getColumnIndexOrThrow(People.PHONETIC_NAME)));
-
- long syncLocalId = c.getLong(c.getColumnIndexOrThrow(SyncConstValue._SYNC_LOCAL_ID));
- addContactMethodsToContactEntry(cr, syncLocalId, entry);
- addPhonesToContactEntry(cr, syncLocalId, entry);
- addOrganizationsToContactEntry(cr, syncLocalId, entry);
- addGroupMembershipToContactEntry(account, cr, syncLocalId, entry);
- addExtensionsToContactEntry(cr, syncLocalId, entry);
- }
-
- @Override
- protected void deletedCursorToEntry(SyncContext context, Cursor c, Entry entry) {
- deletedCursorToEntryImpl(c, entry, getAccount());
- }
-
- protected boolean handleAllDeletedUnavailable(GDataSyncData syncData, String feed) {
- // Contacts has no way to clear the contacts for just a given feed so it is unable
- // to handle this condition itself. Instead it returns false, which tell the
- // sync framework that it must handle it.
- return false;
- }
-
- protected static void deletedCursorToEntryImpl(Cursor c, Entry entry, Account account) {
- cursorToBaseEntry(entry, account, c);
- }
-
- private static void cursorToBaseEntry(Entry entry, Account account, Cursor c) {
- String feedUrl;
- if (entry instanceof ContactEntry) {
- feedUrl = getContactsFeedForAccount(account);
- } else if (entry instanceof GroupEntry) {
- feedUrl = getGroupsFeedForAccount(account);
- } else if (entry instanceof MediaEntry) {
- feedUrl = getPhotosFeedForAccount(account);
- } else {
- throw new IllegalArgumentException("bad entry type: " + entry.getClass().getName());
- }
-
- String syncId = c.getString(c.getColumnIndexOrThrow(SyncConstValue._SYNC_ID));
- if (syncId != null) {
- String syncVersion = c.getString(c.getColumnIndexOrThrow(SyncConstValue._SYNC_VERSION));
- entry.setId(feedUrl + "/" + syncId);
- entry.setEditUri(entry.getId() + "/" + syncVersion);
- }
- }
-
- private static void addPhonesToContactEntry(ContentResolver cr, long personId,
- ContactEntry entry)
- throws ParseException {
- Cursor c = cr.query(Phones.CONTENT_URI, null, "person=" + personId, null, null);
- int numberIndex = c.getColumnIndexOrThrow(People.Phones.NUMBER);
- try {
- while (c.moveToNext()) {
- PhoneNumber phoneNumber = new PhoneNumber();
- cursorToContactsElement(phoneNumber, c, PROVIDER_TYPE_TO_ENTRY_PHONE);
- phoneNumber.setPhoneNumber(c.getString(numberIndex));
- entry.addPhoneNumber(phoneNumber);
- }
- } finally {
- if (c != null) c.close();
- }
- }
-
-
- static private void addContactMethodsToContactEntry(ContentResolver cr, long personId,
- ContactEntry entry) throws ParseException {
- Cursor c = cr.query(ContactMethods.CONTENT_URI, null,
- "person=" + personId, null, null);
- int kindIndex = c.getColumnIndexOrThrow(ContactMethods.KIND);
- int dataIndex = c.getColumnIndexOrThrow(ContactMethods.DATA);
- int auxDataIndex = c.getColumnIndexOrThrow(ContactMethods.AUX_DATA);
- try {
- while (c.moveToNext()) {
- int kind = c.getInt(kindIndex);
- switch (kind) {
- case Contacts.KIND_IM: {
- ImAddress address = new ImAddress();
- cursorToContactsElement(address, c, PROVIDER_TYPE_TO_ENTRY_IM);
- address.setAddress(c.getString(dataIndex));
- Object object = ContactMethods.decodeImProtocol(c.getString(auxDataIndex));
- if (object == null) {
- address.setProtocolPredefined(ImAddress.PROTOCOL_NONE);
- } else if (object instanceof Integer) {
- address.setProtocolPredefined(
- PROVIDER_IM_PROTOCOL_TO_ENTRY_PROTOCOL.get((Integer)object));
- } else {
- if (!(object instanceof String)) {
- throw new IllegalArgumentException("expected an String, " + object);
- }
- address.setProtocolPredefined(ImAddress.PROTOCOL_CUSTOM);
- address.setProtocolCustom((String)object);
- }
- entry.addImAddress(address);
- break;
- }
- case Contacts.KIND_POSTAL: {
- PostalAddress address = new PostalAddress();
- cursorToContactsElement(address, c, PROVIDER_TYPE_TO_ENTRY_POSTAL);
- address.setValue(c.getString(dataIndex));
- entry.addPostalAddress(address);
- break;
- }
- case Contacts.KIND_EMAIL: {
- EmailAddress address = new EmailAddress();
- cursorToContactsElement(address, c, PROVIDER_TYPE_TO_ENTRY_EMAIL);
- address.setAddress(c.getString(dataIndex));
- entry.addEmailAddress(address);
- break;
- }
- }
- }
- } finally {
- if (c != null) c.close();
- }
- }
-
- private static void addOrganizationsToContactEntry(ContentResolver cr, long personId,
- ContactEntry entry) throws ParseException {
- Cursor c = cr.query(Organizations.CONTENT_URI, null,
- "person=" + personId, null, null);
- try {
- int companyIndex = c.getColumnIndexOrThrow(Organizations.COMPANY);
- int titleIndex = c.getColumnIndexOrThrow(Organizations.TITLE);
- while (c.moveToNext()) {
- Organization organization = new Organization();
- cursorToContactsElement(organization, c, PROVIDER_TYPE_TO_ENTRY_ORGANIZATION);
- organization.setName(c.getString(companyIndex));
- organization.setTitle(c.getString(titleIndex));
- entry.addOrganization(organization);
- }
- } finally {
- if (c != null) c.close();
- }
- }
-
- private static void addGroupMembershipToContactEntry(Account account, ContentResolver cr,
- long personId, ContactEntry entry) throws ParseException {
- Cursor c = cr.query(GroupMembership.RAW_CONTENT_URI, null,
- "person=" + personId, null, null);
- try {
- int serverIdIndex = c.getColumnIndexOrThrow(GroupMembership.GROUP_SYNC_ID);
- int localIdIndex = c.getColumnIndexOrThrow(GroupMembership.GROUP_ID);
- while (c.moveToNext()) {
- String serverId = c.getString(serverIdIndex);
- if (serverId == null) {
- final Uri groupUri = ContentUris
- .withAppendedId(Groups.CONTENT_URI, c.getLong(localIdIndex));
- Cursor groupCursor = cr.query(groupUri, new String[]{Groups._SYNC_ID},
- null, null, null);
- try {
- if (groupCursor.moveToNext()) {
- serverId = groupCursor.getString(0);
- }
- } finally {
- groupCursor.close();
- }
- }
- if (serverId == null) {
- // the group hasn't been synced yet, we can't complete this operation since
- // we don't know what server id to use for the group
- throw new ParseException("unable to construct GroupMembershipInfo since the "
- + "group _sync_id isn't known yet, will retry later");
- }
- GroupMembershipInfo groupMembershipInfo = new GroupMembershipInfo();
- String groupId = getCanonicalGroupsFeedForAccount(account) + "/" + serverId;
- groupMembershipInfo.setGroup(groupId);
- groupMembershipInfo.setDeleted(false);
- entry.addGroup(groupMembershipInfo);
- }
- } finally {
- if (c != null) c.close();
- }
- }
-
- private static void addExtensionsToContactEntry(ContentResolver cr, long personId,
- ContactEntry entry) throws ParseException {
- Cursor c = cr.query(Extensions.CONTENT_URI, null, "person=" + personId, null, null);
- try {
- JSONObject jsonObject = new JSONObject();
- int nameIndex = c.getColumnIndexOrThrow(Extensions.NAME);
- int valueIndex = c.getColumnIndexOrThrow(Extensions.VALUE);
- if (c.getCount() == 0) return;
- while (c.moveToNext()) {
- try {
- jsonObject.put(c.getString(nameIndex), c.getString(valueIndex));
- } catch (JSONException e) {
- throw new ParseException("bad key or value", e);
- }
- }
- ExtendedProperty extendedProperty = new ExtendedProperty();
- extendedProperty.setName("android");
- final String jsonString = jsonObject.toString();
- if (jsonString == null) {
- throw new ParseException("unable to convert cursor into a JSON string, "
- + DatabaseUtils.dumpCursorToString(c));
- }
- extendedProperty.setXmlBlob(jsonString);
- entry.addExtendedProperty(extendedProperty);
- } finally {
- if (c != null) c.close();
- }
- }
-
- private static void cursorToContactsElement(ContactsElement element,
- Cursor c, HashMap<Integer, Byte> map) {
- final int typeIndex = c.getColumnIndexOrThrow("type");
- final int labelIndex = c.getColumnIndexOrThrow("label");
- final int isPrimaryIndex = c.getColumnIndexOrThrow("isprimary");
-
- element.setLabel(c.getString(labelIndex));
- element.setType(map.get(c.getInt(typeIndex)));
- element.setIsPrimary(c.getInt(isPrimaryIndex) != 0);
- }
-
- private static void contactsElementToValues(ContentValues values, ContactsElement element,
- HashMap<Byte, Integer> map) {
- values.put("type", map.get(element.getType()));
- values.put("label", element.getLabel());
- values.put("isprimary", element.isPrimary() ? 1 : 0);
- }
-
- /*
- * Takes the entry, casts it to a ContactEntry and executes the appropriate
- * actions on the ContentProvider to represent the entry.
- */
- protected void updateProvider(Feed feed, Long syncLocalId,
- Entry baseEntry, ContentProvider provider, Object syncInfo,
- GDataSyncData.FeedData feedSyncData) throws ParseException {
-
- // This is a hack to delete these incorrectly created contacts named "Starred in Android"
- if (baseEntry instanceof ContactEntry
- && "Starred in Android".equals(baseEntry.getTitle())) {
- Log.i(TAG, "Deleting incorrectly created contact from the server: " + baseEntry);
- GDataServiceClient client = getGDataServiceClient();
- try {
- client.deleteEntry(baseEntry.getEditUri(), getAuthToken());
- } catch (IOException e) {
- Log.i(TAG, " exception while deleting contact: " + baseEntry, e);
- } catch (com.google.wireless.gdata.client.HttpException e) {
- Log.i(TAG, " exception while deleting contact: " + baseEntry, e);
- }
- }
-
- updateProviderImpl(getAccount(), syncLocalId, baseEntry, provider);
- }
-
- protected static void updateProviderImpl(Account account, Long syncLocalId,
- Entry entry, ContentProvider provider) throws ParseException {
- // If this is a deleted entry then add it to the DELETED_CONTENT_URI
- ContentValues deletedValues = null;
- if (entry.isDeleted()) {
- deletedValues = new ContentValues();
- deletedValues.put(SyncConstValue._SYNC_LOCAL_ID, syncLocalId);
- final String id = entry.getId();
- final String editUri = entry.getEditUri();
- if (!TextUtils.isEmpty(id)) {
- deletedValues.put(SyncConstValue._SYNC_ID, lastItemFromUri(id));
- }
- if (!TextUtils.isEmpty(editUri)) {
- deletedValues.put(SyncConstValue._SYNC_VERSION, lastItemFromUri(editUri));
- }
- deletedValues.put(SyncConstValue._SYNC_ACCOUNT, account.name);
- deletedValues.put(SyncConstValue._SYNC_ACCOUNT_TYPE, account.type);
- }
-
- if (entry instanceof ContactEntry) {
- if (deletedValues != null) {
- provider.insert(People.DELETED_CONTENT_URI, deletedValues);
- return;
- }
- updateProviderWithContactEntry(account, syncLocalId, (ContactEntry) entry, provider);
- return;
- }
- if (entry instanceof GroupEntry) {
- if (deletedValues != null) {
- provider.insert(Groups.DELETED_CONTENT_URI, deletedValues);
- return;
- }
- updateProviderWithGroupEntry(account, syncLocalId, (GroupEntry) entry, provider);
- return;
- }
- throw new IllegalArgumentException("unknown entry type, " + entry.getClass().getName());
- }
-
- protected static void updateProviderWithContactEntry(Account account, Long syncLocalId,
- ContactEntry entry, ContentProvider provider) throws ParseException {
- final String name = entry.getTitle();
- final String notes = entry.getContent();
- final String yomiName = entry.getYomiName();
- final String personSyncId = lastItemFromUri(entry.getId());
- final String personSyncVersion = lastItemFromUri(entry.getEditUri());
-
- // Store the info about the person
- ContentValues values = new ContentValues();
- values.put(People.NAME, name);
- values.put(People.NOTES, notes);
- values.put(People.PHONETIC_NAME, yomiName);
- values.put(SyncConstValue._SYNC_ACCOUNT, account.name);
- values.put(SyncConstValue._SYNC_ACCOUNT_TYPE, account.type);
- values.put(SyncConstValue._SYNC_ID, personSyncId);
- values.put(SyncConstValue._SYNC_DIRTY, "0");
- values.put(SyncConstValue._SYNC_LOCAL_ID, syncLocalId);
- values.put(SyncConstValue._SYNC_TIME, personSyncVersion);
- values.put(SyncConstValue._SYNC_VERSION, personSyncVersion);
- Uri personUri = provider.insert(People.CONTENT_URI, values);
-
- // Store the photo information
- final boolean photoExistsOnServer = !TextUtils.isEmpty(entry.getLinkPhotoHref());
- final String photoVersion = lastItemFromUri(entry.getLinkEditPhotoHref());
- values.clear();
- values.put(Photos.PERSON_ID, ContentUris.parseId(personUri));
- values.put(Photos.EXISTS_ON_SERVER, photoExistsOnServer ? 1 : 0);
- values.put(SyncConstValue._SYNC_ACCOUNT, account.name);
- values.put(SyncConstValue._SYNC_ACCOUNT_TYPE, account.type);
- values.put(SyncConstValue._SYNC_ID, personSyncId);
- values.put(SyncConstValue._SYNC_DIRTY, 0);
- values.put(SyncConstValue._SYNC_LOCAL_ID, syncLocalId);
- values.put(SyncConstValue._SYNC_TIME, photoVersion);
- values.put(SyncConstValue._SYNC_VERSION, photoVersion);
- if (provider.insert(Photos.CONTENT_URI, values) == null) {
- Log.e(TAG, "error inserting photo row, " + values);
- }
-
- // Store each email address
- for (Object object : entry.getEmailAddresses()) {
- EmailAddress email = (EmailAddress) object;
- values.clear();
- contactsElementToValues(values, email, ENTRY_TYPE_TO_PROVIDER_EMAIL);
- values.put(ContactMethods.DATA, email.getAddress());
- values.put(ContactMethods.KIND, Contacts.KIND_EMAIL);
- Uri uri = Uri.withAppendedPath(personUri, People.ContactMethods.CONTENT_DIRECTORY);
- provider.insert(uri, values);
- }
-
- // Store each postal address
- for (Object object : entry.getPostalAddresses()) {
- PostalAddress address = (PostalAddress) object;
- values.clear();
- contactsElementToValues(values, address, ENTRY_TYPE_TO_PROVIDER_POSTAL);
- values.put(ContactMethods.DATA, address.getValue());
- values.put(ContactMethods.KIND, Contacts.KIND_POSTAL);
- Uri uri = Uri.withAppendedPath(personUri, People.ContactMethods.CONTENT_DIRECTORY);
- provider.insert(uri, values);
- }
-
- // Store each im address
- for (Object object : entry.getImAddresses()) {
- ImAddress address = (ImAddress) object;
- values.clear();
- contactsElementToValues(values, address, ENTRY_TYPE_TO_PROVIDER_IM);
- values.put(ContactMethods.DATA, address.getAddress());
- values.put(ContactMethods.KIND, Contacts.KIND_IM);
- final byte protocolType = address.getProtocolPredefined();
- if (protocolType == ImAddress.PROTOCOL_NONE) {
- // don't add anything
- } else if (protocolType == ImAddress.PROTOCOL_CUSTOM) {
- values.put(ContactMethods.AUX_DATA,
- ContactMethods.encodeCustomImProtocol(address.getProtocolCustom()));
- } else {
- Integer providerProtocolType =
- ENTRY_IM_PROTOCOL_TO_PROVIDER_PROTOCOL .get(protocolType);
- if (providerProtocolType == null) {
- throw new IllegalArgumentException("unknown protocol type, " + protocolType);
- }
- values.put(ContactMethods.AUX_DATA,
- ContactMethods.encodePredefinedImProtocol(providerProtocolType));
- }
- Uri uri = Uri.withAppendedPath(personUri, People.ContactMethods.CONTENT_DIRECTORY);
- provider.insert(uri, values);
- }
-
- // Store each organization
- for (Object object : entry.getOrganizations()) {
- Organization organization = (Organization) object;
- values.clear();
- contactsElementToValues(values, organization, ENTRY_TYPE_TO_PROVIDER_ORGANIZATION);
- values.put(Organizations.COMPANY, organization.getName());
- values.put(Organizations.TITLE, organization.getTitle());
- values.put(Organizations.COMPANY, organization.getName());
- Uri uri = Uri.withAppendedPath(personUri, Organizations.CONTENT_DIRECTORY);
- provider.insert(uri, values);
- }
-
- // Store each group
- for (Object object : entry.getGroups()) {
- GroupMembershipInfo groupMembershipInfo = (GroupMembershipInfo) object;
- if (groupMembershipInfo.isDeleted()) {
- continue;
- }
- values.clear();
- values.put(GroupMembership.GROUP_SYNC_ACCOUNT, account.name);
- values.put(GroupMembership.GROUP_SYNC_ACCOUNT_TYPE, account.type);
- values.put(GroupMembership.GROUP_SYNC_ID,
- lastItemFromUri(groupMembershipInfo.getGroup()));
- Uri uri = Uri.withAppendedPath(personUri, GroupMembership.CONTENT_DIRECTORY);
- provider.insert(uri, values);
- }
-
- // Store each phone number
- for (Object object : entry.getPhoneNumbers()) {
- PhoneNumber phone = (PhoneNumber) object;
- values.clear();
- contactsElementToValues(values, phone, ENTRY_TYPE_TO_PROVIDER_PHONE);
- values.put(People.Phones.NUMBER, phone.getPhoneNumber());
- values.put(People.Phones.LABEL, phone.getLabel());
- Uri uri = Uri.withAppendedPath(personUri, People.Phones.CONTENT_DIRECTORY);
- provider.insert(uri, values);
- }
-
- // Store the extended properties
- for (Object object : entry.getExtendedProperties()) {
- ExtendedProperty extendedProperty = (ExtendedProperty) object;
- if (!"android".equals(extendedProperty.getName())) {
- continue;
- }
- JSONObject jsonObject = null;
- try {
- jsonObject = new JSONObject(extendedProperty.getXmlBlob());
- } catch (JSONException e) {
- Log.w(TAG, "error parsing the android extended property, dropping, entry is "
- + entry.toString());
- continue;
- }
- Iterator jsonIterator = jsonObject.keys();
- while (jsonIterator.hasNext()) {
- String key = (String)jsonIterator.next();
- values.clear();
- values.put(Extensions.NAME, key);
- try {
- values.put(Extensions.VALUE, jsonObject.getString(key));
- } catch (JSONException e) {
- // this should never happen, since we just got the key from the iterator
- }
- Uri uri = Uri.withAppendedPath(personUri, People.Extensions.CONTENT_DIRECTORY);
- if (null == provider.insert(uri, values)) {
- Log.e(TAG, "Error inserting extension into provider, uri "
- + uri + ", values " + values);
- }
- }
- break;
- }
- }
-
- protected static void updateProviderWithGroupEntry(Account account, Long syncLocalId,
- GroupEntry entry, ContentProvider provider) throws ParseException {
- ContentValues values = new ContentValues();
- values.put(Groups.NAME, entry.getTitle());
- values.put(Groups.NOTES, entry.getContent());
- values.put(Groups.SYSTEM_ID, entry.getSystemGroup());
- values.put(Groups._SYNC_ACCOUNT, account.name);
- values.put(Groups._SYNC_ACCOUNT_TYPE, account.type);
- values.put(Groups._SYNC_ID, lastItemFromUri(entry.getId()));
- values.put(Groups._SYNC_DIRTY, 0);
- values.put(Groups._SYNC_LOCAL_ID, syncLocalId);
- final String editUri = entry.getEditUri();
- final String syncVersion = editUri == null ? null : lastItemFromUri(editUri);
- values.put(Groups._SYNC_TIME, syncVersion);
- values.put(Groups._SYNC_VERSION, syncVersion);
- provider.insert(Groups.CONTENT_URI, values);
- }
-
- private static String lastItemFromUri(String url) {
- return url.substring(url.lastIndexOf('/') + 1);
- }
-
- protected void savePhoto(long person, InputStream photoInput, String photoVersion)
- throws IOException {
- try {
- ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
- byte[] data = new byte[1024];
- while(true) {
- int bytesRead = photoInput.read(data);
- if (bytesRead < 0) break;
- byteStream.write(data, 0, bytesRead);
- }
-
- ContentValues values = new ContentValues();
- // we have to include this here otherwise the provider will set it to 1
- values.put(Photos._SYNC_DIRTY, 0);
- values.put(Photos.LOCAL_VERSION, photoVersion);
- values.put(Photos.DATA, byteStream.toByteArray());
- Uri photoUri = Uri.withAppendedPath(People.CONTENT_URI,
- "" + person + "/" + Photos.CONTENT_DIRECTORY);
- if (getContext().getContentResolver().update(photoUri, values,
- "_sync_dirty=0", null) > 0) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "savePhoto: updated " + photoUri + " with values " + values);
- }
- } else {
- Log.e(TAG, "savePhoto: update of " + photoUri + " with values " + values
- + " affected no rows");
- }
- } finally {
- try {
- if (photoInput != null) photoInput.close();
- } catch (IOException e) {
- // we don't care about exceptions here
- }
- }
- }
-
- /**
- * Make sure the contacts subscriptions we expect based on the current
- * accounts are present and that there aren't any extra subscriptions
- * that we don't expect.
- */
- @Override
- public void onAccountsChanged(Account[] accountsArray) {
- if (!"yes".equals(SystemProperties.get("ro.config.sync"))) {
- return;
- }
-
- ContentResolver cr = getContext().getContentResolver();
- for (Account account : accountsArray) {
- // TODO(fredq) should be using account instead of null
- String value = Contacts.Settings.getSetting(cr, null,
- Contacts.Settings.SYNC_EVERYTHING);
- if (value == null) {
- // TODO(fredq) should be using account instead of null
- Contacts.Settings.setSetting(cr, null, Contacts.Settings.SYNC_EVERYTHING, "1");
- }
- updateSubscribedFeeds(cr, account);
- }
- }
-
- /**
- * Returns the contacts feed url for a specific account.
- * @param account The account
- * @return The contacts feed url for a specific account.
- */
- public static String getContactsFeedForAccount(Account account) {
- String url = CONTACTS_FEED_URL + account.name + "/base2_property-android";
- return rewriteUrlforAccount(account, url);
- }
-
- /**
- * Returns the contacts group feed url for a specific account.
- * @param account The account
- * @param groupSyncId The group id
- * @return The contacts feed url for a specific account and group.
- */
- public static String getContactsFeedForGroup(Account account, String groupSyncId) {
- String groupId = getCanonicalGroupsFeedForAccount(account);
- try {
- groupId = URLEncoder.encode(groupId, "utf-8");
- } catch (UnsupportedEncodingException e) {
- throw new IllegalArgumentException("unable to url encode group: " + groupId);
- }
- return getContactsFeedForAccount(account) + "?group=" + groupId + "/" + groupSyncId;
- }
-
- /**
- * Returns the groups feed url for a specific account.
- * @param account The account
- * @return The groups feed url for a specific account.
- */
- public static String getGroupsFeedForAccount(Account account) {
- String url = GROUPS_FEED_URL + account.name + "/base2_property-android";
- return rewriteUrlforAccount(account, url);
- }
-
- /**
- * Returns the groups feed url for a specific account that should be
- * used as the foreign reference to this group, e.g. in the
- * group membership element of the ContactEntry. The canonical groups
- * feed always uses http (so it doesn't need to be rewritten) and it always
- * uses the base projection.
- * @param account The account
- * @return The groups feed url for a specific account.
- */
- public static String getCanonicalGroupsFeedForAccount(Account account) {
- return GROUPS_FEED_URL + account.name + "/base";
- }
-
- /**
- * Returns the photo feed url for a specific account.
- * @param account The account
- * @return The photo feed url for a specific account.
- */
- public static String getPhotosFeedForAccount(Account account) {
- String url = PHOTO_FEED_URL + account.name;
- return rewriteUrlforAccount(account, url);
- }
-
- protected static boolean getFeedReturnsPartialDiffs() {
- return true;
- }
-
- @Override
- protected void updateQueryParameters(QueryParams params, GDataSyncData.FeedData feedSyncData) {
- // we want to get the events ordered by last modified, so we can
- // recover in case we cannot process the entire feed.
- params.setParamValue("orderby", "lastmodified");
- params.setParamValue("sortorder", "ascending");
-
- // set showdeleted so that we get tombstones, only do this when we
- // are doing an incremental sync
- if (params.getUpdatedMin() != null) {
- params.setParamValue("showdeleted", "true");
- }
- }
-
- @Override
- public void onSyncStarting(SyncContext context, Account account, boolean manualSync,
- SyncResult result) {
- mPerformedGetServerDiffs = false;
- mIsManualSync = manualSync;
- mPhotoDownloads = 0;
- mPhotoUploads = 0;
- super.onSyncStarting(context, account, manualSync, result);
- }
-
- @Override
- public void onSyncEnding(SyncContext context, boolean success) {
- final ContentResolver cr = getContext().getContentResolver();
-
- if (success && mPerformedGetServerDiffs && !mSyncCanceled) {
- final Account account = getAccount();
- Cursor cursor = cr.query(
- Photos.CONTENT_URI,
- new String[]{Photos._SYNC_ID, Photos._SYNC_VERSION, Photos.PERSON_ID,
- Photos.DOWNLOAD_REQUIRED}, ""
- + "_sync_account=? AND _sync_account_type=? AND download_required != 0",
- new String[]{account.name, account.type}, null);
- try {
- if (cursor.getCount() != 0) {
- Bundle extras = new Bundle();
- extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, mIsManualSync);
- extras.putString("feed", ContactsSyncAdapter.getPhotosFeedForAccount(account));
- ContentResolver.requestSync(account, Contacts.AUTHORITY, extras);
- }
- } finally {
- cursor.close();
- }
- }
-
- super.onSyncEnding(context, success);
- }
-
- public static void updateSubscribedFeeds(ContentResolver cr, Account account) {
- Set<String> feedsToSync = Sets.newHashSet();
- feedsToSync.add(getGroupsFeedForAccount(account));
- addContactsFeedsToSync(cr, account, feedsToSync);
-
- Cursor c = SubscribedFeeds.Feeds.query(cr, sSubscriptionProjection,
- SubscribedFeeds.Feeds.AUTHORITY + "=?"
- + " AND " + SubscribedFeeds.Feeds._SYNC_ACCOUNT + "=?"
- + " AND " + SubscribedFeeds.Feeds._SYNC_ACCOUNT_TYPE + "=?",
- new String[]{Contacts.AUTHORITY, account.name, account.type}, null);
- try {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "scanning over subscriptions with authority "
- + Contacts.AUTHORITY + " and account " + account);
- }
- c.moveToNext();
- while (!c.isAfterLast()) {
- String feedInCursor = c.getString(1);
- if (feedsToSync.contains(feedInCursor)) {
- feedsToSync.remove(feedInCursor);
- c.moveToNext();
- } else {
- c.deleteRow();
- }
- }
- c.commitUpdates();
- } finally {
- c.close();
- }
-
- // any feeds remaining in feedsToSync need a subscription
- for (String feed : feedsToSync) {
- SubscribedFeeds.addFeed(cr, feed, account, Contacts.AUTHORITY, ContactsClient.SERVICE);
-
- // request a sync of this feed
- Bundle extras = new Bundle();
- extras.putString("feed", feed);
- ContentResolver.requestSync(account, Contacts.AUTHORITY, extras);
- }
- }
-}