michael@0: /** michael@0: * Copyright (c) 2012-2013, Gerald Garcia, David Wiesner, Timo Berger michael@0: * 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@0: * michael@0: */ michael@0: michael@0: package org.gege.caldavsyncadapter.syncadapter; michael@0: michael@0: import java.io.IOException; michael@0: import java.net.URI; michael@0: //import java.net.MalformedURLException; michael@0: import java.net.URISyntaxException; michael@0: import java.util.ArrayList; michael@0: //import java.security.GeneralSecurityException; michael@0: michael@0: import javax.xml.parsers.ParserConfigurationException; michael@0: michael@0: import net.fortuna.ical4j.data.ParserException; michael@0: michael@0: import org.apache.http.ParseException; michael@0: import org.apache.http.client.ClientProtocolException; michael@0: import org.gege.caldavsyncadapter.Event; michael@0: import org.gege.caldavsyncadapter.android.entities.AndroidEvent; michael@0: import org.gege.caldavsyncadapter.authenticator.AuthenticatorActivity; michael@0: import org.gege.caldavsyncadapter.caldav.CaldavFacade; michael@0: import org.gege.caldavsyncadapter.caldav.CaldavProtocolException; michael@0: import org.gege.caldavsyncadapter.caldav.entities.DavCalendar; michael@0: import org.gege.caldavsyncadapter.caldav.entities.CalendarEvent; michael@0: import org.gege.caldavsyncadapter.caldav.entities.CalendarList; michael@0: import org.gege.caldavsyncadapter.caldav.entities.DavCalendar.CalendarSource; michael@0: import org.gege.caldavsyncadapter.syncadapter.notifications.NotificationsHelper; michael@0: import org.xml.sax.SAXException; 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@0: public class SyncAdapter extends AbstractThreadedSyncAdapter { michael@0: michael@0: private static final String TAG = "SyncAdapter"; michael@0: private AccountManager mAccountManager; michael@0: private String mVersion = ""; michael@0: private int mCountPerformSync = 0; michael@0: private int mCountSyncCanceled = 0; michael@0: private int mCountProviderFailed = 0; michael@0: michael@0: private int mCountProviderFailedMax = 3; michael@0: // private Context mContext; michael@0: 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@0: michael@0: // ignore same CTag michael@0: //private static final boolean FORCE_SYNCHRONIZE = false; michael@0: // drop all calendar before synchro michael@0: //private static final boolean DROP_CALENDAR_EVENTS = false; michael@0: michael@0: public SyncAdapter(Context context, boolean autoInitialize) { michael@0: super(context, autoInitialize); michael@0: //android.os.Debug.waitForDebugger(); michael@0: mAccountManager = AccountManager.get(context); michael@0: try { michael@0: mVersion = context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionName; michael@0: } catch (NameNotFoundException e) { michael@0: e.printStackTrace(); michael@0: } michael@0: // mContext = context; michael@0: } michael@0: michael@0: @Override michael@0: public void onPerformSync(Account account, Bundle extras, String authority, michael@0: ContentProviderClient provider, SyncResult syncResult) { michael@0: boolean bolError = false; michael@0: michael@0: String url = mAccountManager.getUserData(account, AuthenticatorActivity.USER_DATA_URL_KEY); michael@0: this.mCountPerformSync += 1; michael@0: Log.v(TAG, "onPerformSync() count:" + String.valueOf(this.mCountPerformSync) + " on " + account.name + " with URL " + url); michael@0: michael@0: CalendarList serverCalList; michael@0: michael@0: CalendarList androidCalList = new CalendarList(account, provider, CalendarSource.Android, url); michael@0: androidCalList.readCalendarFromClient(); michael@0: ArrayList notifyList = new ArrayList(); michael@0: michael@0: try { michael@0: String Username = ""; michael@0: String UserDataVersion = mAccountManager.getUserData(account, AuthenticatorActivity.USER_DATA_VERSION); michael@0: if (UserDataVersion == null) { michael@0: Username = account.name; michael@0: } else { michael@0: Username = mAccountManager.getUserData(account, AuthenticatorActivity.USER_DATA_USERNAME); michael@0: } michael@0: michael@0: CaldavFacade facade = new CaldavFacade(Username, mAccountManager.getPassword(account), url); michael@0: facade.setAccount(account); michael@0: facade.setProvider(provider); michael@0: facade.setVersion(mVersion); michael@0: serverCalList = facade.getCalendarList(this.getContext()); michael@0: //String davProperties = facade.getLastDav(); michael@0: Log.i(TAG, String.valueOf(androidCalList.getCalendarList().size()) + " calendars found at android"); michael@0: michael@0: for (DavCalendar serverCalendar : serverCalList.getCalendarList()) { michael@0: Log.i(TAG, "Detected calendar name=" + serverCalendar.getCalendarDisplayName() + " URI=" + serverCalendar.getURI()); michael@0: michael@0: Uri androidCalendarUri = serverCalendar.checkAndroidCalendarList(androidCalList, this.getContext()); michael@0: michael@0: // check if the adapter was able to get an existing calendar or create a new one michael@0: if (androidCalendarUri != null) { michael@0: // the provider seems to work correct, reset the counter michael@0: mCountProviderFailed = 0; michael@0: DavCalendar androidCalendar = androidCalList.getCalendarByAndroidUri(androidCalendarUri); michael@0: michael@0: //if ((FORCE_SYNCHRONIZE) || (androidCalendar.getcTag() == null) || (!androidCalendar.getcTag().equals(serverCalendar.getcTag()))) { michael@0: if ((androidCalendar.getcTag() == null) || (!androidCalendar.getcTag().equals(serverCalendar.getcTag()))) { michael@0: Log.d(TAG, "CTag has changed, something to synchronise"); michael@0: if (serverCalendar.readCalendarEvents(facade)) { michael@0: this.synchroniseEvents(androidCalendar, serverCalendar, syncResult.stats, notifyList); michael@0: michael@0: Log.d(TAG, "Updating stored CTag"); michael@0: //serverCalendar.updateAndroidCalendar(androidCalendarUri, Calendar.CTAG, serverCalendar.getcTag()); michael@0: androidCalendar.setCTag(serverCalendar.getcTag(), true); michael@0: } else { michael@0: Log.d(TAG, "unable to read events from server calendar"); michael@0: } michael@0: } else { michael@0: 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@0: michael@0: } michael@0: michael@0: this.checkDirtyAndroidEvents(provider, account, androidCalendarUri, facade, serverCalendar.getURI(), syncResult.stats, notifyList); michael@0: } else { michael@0: // this happens if the data provider failes to get an existing or create a new calendar michael@0: mCountProviderFailed += 1; michael@0: Log.e(TAG, "failed to get an existing or create a new calendar"); michael@0: syncResult.stats.numIoExceptions += 1; michael@0: if (mCountProviderFailed >= mCountProviderFailedMax) { michael@0: // see issue #96 michael@0: NotificationsHelper.signalSyncErrors(this.getContext(), "Caldav sync error (provider failed)", "are you using CyanogenMod in Incognito Mode?"); michael@0: } else { michael@0: NotificationsHelper.signalSyncErrors(this.getContext(), "Caldav sync error (provider failed)", "the provider failed to get an existing or create a new calendar"); michael@0: } michael@0: bolError = true; michael@0: } michael@0: } michael@0: michael@0: if (!bolError) { michael@0: // check whether a calendar is not synced -> delete it at android michael@0: androidCalList.deleteCalendarOnClientSideOnly(this.getContext()); michael@0: } michael@0: michael@0: // notify the ContentResolver michael@0: for (Uri uri : androidCalList.getNotifyList()) { michael@0: this.getContext().getContentResolver().notifyChange(uri, null); michael@0: } michael@0: for (Uri uri : serverCalList.getNotifyList()) { michael@0: this.getContext().getContentResolver().notifyChange(uri, null); michael@0: } michael@0: for (Uri uri : notifyList) { michael@0: this.getContext().getContentResolver().notifyChange(uri, null); michael@0: } 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,"Entries: " + String.valueOf(syncResult.stats.numEntries)); michael@0: Log.i(TAG,"Rows inserted: " + String.valueOf(syncResult.stats.numInserts)); michael@0: Log.i(TAG,"Rows updated: " + String.valueOf(syncResult.stats.numUpdates)); michael@0: Log.i(TAG,"Rows deleted: " + String.valueOf(syncResult.stats.numDeletes)); michael@0: Log.i(TAG,"Rows skipped: " + String.valueOf(syncResult.stats.numSkippedEntries)); michael@0: Log.i(TAG,"Io Exceptions: " + String.valueOf(syncResult.stats.numIoExceptions)); michael@0: Log.i(TAG,"Parse Exceptions: " + String.valueOf(syncResult.stats.numParseExceptions)); michael@0: Log.i(TAG,"Auth Exceptions: " + String.valueOf(syncResult.stats.numAuthExceptions)); michael@0: 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@0: NotificationsHelper.signalSyncErrors(this.getContext(), "Caldav sync error (parsing)", e.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@0: } catch (Exception e) { michael@0: Log.e(TAG, "Updating calendar exception " + e.getClass().getName(), e); michael@0: syncResult.stats.numParseExceptions++; michael@0: NotificationsHelper.signalSyncErrors(this.getContext(), "Caldav sync error (general)", e.getMessage()); michael@0: //NotificationsHelper.getCurrentSyncLog().addException(e); michael@0: //throw new RuntimeException(e); michael@0: } michael@0: } michael@0: michael@0: public void onSyncCanceled () { michael@0: //TODO: implement SyncCanceled michael@0: this.mCountSyncCanceled += 1; michael@0: Log.v(TAG, "onSyncCanceled() count:" + String.valueOf(this.mCountSyncCanceled)); michael@0: } michael@0: michael@0: michael@0: /** michael@0: * both calender event and android event have been found. michael@0: * server wins always at the moment. michael@0: * @param androidCalendar michael@0: * @param serverCalendar michael@0: * @param stats michael@0: * @param notifyList michael@0: * @throws ClientProtocolException michael@0: * @throws URISyntaxException michael@0: * @throws IOException michael@0: * @throws ParserConfigurationException michael@0: * @throws SAXException michael@0: * @throws RemoteException michael@0: * @throws CaldavProtocolException michael@0: * @throws ParserException michael@0: * @see SyncAdapter#updateAndroidEvent(ContentProviderClient, Account, AndroidEvent, CalendarEvent) michael@0: * @see SyncAdapter#tagAndroidEvent(ContentProviderClient, Account, AndroidEvent) michael@0: * @see SyncAdapter#untagAndroidEvents(ContentProviderClient, Account, Uri) michael@0: * @see SyncAdapter#deleteUntaggedEvents(ContentProviderClient, Account, Uri) michael@0: */ michael@0: private void synchroniseEvents( michael@0: DavCalendar androidCalendar, michael@0: DavCalendar serverCalendar, michael@0: SyncStats stats, michael@0: ArrayList notifyList michael@0: ) 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@0: michael@0: int rowInsert = 0; michael@0: int rowUpdate = 0; michael@0: int rowTag = 0; michael@0: int rowDelete = 0; michael@0: int rowUntag = 0; michael@0: int rowSkip = 0; michael@0: michael@0: for (CalendarEvent calendarEvent : serverCalendar.getCalendarEvents()) { michael@0: try { michael@0: AndroidEvent androidEvent = calendarEvent.getAndroidEvent(androidCalendar); michael@0: michael@0: Log.i(TAG, "Event " + calendarEvent.getUri().toString()+ " androidUri="+androidEvent); michael@0: michael@0: if (androidEvent == null) { michael@0: /* new android event */ michael@0: if (calendarEvent.createAndroidEvent(androidCalendar)) { michael@0: rowInsert += 1; michael@0: androidEvent = calendarEvent.getAndroidEvent(androidCalendar); michael@0: notifyList.add(androidEvent.getUri()); michael@0: } else { michael@0: rowSkip += 1; michael@0: } michael@0: } else { michael@0: /* the android exists */ michael@0: String androidETag = androidEvent.getETag(); michael@0: if (androidETag == null) michael@0: androidETag = ""; michael@0: Log.d(TAG, "Event compare: " + androidETag + " <> " + calendarEvent.getETag().toString()); michael@0: if ((androidEvent.getETag() == null) || (!androidETag.equals(calendarEvent.getETag()))) { michael@0: /* the android event is getting updated */ michael@0: if (calendarEvent.updateAndroidEvent(androidEvent)) { michael@0: rowUpdate += 1; michael@0: notifyList.add(androidEvent.getUri()); michael@0: } else { michael@0: rowSkip += 1; michael@0: } michael@0: } michael@0: } michael@0: if (androidEvent != null) michael@0: //if (androidEvent.tagAndroidEvent()) michael@0: if (androidCalendar.tagAndroidEvent(androidEvent)) michael@0: rowTag += 1; michael@0: michael@0: michael@0: } catch (ParserException ex) { michael@0: Log.e(TAG, "Parser exception", ex); michael@0: stats.numParseExceptions++; michael@0: michael@0: NotificationsHelper.signalSyncErrors(getContext(), "Caldav sync error (parsing)", ex.getMessage()); michael@0: //NotificationsHelper.getCurrentSyncLog().addException(ex); michael@0: } catch (CaldavProtocolException ex) { michael@0: Log.e(TAG, "Caldav exception", ex); michael@0: stats.numParseExceptions++; michael@0: michael@0: NotificationsHelper.signalSyncErrors(getContext(), "Caldav sync error (caldav)", ex.getMessage()); michael@0: //NotificationsHelper.getCurrentSyncLog().addException(ex); michael@0: } michael@0: } michael@0: michael@0: rowDelete = androidCalendar.deleteUntaggedEvents(); michael@0: 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@0: Log.i(TAG,"Rows tagged: " + String.valueOf(rowTag)); michael@0: Log.i(TAG,"Rows untagged: " + String.valueOf(rowUntag)); michael@0: michael@0: stats.numInserts += rowInsert; michael@0: stats.numUpdates += rowUpdate; michael@0: stats.numDeletes += rowDelete; michael@0: stats.numSkippedEntries += rowSkip; michael@0: stats.numEntries += rowInsert + rowUpdate + rowDelete; michael@0: michael@0: } michael@0: michael@0: /** michael@0: * checks the android events for the dirty flag. michael@0: * the flag is set by android when the event has been changed. michael@0: * the dirty flag is removed when an android event has been updated from calendar event michael@0: * @param provider michael@0: * @param account michael@0: * @param calendarUri michael@0: * @param facade michael@0: * @param caldavCalendarUri michael@0: * @param stats michael@0: * @param notifyList michael@0: * @return count of dirty events michael@0: */ michael@0: private int checkDirtyAndroidEvents( michael@0: ContentProviderClient provider, michael@0: Account account, michael@0: Uri calendarUri, michael@0: CaldavFacade facade, michael@0: URI caldavCalendarUri, michael@0: SyncStats stats, michael@0: ArrayList notifyList michael@0: ) { michael@0: Cursor curEvent = null; michael@0: Cursor curAttendee = null; michael@0: Cursor curReminder = null; michael@0: Long EventID; michael@0: Long CalendarID; michael@0: AndroidEvent androidEvent = null; michael@0: int rowDirty = 0; michael@0: int rowInsert = 0; michael@0: int rowUpdate = 0; michael@0: int rowDelete = 0; michael@0: michael@0: try { michael@0: CalendarID = ContentUris.parseId(calendarUri); michael@0: String selection = "(" + Events.DIRTY + " = ?) AND (" + Events.CALENDAR_ID + " = ?)"; michael@0: String[] selectionArgs = new String[] {"1", CalendarID.toString()}; michael@0: curEvent = provider.query(Events.CONTENT_URI, null, selection, selectionArgs, null); michael@0: michael@0: while (curEvent.moveToNext()) { michael@0: EventID = curEvent.getLong(curEvent.getColumnIndex(Events._ID)); michael@0: Uri returnedUri = ContentUris.withAppendedId(Events.CONTENT_URI, EventID); michael@0: michael@0: //androidEvent = new AndroidEvent(account, provider, returnedUri, calendarUri); michael@0: androidEvent = new AndroidEvent(returnedUri, calendarUri); michael@0: androidEvent.readContentValues(curEvent); michael@0: michael@0: selection = "(" + Attendees.EVENT_ID + " = ?)"; michael@0: selectionArgs = new String[] {String.valueOf(EventID)}; michael@0: curAttendee = provider.query(Attendees.CONTENT_URI, null, selection, selectionArgs, null); michael@0: selection = "(" + Reminders.EVENT_ID + " = ?)"; michael@0: selectionArgs = new String[] {String.valueOf(EventID)}; michael@0: curReminder = provider.query(Reminders.CONTENT_URI, null, selection, selectionArgs, null); michael@0: androidEvent.readAttendees(curAttendee); michael@0: androidEvent.readReminder(curReminder); michael@0: curAttendee.close(); michael@0: curReminder.close(); michael@0: michael@0: String SyncID = androidEvent.ContentValues.getAsString(Events._SYNC_ID); michael@0: michael@0: boolean Deleted = false; michael@0: int intDeleted = 0; michael@0: intDeleted = curEvent.getInt(curEvent.getColumnIndex(Events.DELETED)); michael@0: Deleted = (intDeleted == 1); michael@0: michael@0: if (SyncID == null) { michael@0: // new Android event michael@0: String newGUID = java.util.UUID.randomUUID().toString() + "-caldavsyncadapter"; michael@0: String calendarPath = caldavCalendarUri.getPath(); michael@0: if (!calendarPath.endsWith("/")) michael@0: calendarPath += "/"; michael@0: michael@0: SyncID = calendarPath + newGUID + ".ics"; michael@0: michael@0: androidEvent.createIcs(newGUID); michael@0: michael@0: if (facade.createEvent(URI.create(SyncID), androidEvent.getIcsEvent().toString())) { michael@0: //HINT: bugfix for google calendar replace("@", "%40") michael@0: if (SyncID.contains("@")) michael@0: SyncID = SyncID.replace("@", "%40"); michael@0: ContentValues values = new ContentValues(); michael@0: values.put(Events._SYNC_ID, SyncID); michael@0: michael@0: //google doesn't send the etag after creation michael@0: //HINT: my SabreDAV send always the same etag after putting a new event michael@0: //String LastETag = facade.getLastETag(); michael@0: //if (!LastETag.equals("")) { michael@0: // values.put(Event.ETAG, LastETag); michael@0: //} else { michael@0: //so get the etag with a new REPORT michael@0: CalendarEvent calendarEvent = new CalendarEvent(account, provider); michael@0: calendarEvent.calendarURL = caldavCalendarUri.toURL(); michael@0: URI SyncURI = new URI(SyncID); michael@0: calendarEvent.setUri(SyncURI); michael@0: CaldavFacade.getEvent(calendarEvent); michael@0: values.put(Event.ETAG, calendarEvent.getETag()); michael@0: //} michael@0: values.put(Event.UID, newGUID); michael@0: values.put(Events.DIRTY, 0); michael@0: values.put(Event.RAWDATA, androidEvent.getIcsEvent().toString()); michael@0: michael@0: int rowCount = provider.update(asSyncAdapter(androidEvent.getUri(), account.name, account.type), values, null, null); michael@0: if (rowCount == 1) { michael@0: rowInsert += 1; michael@0: notifyList.add(androidEvent.getUri()); michael@0: } michael@0: } michael@0: } else if (Deleted) { michael@0: // deleted Android event michael@0: if (facade.deleteEvent(URI.create(SyncID), androidEvent.getETag())) { michael@0: String mSelectionClause = "(" + Events._ID + "= ?)"; michael@0: String[] mSelectionArgs = {String.valueOf(EventID)}; michael@0: michael@0: int countDeleted = provider.delete(asSyncAdapter(Events.CONTENT_URI, account.name, account.type), mSelectionClause, mSelectionArgs); michael@0: michael@0: if (countDeleted == 1) { michael@0: rowDelete += 1; michael@0: notifyList.add(androidEvent.getUri()); michael@0: } michael@0: } michael@0: } else { michael@0: //update the android event to the server michael@0: String uid = androidEvent.getUID(); michael@0: if ((uid == null) || (uid.equals(""))) { michael@0: //COMPAT: this is needed because in the past, the UID was not stored in the android event michael@0: CalendarEvent calendarEvent = new CalendarEvent(account, provider); michael@0: URI syncURI = new URI(SyncID); michael@0: calendarEvent.setUri(syncURI); michael@0: calendarEvent.calendarURL = caldavCalendarUri.toURL(); michael@0: if (calendarEvent.fetchBody()) { michael@0: calendarEvent.readContentValues(); michael@0: uid = calendarEvent.getUID(); michael@0: } michael@0: } michael@0: if (uid != null) { michael@0: androidEvent.createIcs(uid); michael@0: michael@0: if (facade.updateEvent(URI.create(SyncID), androidEvent.getIcsEvent().toString(), androidEvent.getETag())) { michael@0: selection = "(" + Events._ID + "= ?)"; michael@0: selectionArgs = new String[] {EventID.toString()}; michael@0: androidEvent.ContentValues.put(Events.DIRTY, 0); michael@0: michael@0: //google doesn't send the etag after update michael@0: String LastETag = facade.getLastETag(); michael@0: if (!LastETag.equals("")) { michael@0: androidEvent.ContentValues.put(Event.ETAG, LastETag); michael@0: } else { michael@0: //so get the etag with a new REPORT michael@0: CalendarEvent calendarEvent = new CalendarEvent(account, provider); michael@0: calendarEvent.calendarURL = caldavCalendarUri.toURL(); michael@0: URI SyncURI = new URI(SyncID); michael@0: calendarEvent.setUri(SyncURI); michael@0: CaldavFacade.getEvent(calendarEvent); michael@0: androidEvent.ContentValues.put(Event.ETAG, calendarEvent.getETag()); michael@0: } michael@0: androidEvent.ContentValues.put(Event.RAWDATA, androidEvent.getIcsEvent().toString()); michael@0: int RowCount = provider.update(asSyncAdapter(androidEvent.getUri(), account.name, account.type), androidEvent.ContentValues, null, null); michael@0: michael@0: if (RowCount == 1) { michael@0: rowUpdate += 1; michael@0: notifyList.add(androidEvent.getUri()); michael@0: } michael@0: } else { michael@0: rowDirty += 1; michael@0: } michael@0: } else { michael@0: rowDirty += 1; michael@0: } michael@0: } michael@0: } michael@0: 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@0: michael@0: stats.numInserts += rowInsert; michael@0: stats.numUpdates += rowUpdate; michael@0: stats.numDeletes += rowDelete; michael@0: stats.numSkippedEntries += rowDirty; michael@0: stats.numEntries += rowInsert + rowUpdate + rowDelete; michael@0: } catch (RemoteException e) { michael@0: e.printStackTrace(); michael@0: } catch (URISyntaxException e) { michael@0: // TODO Automatisch generierter Erfassungsblock michael@0: e.printStackTrace(); michael@0: } catch (ClientProtocolException e) { michael@0: // TODO Automatisch generierter Erfassungsblock michael@0: e.printStackTrace(); michael@0: } catch (IOException e) { michael@0: // TODO Automatisch generierter Erfassungsblock michael@0: e.printStackTrace(); michael@0: } catch (CaldavProtocolException e) { michael@0: // TODO Automatisch generierter Erfassungsblock michael@0: e.printStackTrace(); michael@0: } catch (ParserException e) { michael@0: // TODO Automatisch generierter Erfassungsblock michael@0: e.printStackTrace(); michael@0: } michael@0: michael@0: return rowDirty; michael@0: } 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@0: michael@0: private static Uri asSyncAdapter(Uri uri, String account, String accountType) { michael@0: return uri.buildUpon() michael@0: .appendQueryParameter(android.provider.CalendarContract.CALLER_IS_SYNCADAPTER,"true") michael@0: .appendQueryParameter(Calendars.ACCOUNT_NAME, account) michael@0: .appendQueryParameter(Calendars.ACCOUNT_TYPE, accountType).build(); michael@0: } michael@0: michael@0: } michael@0: