michael@0: /**
michael@0: * Copyright (c) 2012-2013, Gerald Garcia, David Wiesner, Timo Berger
michael@8: *
michael@0: * This file is part of Andoid Caldav Sync Adapter Free.
michael@0: *
michael@0: * Andoid Caldav Sync Adapter Free is free software: you can redistribute
michael@0: * it and/or modify it under the terms of the GNU General Public License
michael@0: * as published by the Free Software Foundation, either version 3 of the
michael@0: * License, or at your option any later version.
michael@0: *
michael@0: * Andoid Caldav Sync Adapter Free is distributed in the hope that
michael@0: * it will be useful, but WITHOUT ANY WARRANTY; without even the implied
michael@0: * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
michael@0: * GNU General Public License for more details.
michael@0: *
michael@0: * You should have received a copy of the GNU General Public License
michael@0: * along with Andoid Caldav Sync Adapter Free.
michael@0: * If not, see .
michael@8: *
michael@0: */
michael@0:
michael@0: package org.gege.caldavsyncadapter.syncadapter;
michael@0:
michael@0: import android.accounts.Account;
michael@0: import android.accounts.AccountManager;
michael@0: import android.content.AbstractThreadedSyncAdapter;
michael@0: import android.content.ContentProviderClient;
michael@0: import android.content.ContentUris;
michael@0: import android.content.ContentValues;
michael@0: import android.content.Context;
michael@0: import android.content.SyncResult;
michael@0: import android.content.SyncStats;
michael@0: import android.content.pm.PackageManager.NameNotFoundException;
michael@0: import android.database.Cursor;
michael@0: import android.net.Uri;
michael@0: import android.os.Bundle;
michael@0: import android.os.RemoteException;
michael@0: import android.provider.CalendarContract.Attendees;
michael@0: import android.provider.CalendarContract.Calendars;
michael@0: import android.provider.CalendarContract.Events;
michael@0: import android.provider.CalendarContract.Reminders;
michael@0: import android.util.Log;
michael@0:
michael@8: import net.fortuna.ical4j.data.ParserException;
michael@8:
michael@8: import org.apache.http.ParseException;
michael@8: import org.apache.http.client.ClientProtocolException;
michael@8: import org.gege.caldavsyncadapter.Constants;
michael@8: import org.gege.caldavsyncadapter.Event;
michael@8: import org.gege.caldavsyncadapter.android.entities.AndroidEvent;
michael@8: import org.gege.caldavsyncadapter.authenticator.AuthenticatorActivity;
michael@8: import org.gege.caldavsyncadapter.caldav.CaldavFacade;
michael@8: import org.gege.caldavsyncadapter.caldav.CaldavProtocolException;
michael@8: import org.gege.caldavsyncadapter.caldav.entities.CalendarEvent;
michael@8: import org.gege.caldavsyncadapter.caldav.entities.CalendarList;
michael@8: import org.gege.caldavsyncadapter.caldav.entities.DavCalendar;
michael@8: import org.gege.caldavsyncadapter.caldav.entities.DavCalendar.CalendarSource;
michael@8: import org.gege.caldavsyncadapter.syncadapter.notifications.NotificationsHelper;
michael@8: import org.xml.sax.SAXException;
michael@8:
michael@8: import java.io.IOException;
michael@8: import java.net.URI;
michael@8: import java.net.URISyntaxException;
michael@8: import java.util.ArrayList;
michael@8:
michael@8: import javax.xml.parsers.ParserConfigurationException;
michael@8:
michael@8: //import java.net.MalformedURLException;
michael@8: //import java.security.GeneralSecurityException;
michael@8:
michael@0: public class SyncAdapter extends AbstractThreadedSyncAdapter {
michael@0:
michael@8: private static final String TAG = "SyncAdapter";
michael@8: private AccountManager mAccountManager;
michael@8: private String mVersion = "";
michael@8: private int mCountPerformSync = 0;
michael@8: private int mCountSyncCanceled = 0;
michael@8: private int mCountProviderFailed = 0;
michael@8:
michael@8: private int mCountProviderFailedMax = 3;
michael@0: // private Context mContext;
michael@8:
michael@0:
michael@0: /* private static final String[] CALENDAR_PROJECTION = new String[] {
michael@0: Calendars._ID, // 0
michael@0: Calendars.ACCOUNT_NAME, // 1
michael@0: Calendars.CALENDAR_DISPLAY_NAME, // 2
michael@0: Calendars.OWNER_ACCOUNT, // 3
michael@0: Calendar.CTAG // 4
michael@0: };*/
michael@0:
michael@0: /* // The indices for the projection array above.
michael@0: private static final int PROJECTION_ID_INDEX = 0;
michael@0: private static final int PROJECTION_ACCOUNT_NAME_INDEX = 1;
michael@0: private static final int PROJECTION_DISPLAY_NAME_INDEX = 2;
michael@0: private static final int PROJECTION_OWNER_ACCOUNT_INDEX = 3;
michael@0: */
michael@0:
michael@0: /*
michael@0: private static final String[] EVENT_PROJECTION = new String[] {
michael@0: Events._ID,
michael@0: Events._SYNC_ID,
michael@0: Events.SYNC_DATA1,
michael@0: Events.CALENDAR_ID
michael@0: };
michael@0: */
michael@8:
michael@8: // ignore same CTag
michael@8: //private static final boolean FORCE_SYNCHRONIZE = false;
michael@8: // drop all calendar before synchro
michael@8: //private static final boolean DROP_CALENDAR_EVENTS = false;
michael@8:
michael@8: public SyncAdapter(Context context, boolean autoInitialize) {
michael@8: super(context, autoInitialize);
michael@8: //android.os.Debug.waitForDebugger();
michael@8: mAccountManager = AccountManager.get(context);
michael@8: try {
michael@8: mVersion = context.getPackageManager()
michael@8: .getPackageInfo(context.getPackageName(), 0).versionName;
michael@8: } catch (NameNotFoundException e) {
michael@8: e.printStackTrace();
michael@8: }
michael@0: // mContext = context;
michael@8: }
michael@0:
michael@8: @Override
michael@8: public void onPerformSync(Account account, Bundle extras, String authority,
michael@8: ContentProviderClient provider, SyncResult syncResult) {
michael@8: boolean bolError = false;
michael@0:
michael@8: String url = mAccountManager.getUserData(account, AuthenticatorActivity.USER_DATA_URL_KEY);
michael@8: String trust = mAccountManager.getUserData(account, Constants.USER_DATA_TRUST_ALL_KEY);
michael@8: this.mCountPerformSync += 1;
michael@8: Log.v(TAG, "onPerformSync() count:" + String.valueOf(this.mCountPerformSync) + " on " + account.name + " with URL " + url);
michael@0:
michael@8: CalendarList serverCalList;
michael@0:
michael@8: CalendarList androidCalList = new CalendarList(account, provider, CalendarSource.Android, url);
michael@8: androidCalList.readCalendarFromClient();
michael@8: ArrayList notifyList = new ArrayList();
michael@0:
michael@8: try {
michael@8: String Username = "";
michael@8: String UserDataVersion = mAccountManager.getUserData(account, AuthenticatorActivity.USER_DATA_VERSION);
michael@8: if (UserDataVersion == null) {
michael@8: Username = account.name;
michael@8: } else {
michael@8: Username = mAccountManager.getUserData(account, AuthenticatorActivity.USER_DATA_USERNAME);
michael@8: }
michael@0:
michael@8: CaldavFacade facade = new CaldavFacade(Username, mAccountManager.getPassword(account), url, trust);
michael@8: facade.setAccount(account);
michael@8: facade.setProvider(provider);
michael@8: facade.setVersion(mVersion);
michael@8: serverCalList = facade.getCalendarList(this.getContext());
michael@8: //String davProperties = facade.getLastDav();
michael@8: Log.i(TAG, String.valueOf(androidCalList.getCalendarList()
michael@8: .size()) + " calendars found at android");
michael@0:
michael@8: for (DavCalendar serverCalendar : serverCalList.getCalendarList()) {
michael@8: Log.i(TAG, "Detected calendar name=" + serverCalendar.getCalendarDisplayName() + " URI=" + serverCalendar
michael@8: .getURI());
michael@8:
michael@8: Uri androidCalendarUri = serverCalendar.checkAndroidCalendarList(androidCalList, this
michael@8: .getContext());
michael@8:
michael@8: // check if the adapter was able to get an existing calendar or create a new one
michael@8: if (androidCalendarUri != null) {
michael@8: // the provider seems to work correct, reset the counter
michael@8: mCountProviderFailed = 0;
michael@8: DavCalendar androidCalendar = androidCalList.getCalendarByAndroidUri(androidCalendarUri);
michael@8:
michael@8: //if ((FORCE_SYNCHRONIZE) || (androidCalendar.getcTag() == null) || (!androidCalendar.getcTag().equals(serverCalendar.getcTag()))) {
michael@8: if ((androidCalendar.getcTag() == null) || (!androidCalendar.getcTag()
michael@8: .equals(serverCalendar.getcTag()))) {
michael@8: Log.d(TAG, "CTag has changed, something to synchronise");
michael@8: if (serverCalendar.readCalendarEvents(facade)) {
michael@8: this.synchroniseEvents(androidCalendar, serverCalendar, syncResult.stats, notifyList);
michael@8:
michael@8: Log.d(TAG, "Updating stored CTag");
michael@8: //serverCalendar.updateAndroidCalendar(androidCalendarUri, Calendar.CTAG, serverCalendar.getcTag());
michael@8: androidCalendar.setCTag(serverCalendar.getcTag(), true);
michael@8: } else {
michael@8: Log.d(TAG, "unable to read events from server calendar");
michael@8: }
michael@8: } else {
michael@8: Log.d(TAG, "CTag has not changed, nothing to do");
michael@0:
michael@0: /* this is unnecessary. "SkippedEntries" are:
michael@0: * Counter for tracking how many entries, either from the server or the local store,
michael@0: * were ignored during the sync operation. This could happen if the SyncAdapter detected
michael@0: * some unparsable data but decided to skip it and move on rather than failing immediately.
michael@0: */
michael@0:
michael@0: /*long CalendarID = ContentUris.parseId(androidCalendarUri);
michael@0: String selection = "(" + Events.CALENDAR_ID + " = ?)";
michael@0: String[] selectionArgs = new String[] {String.valueOf(CalendarID)};
michael@0: Cursor countCursor = provider.query(Events.CONTENT_URI, new String[] {"count(*) AS count"},
michael@0: selection,
michael@0: selectionArgs,
michael@0: null);
michael@0:
michael@0: countCursor.moveToFirst();
michael@0: int count = countCursor.getInt(0);
michael@0: syncResult.stats.numSkippedEntries += count;
michael@0: countCursor.close();*/
michael@8:
michael@8: }
michael@8:
michael@8: this.checkDirtyAndroidEvents(provider, account, androidCalendarUri, facade, serverCalendar
michael@8: .getURI(), syncResult.stats, notifyList);
michael@8: } else {
michael@8: // this happens if the data provider failes to get an existing or create a new calendar
michael@8: mCountProviderFailed += 1;
michael@8: Log.e(TAG, "failed to get an existing or create a new calendar");
michael@8: syncResult.stats.numIoExceptions += 1;
michael@8: if (mCountProviderFailed >= mCountProviderFailedMax) {
michael@8: // see issue #96
michael@8: NotificationsHelper.signalSyncErrors(this.getContext(), "Caldav sync error (provider failed)", "are you using CyanogenMod in Incognito Mode?");
michael@8: } else {
michael@8: NotificationsHelper.signalSyncErrors(this.getContext(), "Caldav sync error (provider failed)", "the provider failed to get an existing or create a new calendar");
michael@8: }
michael@8: bolError = true;
michael@8: }
michael@8: }
michael@8:
michael@8: if (!bolError) {
michael@8: // check whether a calendar is not synced -> delete it at android
michael@8: androidCalList.deleteCalendarOnClientSideOnly(this.getContext());
michael@8: }
michael@8:
michael@8: // notify the ContentResolver
michael@8: for (Uri uri : androidCalList.getNotifyList()) {
michael@8: this.getContext().getContentResolver().notifyChange(uri, null);
michael@8: }
michael@8: for (Uri uri : serverCalList.getNotifyList()) {
michael@8: this.getContext().getContentResolver().notifyChange(uri, null);
michael@8: }
michael@8: for (Uri uri : notifyList) {
michael@8: this.getContext().getContentResolver().notifyChange(uri, null);
michael@8: }
michael@8:
michael@8: //Log.i(TAG,"Statistiks for Calendar: " + serverCalendar.getURI().toString());
michael@8: //Log.i(TAG,"Statistiks for AndroidCalendar: " + androidCalendar.getAndroidCalendarUri().toString());
michael@8: Log.i(TAG, "Entries: " + String.valueOf(syncResult.stats.numEntries));
michael@8: Log.i(TAG, "Rows inserted: " + String.valueOf(syncResult.stats.numInserts));
michael@8: Log.i(TAG, "Rows updated: " + String.valueOf(syncResult.stats.numUpdates));
michael@8: Log.i(TAG, "Rows deleted: " + String.valueOf(syncResult.stats.numDeletes));
michael@8: Log.i(TAG, "Rows skipped: " + String.valueOf(syncResult.stats.numSkippedEntries));
michael@8: Log.i(TAG, "Io Exceptions: " + String.valueOf(syncResult.stats.numIoExceptions));
michael@8: Log.i(TAG, "Parse Exceptions: " + String.valueOf(syncResult.stats.numParseExceptions));
michael@8: Log.i(TAG, "Auth Exceptions: " + String.valueOf(syncResult.stats.numAuthExceptions));
michael@8: Log.i(TAG, "Conflict Detected Exceptions: " + String.valueOf(syncResult.stats.numConflictDetectedExceptions));
michael@0:
michael@0: /*} catch (final AuthenticatorException e) {
michael@0: syncResult.stats.numParseExceptions++;
michael@0: Log.e(TAG, "AuthenticatorException", e);*/
michael@0: /*} catch (final OperationCanceledException e) {
michael@0: Log.e(TAG, "OperationCanceledExcetpion", e);*/
michael@0: } catch (final IOException e) {
michael@0: Log.e(TAG, "IOException", e);
michael@0: syncResult.stats.numIoExceptions++;
michael@0: NotificationsHelper.signalSyncErrors(this.getContext(), "Caldav sync error (IO)", e.getMessage());
michael@0: //NotificationsHelper.getCurrentSyncLog().addException(e);
michael@0: /*} catch (final AuthenticationException e) {
michael@0: //mAccountManager.invalidateAuthToken(Constants.ACCOUNT_TYPE, authtoken);
michael@0: syncResult.stats.numAuthExceptions++;
michael@0: Log.e(TAG, "AuthenticationException", e);*/
michael@0: } catch (final ParseException e) {
michael@0: syncResult.stats.numParseExceptions++;
michael@0: Log.e(TAG, "ParseException", e);
michael@8: NotificationsHelper.signalSyncErrors(this.getContext(), "Caldav sync error (parsing)", e
michael@8: .getMessage());
michael@0: //NotificationsHelper.getCurrentSyncLog().addException(e);
michael@0: /*} catch (final JSONException e) {
michael@0: syncResult.stats.numParseExceptions++;
michael@0: Log.e(TAG, "JSONException", e);*/
michael@8: } catch (Exception e) {
michael@8: Log.e(TAG, "Updating calendar exception " + e.getClass().getName(), e);
michael@0: syncResult.stats.numParseExceptions++;
michael@8: NotificationsHelper.signalSyncErrors(this.getContext(), "Caldav sync error (general)", e
michael@8: .getMessage());
michael@0: //NotificationsHelper.getCurrentSyncLog().addException(e);
michael@8: //throw new RuntimeException(e);
michael@8: }
michael@8: }
michael@0:
michael@8: public void onSyncCanceled() {
michael@8: //TODO: implement SyncCanceled
michael@8: this.mCountSyncCanceled += 1;
michael@8: Log.v(TAG, "onSyncCanceled() count:" + String.valueOf(this.mCountSyncCanceled));
michael@8: }
michael@0:
michael@8:
michael@8: /**
michael@8: * both calender event and android event have been found.
michael@8: * server wins always at the moment.
michael@8: *
michael@8: * @param androidCalendar
michael@8: * @param serverCalendar
michael@8: * @param stats
michael@8: * @param notifyList
michael@8: * @throws ClientProtocolException
michael@8: * @throws URISyntaxException
michael@8: * @throws IOException
michael@8: * @throws ParserConfigurationException
michael@8: * @throws SAXException
michael@8: * @throws RemoteException
michael@8: * @throws CaldavProtocolException
michael@8: * @throws ParserException
michael@8: * @see SyncAdapter#updateAndroidEvent(ContentProviderClient, Account, AndroidEvent, CalendarEvent)
michael@8: * @see SyncAdapter#tagAndroidEvent(ContentProviderClient, Account, AndroidEvent)
michael@8: * @see SyncAdapter#untagAndroidEvents(ContentProviderClient, Account, Uri)
michael@8: * @see SyncAdapter#deleteUntaggedEvents(ContentProviderClient, Account, Uri)
michael@8: */
michael@8: private void synchroniseEvents(
michael@8: DavCalendar androidCalendar,
michael@8: DavCalendar serverCalendar,
michael@8: SyncStats stats,
michael@8: ArrayList notifyList
michael@8: ) throws ClientProtocolException, URISyntaxException, IOException, ParserConfigurationException, SAXException, RemoteException, CaldavProtocolException, ParserException {
michael@0:
michael@0: /*if (DROP_CALENDAR_EVENTS) {
michael@0: dropAllEvents(account, provider, androidCalendar.getAndroidCalendarUri());
michael@0: }*/
michael@8:
michael@8: int rowInsert = 0;
michael@8: int rowUpdate = 0;
michael@8: int rowTag = 0;
michael@8: int rowDelete = 0;
michael@8: int rowUntag = 0;
michael@8: int rowSkip = 0;
michael@8:
michael@8: for (CalendarEvent calendarEvent : serverCalendar.getCalendarEvents()) {
michael@8: try {
michael@8: AndroidEvent androidEvent = calendarEvent.getAndroidEvent(androidCalendar);
michael@8:
michael@8: Log.i(TAG, "Event " + calendarEvent.getUri()
michael@8: .toString() + " androidUri=" + androidEvent);
michael@8:
michael@8: if (androidEvent == null) {
michael@0: /* new android event */
michael@8: if (calendarEvent.createAndroidEvent(androidCalendar)) {
michael@8: rowInsert += 1;
michael@8: androidEvent = calendarEvent.getAndroidEvent(androidCalendar);
michael@8: notifyList.add(androidEvent.getUri());
michael@8: } else {
michael@8: rowSkip += 1;
michael@8: }
michael@8: } else {
michael@0: /* the android exists */
michael@8: String androidETag = androidEvent.getETag();
michael@8: if (androidETag == null)
michael@8: androidETag = "";
michael@8: Log.d(TAG, "Event compare: " + androidETag + " <> " + calendarEvent.getETag()
michael@8: .toString());
michael@8: if ((androidEvent.getETag() == null) || (!androidETag.equals(calendarEvent.getETag()))) {
michael@0: /* the android event is getting updated */
michael@8: if (calendarEvent.updateAndroidEvent(androidEvent)) {
michael@8: rowUpdate += 1;
michael@8: notifyList.add(androidEvent.getUri());
michael@8: } else {
michael@8: rowSkip += 1;
michael@8: }
michael@8: }
michael@8: }
michael@8: if (androidEvent != null)
michael@8: //if (androidEvent.tagAndroidEvent())
michael@8: if (androidCalendar.tagAndroidEvent(androidEvent))
michael@8: rowTag += 1;
michael@0:
michael@0:
michael@8: } catch (ParserException ex) {
michael@8: Log.e(TAG, "Parser exception", ex);
michael@8: stats.numParseExceptions++;
michael@8:
michael@8: NotificationsHelper.signalSyncErrors(getContext(), "Caldav sync error (parsing)", ex
michael@8: .getMessage());
michael@8: //NotificationsHelper.getCurrentSyncLog().addException(ex);
michael@8: } catch (CaldavProtocolException ex) {
michael@8: Log.e(TAG, "Caldav exception", ex);
michael@8: stats.numParseExceptions++;
michael@8:
michael@8: NotificationsHelper.signalSyncErrors(getContext(), "Caldav sync error (caldav)", ex.getMessage());
michael@8: //NotificationsHelper.getCurrentSyncLog().addException(ex);
michael@8: }
michael@8: }
michael@8:
michael@8: rowDelete = androidCalendar.deleteUntaggedEvents();
michael@8: rowUntag = androidCalendar.untagAndroidEvents();
michael@0:
michael@0: /*Log.i(TAG,"Statistiks for Calendar: " + serverCalendar.getURI().toString());
michael@0: Log.i(TAG,"Statistiks for AndroidCalendar: " + androidCalendar.getAndroidCalendarUri().toString());
michael@0: Log.i(TAG,"Rows inserted: " + String.valueOf(rowInsert));
michael@0: Log.i(TAG,"Rows updated: " + String.valueOf(rowUpdate));
michael@0: Log.i(TAG,"Rows deleted: " + String.valueOf(rowDelete));
michael@0: Log.i(TAG,"Rows skipped: " + String.valueOf(rowSkip));*/
michael@8: Log.i(TAG, "Rows tagged: " + String.valueOf(rowTag));
michael@8: Log.i(TAG, "Rows untagged: " + String.valueOf(rowUntag));
michael@0:
michael@8: stats.numInserts += rowInsert;
michael@8: stats.numUpdates += rowUpdate;
michael@8: stats.numDeletes += rowDelete;
michael@8: stats.numSkippedEntries += rowSkip;
michael@8: stats.numEntries += rowInsert + rowUpdate + rowDelete;
michael@0:
michael@8: }
michael@0:
michael@8: /**
michael@8: * checks the android events for the dirty flag.
michael@8: * the flag is set by android when the event has been changed.
michael@8: * the dirty flag is removed when an android event has been updated from calendar event
michael@8: *
michael@8: * @param provider
michael@8: * @param account
michael@8: * @param calendarUri
michael@8: * @param facade
michael@8: * @param caldavCalendarUri
michael@8: * @param stats
michael@8: * @param notifyList
michael@8: * @return count of dirty events
michael@8: */
michael@8: private int checkDirtyAndroidEvents(
michael@8: ContentProviderClient provider,
michael@8: Account account,
michael@8: Uri calendarUri,
michael@8: CaldavFacade facade,
michael@8: URI caldavCalendarUri,
michael@8: SyncStats stats,
michael@8: ArrayList notifyList
michael@8: ) {
michael@8: Cursor curEvent = null;
michael@8: Cursor curAttendee = null;
michael@8: Cursor curReminder = null;
michael@8: Long EventID;
michael@8: Long CalendarID;
michael@8: AndroidEvent androidEvent = null;
michael@8: int rowDirty = 0;
michael@8: int rowInsert = 0;
michael@8: int rowUpdate = 0;
michael@8: int rowDelete = 0;
michael@0:
michael@8: try {
michael@8: CalendarID = ContentUris.parseId(calendarUri);
michael@8: String selection = "(" + Events.DIRTY + " = ?) AND (" + Events.CALENDAR_ID + " = ?)";
michael@8: String[] selectionArgs = new String[]{"1", CalendarID.toString()};
michael@8: curEvent = provider.query(Events.CONTENT_URI, null, selection, selectionArgs, null);
michael@0:
michael@8: while (curEvent.moveToNext()) {
michael@8: EventID = curEvent.getLong(curEvent.getColumnIndex(Events._ID));
michael@8: Uri returnedUri = ContentUris.withAppendedId(Events.CONTENT_URI, EventID);
michael@8:
michael@8: //androidEvent = new AndroidEvent(account, provider, returnedUri, calendarUri);
michael@8: androidEvent = new AndroidEvent(returnedUri, calendarUri);
michael@8: androidEvent.readContentValues(curEvent);
michael@8:
michael@8: selection = "(" + Attendees.EVENT_ID + " = ?)";
michael@8: selectionArgs = new String[]{String.valueOf(EventID)};
michael@8: curAttendee = provider.query(Attendees.CONTENT_URI, null, selection, selectionArgs, null);
michael@8: selection = "(" + Reminders.EVENT_ID + " = ?)";
michael@8: selectionArgs = new String[]{String.valueOf(EventID)};
michael@8: curReminder = provider.query(Reminders.CONTENT_URI, null, selection, selectionArgs, null);
michael@8: androidEvent.readAttendees(curAttendee);
michael@8: androidEvent.readReminder(curReminder);
michael@8: curAttendee.close();
michael@8: curReminder.close();
michael@8:
michael@8: String SyncID = androidEvent.ContentValues.getAsString(Events._SYNC_ID);
michael@8:
michael@8: boolean Deleted = false;
michael@8: int intDeleted = 0;
michael@8: intDeleted = curEvent.getInt(curEvent.getColumnIndex(Events.DELETED));
michael@8: Deleted = (intDeleted == 1);
michael@8:
michael@8: if (SyncID == null) {
michael@8: // new Android event
michael@8: String newGUID = java.util.UUID.randomUUID().toString() + "-caldavsyncadapter";
michael@8: String calendarPath = caldavCalendarUri.getPath();
michael@8: if (!calendarPath.endsWith("/"))
michael@8: calendarPath += "/";
michael@8:
michael@8: SyncID = calendarPath + newGUID + ".ics";
michael@8:
michael@8: androidEvent.createIcs(newGUID);
michael@8:
michael@8: if (facade.createEvent(URI.create(SyncID), androidEvent.getIcsEvent()
michael@8: .toString())) {
michael@8: //HINT: bugfix for google calendar replace("@", "%40")
michael@8: if (SyncID.contains("@"))
michael@8: SyncID = SyncID.replace("@", "%40");
michael@8: ContentValues values = new ContentValues();
michael@8: values.put(Events._SYNC_ID, SyncID);
michael@8:
michael@8: //google doesn't send the etag after creation
michael@8: //HINT: my SabreDAV send always the same etag after putting a new event
michael@8: //String LastETag = facade.getLastETag();
michael@8: //if (!LastETag.equals("")) {
michael@8: // values.put(Event.ETAG, LastETag);
michael@8: //} else {
michael@8: //so get the etag with a new REPORT
michael@8: CalendarEvent calendarEvent = new CalendarEvent(account, provider);
michael@8: calendarEvent.calendarURL = caldavCalendarUri.toURL();
michael@8: URI SyncURI = new URI(SyncID);
michael@8: calendarEvent.setUri(SyncURI);
michael@8: CaldavFacade.getEvent(calendarEvent);
michael@8: values.put(Event.ETAG, calendarEvent.getETag());
michael@8: //}
michael@8: values.put(Event.UID, newGUID);
michael@8: values.put(Events.DIRTY, 0);
michael@8: values.put(Event.RAWDATA, androidEvent.getIcsEvent().toString());
michael@8:
michael@8: int rowCount = provider.update(asSyncAdapter(androidEvent.getUri(), account.name, account.type), values, null, null);
michael@8: if (rowCount == 1) {
michael@8: rowInsert += 1;
michael@8: notifyList.add(androidEvent.getUri());
michael@8: }
michael@8: }
michael@8: } else if (Deleted) {
michael@8: // deleted Android event
michael@8: if (facade.deleteEvent(URI.create(SyncID), androidEvent.getETag())) {
michael@8: String mSelectionClause = "(" + Events._ID + "= ?)";
michael@8: String[] mSelectionArgs = {String.valueOf(EventID)};
michael@8:
michael@8: int countDeleted = provider.delete(asSyncAdapter(Events.CONTENT_URI, account.name, account.type), mSelectionClause, mSelectionArgs);
michael@8:
michael@8: if (countDeleted == 1) {
michael@8: rowDelete += 1;
michael@8: notifyList.add(androidEvent.getUri());
michael@8: }
michael@8: }
michael@8: } else {
michael@8: //update the android event to the server
michael@8: String uid = androidEvent.getUID();
michael@8: if ((uid == null) || (uid.equals(""))) {
michael@8: //COMPAT: this is needed because in the past, the UID was not stored in the android event
michael@8: CalendarEvent calendarEvent = new CalendarEvent(account, provider);
michael@8: URI syncURI = new URI(SyncID);
michael@8: calendarEvent.setUri(syncURI);
michael@8: calendarEvent.calendarURL = caldavCalendarUri.toURL();
michael@8: if (calendarEvent.fetchBody()) {
michael@8: calendarEvent.readContentValues();
michael@8: uid = calendarEvent.getUID();
michael@8: }
michael@8: }
michael@8: if (uid != null) {
michael@8: androidEvent.createIcs(uid);
michael@8:
michael@8: if (facade.updateEvent(URI.create(SyncID), androidEvent.getIcsEvent()
michael@8: .toString(), androidEvent.getETag())) {
michael@8: selection = "(" + Events._ID + "= ?)";
michael@8: selectionArgs = new String[]{EventID.toString()};
michael@8: androidEvent.ContentValues.put(Events.DIRTY, 0);
michael@8:
michael@8: //google doesn't send the etag after update
michael@8: String LastETag = facade.getLastETag();
michael@8: if (!LastETag.equals("")) {
michael@8: androidEvent.ContentValues.put(Event.ETAG, LastETag);
michael@8: } else {
michael@8: //so get the etag with a new REPORT
michael@8: CalendarEvent calendarEvent = new CalendarEvent(account, provider);
michael@8: calendarEvent.calendarURL = caldavCalendarUri.toURL();
michael@8: URI SyncURI = new URI(SyncID);
michael@8: calendarEvent.setUri(SyncURI);
michael@8: CaldavFacade.getEvent(calendarEvent);
michael@8: androidEvent.ContentValues.put(Event.ETAG, calendarEvent.getETag());
michael@8: }
michael@8: androidEvent.ContentValues.put(Event.RAWDATA, androidEvent.getIcsEvent()
michael@8: .toString());
michael@8: int RowCount = provider.update(asSyncAdapter(androidEvent.getUri(), account.name, account.type), androidEvent.ContentValues, null, null);
michael@8:
michael@8: if (RowCount == 1) {
michael@8: rowUpdate += 1;
michael@8: notifyList.add(androidEvent.getUri());
michael@8: }
michael@8: } else {
michael@8: rowDirty += 1;
michael@8: }
michael@8: } else {
michael@8: rowDirty += 1;
michael@8: }
michael@8: }
michael@8: }
michael@8: curEvent.close();
michael@0:
michael@0: /*if ((rowInsert > 0) || (rowUpdate > 0) || (rowDelete > 0) || (rowDirty > 0)) {
michael@0: Log.i(TAG,"Android Rows inserted: " + String.valueOf(rowInsert));
michael@0: Log.i(TAG,"Android Rows updated: " + String.valueOf(rowUpdate));
michael@0: Log.i(TAG,"Android Rows deleted: " + String.valueOf(rowDelete));
michael@0: Log.i(TAG,"Android Rows dirty: " + String.valueOf(rowDirty));
michael@0: }*/
michael@8:
michael@8: stats.numInserts += rowInsert;
michael@8: stats.numUpdates += rowUpdate;
michael@8: stats.numDeletes += rowDelete;
michael@8: stats.numSkippedEntries += rowDirty;
michael@8: stats.numEntries += rowInsert + rowUpdate + rowDelete;
michael@8: } catch (RemoteException e) {
michael@8: e.printStackTrace();
michael@8: } catch (URISyntaxException e) {
michael@8: // TODO Automatisch generierter Erfassungsblock
michael@8: e.printStackTrace();
michael@8: } catch (ClientProtocolException e) {
michael@8: // TODO Automatisch generierter Erfassungsblock
michael@8: e.printStackTrace();
michael@8: } catch (IOException e) {
michael@8: // TODO Automatisch generierter Erfassungsblock
michael@8: e.printStackTrace();
michael@8: } catch (CaldavProtocolException e) {
michael@8: // TODO Automatisch generierter Erfassungsblock
michael@8: e.printStackTrace();
michael@8: } catch (ParserException e) {
michael@8: // TODO Automatisch generierter Erfassungsblock
michael@8: e.printStackTrace();
michael@8: }
michael@8:
michael@8: return rowDirty;
michael@8: }
michael@0:
michael@0: /* private Account UpgradeAccount(Account OldAccount) {
michael@0: String Username = OldAccount.name;
michael@0: String Type = OldAccount.type;
michael@0: String Password = this.mAccountManager.getPassword(OldAccount);
michael@0: String Url = this.mAccountManager.getUserData(OldAccount, AuthenticatorActivity.USER_DATA_URL_KEY);
michael@0:
michael@0: Account NewAccount = new Account(Username + AuthenticatorActivity.ACCOUNT_NAME_SPLITTER + Url, Type);
michael@0: if (this.mAccountManager.addAccountExplicitly(NewAccount, Password, null)) {
michael@0: this.mAccountManager.setUserData(NewAccount, AuthenticatorActivity.USER_DATA_URL_KEY, Url);
michael@0: this.mAccountManager.setUserData(NewAccount, AuthenticatorActivity.USER_DATA_USERNAME, Username);
michael@0: }
michael@0: this.mAccountManager.removeAccount(OldAccount, null, null);
michael@0:
michael@0: return NewAccount;
michael@0: }*/
michael@0:
michael@0: /* private void dropAllEvents(Account account, ContentProviderClient provider, Uri calendarUri) throws RemoteException {
michael@0:
michael@0: Log.i(TAG, "Deleting all calendar events for "+calendarUri);
michael@0:
michael@0: String selection = "(" + Events.CALENDAR_ID + " = ?)";
michael@0: String[] selectionArgs = new String[] {Long.toString(ContentUris.parseId(calendarUri))};
michael@0:
michael@0: provider.delete(asSyncAdapter(Events.CONTENT_URI, account.name, account.type),
michael@0: selection, selectionArgs);
michael@0:
michael@0: }*/
michael@8:
michael@8: private static Uri asSyncAdapter(Uri uri, String account, String accountType) {
michael@8: return uri.buildUpon()
michael@8: .appendQueryParameter(android.provider.CalendarContract.CALLER_IS_SYNCADAPTER, "true")
michael@8: .appendQueryParameter(Calendars.ACCOUNT_NAME, account)
michael@8: .appendQueryParameter(Calendars.ACCOUNT_TYPE, accountType).build();
michael@8: }
michael@0:
michael@0: }
michael@0: