org.gege.caldavsyncadapter.syncadapter.SyncAdapter.java Source code

Java tutorial

Introduction

Here is the source code for org.gege.caldavsyncadapter.syncadapter.SyncAdapter.java

Source

/**
 * Copyright (c) 2012-2013, Gerald Garcia, David Wiesner, Timo Berger
 * 
 * This file is part of Andoid Caldav Sync Adapter Free.
 *
 * Andoid Caldav Sync Adapter Free is free software: you can redistribute 
 * it and/or modify it under the terms of the GNU General Public License 
 * as published by the Free Software Foundation, either version 3 of the 
 * License, or at your option any later version.
 *
 * Andoid Caldav Sync Adapter Free is distributed in the hope that 
 * it will be useful, but WITHOUT ANY WARRANTY; without even the implied 
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Andoid Caldav Sync Adapter Free.  
 * If not, see <http://www.gnu.org/licenses/>.
 * 
 */

package org.gege.caldavsyncadapter.syncadapter;

import java.io.IOException;
import java.net.URI;
//import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.util.ArrayList;
//import java.security.GeneralSecurityException;

import javax.xml.parsers.ParserConfigurationException;

import net.fortuna.ical4j.data.ParserException;

import org.apache.http.ParseException;
import org.apache.http.client.ClientProtocolException;
import org.gege.caldavsyncadapter.Event;
import org.gege.caldavsyncadapter.android.entities.AndroidEvent;
import org.gege.caldavsyncadapter.authenticator.AuthenticatorActivity;
import org.gege.caldavsyncadapter.caldav.CaldavFacade;
import org.gege.caldavsyncadapter.caldav.CaldavProtocolException;
import org.gege.caldavsyncadapter.caldav.entities.DavCalendar;
import org.gege.caldavsyncadapter.caldav.entities.CalendarEvent;
import org.gege.caldavsyncadapter.caldav.entities.CalendarList;
import org.gege.caldavsyncadapter.caldav.entities.DavCalendar.CalendarSource;
import org.gege.caldavsyncadapter.syncadapter.notifications.NotificationsHelper;
import org.xml.sax.SAXException;

import android.accounts.Account;
import android.accounts.AccountManager;
import android.content.AbstractThreadedSyncAdapter;
import android.content.ContentProviderClient;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.SyncResult;
import android.content.SyncStats;
import android.content.pm.PackageManager.NameNotFoundException;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.RemoteException;
import android.provider.CalendarContract.Attendees;
import android.provider.CalendarContract.Calendars;
import android.provider.CalendarContract.Events;
import android.provider.CalendarContract.Reminders;
import android.util.Log;

public class SyncAdapter extends AbstractThreadedSyncAdapter {

    private static final String TAG = "SyncAdapter";
    private AccountManager mAccountManager;
    private String mVersion = "";
    private int mCountPerformSync = 0;
    private int mCountSyncCanceled = 0;
    private int mCountProviderFailed = 0;

    private int mCountProviderFailedMax = 3;
    //   private Context mContext;

    /*   private static final String[] CALENDAR_PROJECTION = new String[] {
           Calendars._ID,                           // 0
           Calendars.ACCOUNT_NAME,                  // 1
           Calendars.CALENDAR_DISPLAY_NAME,         // 2
           Calendars.OWNER_ACCOUNT,                 // 3
           Calendar.CTAG                            // 4
       };*/

    /*   // The indices for the projection array above.
       private static final int PROJECTION_ID_INDEX = 0;
       private static final int PROJECTION_ACCOUNT_NAME_INDEX = 1;
       private static final int PROJECTION_DISPLAY_NAME_INDEX = 2;
       private static final int PROJECTION_OWNER_ACCOUNT_INDEX = 3;
    */

    /*
       private static final String[] EVENT_PROJECTION = new String[] {
          Events._ID,
          Events._SYNC_ID,
          Events.SYNC_DATA1,
          Events.CALENDAR_ID
       };
    */

    // ignore same CTag
    //private static final boolean FORCE_SYNCHRONIZE = false;
    // drop all calendar before synchro
    //private static final boolean DROP_CALENDAR_EVENTS = false;

    public SyncAdapter(Context context, boolean autoInitialize) {
        super(context, autoInitialize);
        //android.os.Debug.waitForDebugger();
        mAccountManager = AccountManager.get(context);
        try {
            mVersion = context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionName;
        } catch (NameNotFoundException e) {
            e.printStackTrace();
        }
        //      mContext = context;
    }

    @Override
    public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider,
            SyncResult syncResult) {
        boolean bolError = false;

        String url = mAccountManager.getUserData(account, AuthenticatorActivity.USER_DATA_URL_KEY);
        this.mCountPerformSync += 1;
        Log.v(TAG, "onPerformSync() count:" + String.valueOf(this.mCountPerformSync) + " on " + account.name
                + " with URL " + url);

        CalendarList serverCalList;

        CalendarList androidCalList = new CalendarList(account, provider, CalendarSource.Android, url);
        androidCalList.readCalendarFromClient();
        ArrayList<Uri> notifyList = new ArrayList<Uri>();

        try {
            String Username = "";
            String UserDataVersion = mAccountManager.getUserData(account, AuthenticatorActivity.USER_DATA_VERSION);
            if (UserDataVersion == null) {
                Username = account.name;
            } else {
                Username = mAccountManager.getUserData(account, AuthenticatorActivity.USER_DATA_USERNAME);
            }

            CaldavFacade facade = new CaldavFacade(Username, mAccountManager.getPassword(account), url);
            facade.setAccount(account);
            facade.setProvider(provider);
            facade.setVersion(mVersion);
            serverCalList = facade.getCalendarList(this.getContext());
            //String davProperties = facade.getLastDav();
            Log.i(TAG, String.valueOf(androidCalList.getCalendarList().size()) + " calendars found at android");

            for (DavCalendar serverCalendar : serverCalList.getCalendarList()) {
                Log.i(TAG, "Detected calendar name=" + serverCalendar.getCalendarDisplayName() + " URI="
                        + serverCalendar.getURI());

                Uri androidCalendarUri = serverCalendar.checkAndroidCalendarList(androidCalList, this.getContext());

                // check if the adapter was able to get an existing calendar or create a new one
                if (androidCalendarUri != null) {
                    // the provider seems to work correct, reset the counter
                    mCountProviderFailed = 0;
                    DavCalendar androidCalendar = androidCalList.getCalendarByAndroidUri(androidCalendarUri);

                    //if ((FORCE_SYNCHRONIZE) || (androidCalendar.getcTag() == null) || (!androidCalendar.getcTag().equals(serverCalendar.getcTag()))) {
                    if ((androidCalendar.getcTag() == null)
                            || (!androidCalendar.getcTag().equals(serverCalendar.getcTag()))) {
                        Log.d(TAG, "CTag has changed, something to synchronise");
                        if (serverCalendar.readCalendarEvents(facade)) {
                            this.synchroniseEvents(androidCalendar, serverCalendar, syncResult.stats, notifyList);

                            Log.d(TAG, "Updating stored CTag");
                            //serverCalendar.updateAndroidCalendar(androidCalendarUri, Calendar.CTAG, serverCalendar.getcTag());
                            androidCalendar.setCTag(serverCalendar.getcTag(), true);
                        } else {
                            Log.d(TAG, "unable to read events from server calendar");
                        }
                    } else {
                        Log.d(TAG, "CTag has not changed, nothing to do");

                        /* this is unnecessary. "SkippedEntries" are:
                         * Counter for tracking how many entries, either from the server or the local store, 
                         * were ignored during the sync operation. This could happen if the SyncAdapter detected 
                         * some unparsable data but decided to skip it and move on rather than failing immediately. 
                         */

                        /*long CalendarID = ContentUris.parseId(androidCalendarUri);
                        String selection = "(" + Events.CALENDAR_ID + " = ?)";
                        String[] selectionArgs = new String[] {String.valueOf(CalendarID)}; 
                        Cursor countCursor = provider.query(Events.CONTENT_URI, new String[] {"count(*) AS count"},
                           selection,
                           selectionArgs,
                           null);
                            
                          countCursor.moveToFirst();
                          int count = countCursor.getInt(0);
                          syncResult.stats.numSkippedEntries += count;
                          countCursor.close();*/

                    }

                    this.checkDirtyAndroidEvents(provider, account, androidCalendarUri, facade,
                            serverCalendar.getURI(), syncResult.stats, notifyList);
                } else {
                    // this happens if the data provider failes to get an existing or create a new calendar
                    mCountProviderFailed += 1;
                    Log.e(TAG, "failed to get an existing or create a new calendar");
                    syncResult.stats.numIoExceptions += 1;
                    if (mCountProviderFailed >= mCountProviderFailedMax) {
                        // see issue #96
                        NotificationsHelper.signalSyncErrors(this.getContext(),
                                "Caldav sync error (provider failed)",
                                "are you using CyanogenMod in Incognito Mode?");
                    } else {
                        NotificationsHelper.signalSyncErrors(this.getContext(),
                                "Caldav sync error (provider failed)",
                                "the provider failed to get an existing or create a new calendar");
                    }
                    bolError = true;
                }
            }

            if (!bolError) {
                // check whether a calendar is not synced -> delete it at android
                androidCalList.deleteCalendarOnClientSideOnly(this.getContext());
            }

            // notify the ContentResolver
            for (Uri uri : androidCalList.getNotifyList()) {
                this.getContext().getContentResolver().notifyChange(uri, null);
            }
            for (Uri uri : serverCalList.getNotifyList()) {
                this.getContext().getContentResolver().notifyChange(uri, null);
            }
            for (Uri uri : notifyList) {
                this.getContext().getContentResolver().notifyChange(uri, null);
            }

            //Log.i(TAG,"Statistiks for Calendar: " + serverCalendar.getURI().toString());
            //Log.i(TAG,"Statistiks for AndroidCalendar: " + androidCalendar.getAndroidCalendarUri().toString());
            Log.i(TAG, "Entries:                       " + String.valueOf(syncResult.stats.numEntries));
            Log.i(TAG, "Rows inserted:                 " + String.valueOf(syncResult.stats.numInserts));
            Log.i(TAG, "Rows updated:                  " + String.valueOf(syncResult.stats.numUpdates));
            Log.i(TAG, "Rows deleted:                  " + String.valueOf(syncResult.stats.numDeletes));
            Log.i(TAG, "Rows skipped:                  " + String.valueOf(syncResult.stats.numSkippedEntries));
            Log.i(TAG, "Io Exceptions:                 " + String.valueOf(syncResult.stats.numIoExceptions));
            Log.i(TAG, "Parse Exceptions:              " + String.valueOf(syncResult.stats.numParseExceptions));
            Log.i(TAG, "Auth Exceptions:               " + String.valueOf(syncResult.stats.numAuthExceptions));
            Log.i(TAG, "Conflict Detected Exceptions:  "
                    + String.valueOf(syncResult.stats.numConflictDetectedExceptions));

            /*} catch (final AuthenticatorException e) {
                  syncResult.stats.numParseExceptions++;
                  Log.e(TAG, "AuthenticatorException", e);*/
            /*} catch (final OperationCanceledException e) {
                Log.e(TAG, "OperationCanceledExcetpion", e);*/
        } catch (final IOException e) {
            Log.e(TAG, "IOException", e);
            syncResult.stats.numIoExceptions++;
            NotificationsHelper.signalSyncErrors(this.getContext(), "Caldav sync error (IO)", e.getMessage());
            //NotificationsHelper.getCurrentSyncLog().addException(e);
            /*} catch (final AuthenticationException e) {
            //mAccountManager.invalidateAuthToken(Constants.ACCOUNT_TYPE, authtoken);
            syncResult.stats.numAuthExceptions++;
            Log.e(TAG, "AuthenticationException", e);*/
        } catch (final ParseException e) {
            syncResult.stats.numParseExceptions++;
            Log.e(TAG, "ParseException", e);
            NotificationsHelper.signalSyncErrors(this.getContext(), "Caldav sync error (parsing)", e.getMessage());
            //NotificationsHelper.getCurrentSyncLog().addException(e);
            /*} catch (final JSONException e) {
                syncResult.stats.numParseExceptions++;
                Log.e(TAG, "JSONException", e);*/
        } catch (Exception e) {
            Log.e(TAG, "Updating calendar exception " + e.getClass().getName(), e);
            syncResult.stats.numParseExceptions++;
            NotificationsHelper.signalSyncErrors(this.getContext(), "Caldav sync error (general)", e.getMessage());
            //NotificationsHelper.getCurrentSyncLog().addException(e);
            //throw new RuntimeException(e);
        }
    }

    public void onSyncCanceled() {
        //TODO: implement SyncCanceled
        this.mCountSyncCanceled += 1;
        Log.v(TAG, "onSyncCanceled() count:" + String.valueOf(this.mCountSyncCanceled));
    }

    /**
     * both calender event and android event have been found.
     * server wins always at the moment.
     * @param androidCalendar
     * @param serverCalendar
     * @param stats
     * @param notifyList
     * @throws ClientProtocolException
     * @throws URISyntaxException
     * @throws IOException
     * @throws ParserConfigurationException
     * @throws SAXException
     * @throws RemoteException
     * @throws CaldavProtocolException
     * @throws ParserException
     * @see SyncAdapter#updateAndroidEvent(ContentProviderClient, Account, AndroidEvent, CalendarEvent)
     * @see SyncAdapter#tagAndroidEvent(ContentProviderClient, Account, AndroidEvent)
     * @see SyncAdapter#untagAndroidEvents(ContentProviderClient, Account, Uri)
     * @see SyncAdapter#deleteUntaggedEvents(ContentProviderClient, Account, Uri)
     */
    private void synchroniseEvents(DavCalendar androidCalendar, DavCalendar serverCalendar, SyncStats stats,
            ArrayList<Uri> notifyList) throws ClientProtocolException, URISyntaxException, IOException,
            ParserConfigurationException, SAXException, RemoteException, CaldavProtocolException, ParserException {

        /*if (DROP_CALENDAR_EVENTS) {
           dropAllEvents(account, provider, androidCalendar.getAndroidCalendarUri());
        }*/

        int rowInsert = 0;
        int rowUpdate = 0;
        int rowTag = 0;
        int rowDelete = 0;
        int rowUntag = 0;
        int rowSkip = 0;

        for (CalendarEvent calendarEvent : serverCalendar.getCalendarEvents()) {
            try {
                AndroidEvent androidEvent = calendarEvent.getAndroidEvent(androidCalendar);

                Log.i(TAG, "Event " + calendarEvent.getUri().toString() + " androidUri=" + androidEvent);

                if (androidEvent == null) {
                    /* new android event */
                    if (calendarEvent.createAndroidEvent(androidCalendar)) {
                        rowInsert += 1;
                        androidEvent = calendarEvent.getAndroidEvent(androidCalendar);
                        notifyList.add(androidEvent.getUri());
                    } else {
                        rowSkip += 1;
                    }
                } else {
                    /* the android exists */
                    String androidETag = androidEvent.getETag();
                    if (androidETag == null)
                        androidETag = "";
                    Log.d(TAG, "Event compare: " + androidETag + " <> " + calendarEvent.getETag().toString());
                    if ((androidEvent.getETag() == null) || (!androidETag.equals(calendarEvent.getETag()))) {
                        /* the android event is getting updated */
                        if (calendarEvent.updateAndroidEvent(androidEvent)) {
                            rowUpdate += 1;
                            notifyList.add(androidEvent.getUri());
                        } else {
                            rowSkip += 1;
                        }
                    }
                }
                if (androidEvent != null)
                    if (androidEvent.tagAndroidEvent())
                        rowTag += 1;

            } catch (ParserException ex) {
                Log.e(TAG, "Parser exception", ex);
                stats.numParseExceptions++;

                NotificationsHelper.signalSyncErrors(getContext(), "Caldav sync error (parsing)", ex.getMessage());
                //NotificationsHelper.getCurrentSyncLog().addException(ex);
            } catch (CaldavProtocolException ex) {
                Log.e(TAG, "Caldav exception", ex);
                stats.numParseExceptions++;

                NotificationsHelper.signalSyncErrors(getContext(), "Caldav sync error (caldav)", ex.getMessage());
                //NotificationsHelper.getCurrentSyncLog().addException(ex);
            }
        }

        rowDelete = androidCalendar.deleteUntaggedEvents();
        rowUntag = androidCalendar.untagAndroidEvents();

        /*Log.i(TAG,"Statistiks for Calendar: " + serverCalendar.getURI().toString());
        Log.i(TAG,"Statistiks for AndroidCalendar: " + androidCalendar.getAndroidCalendarUri().toString());
        Log.i(TAG,"Rows inserted: " + String.valueOf(rowInsert));
        Log.i(TAG,"Rows updated:  " + String.valueOf(rowUpdate));
        Log.i(TAG,"Rows deleted:  " + String.valueOf(rowDelete));
        Log.i(TAG,"Rows skipped:  " + String.valueOf(rowSkip));*/
        Log.i(TAG, "Rows tagged:   " + String.valueOf(rowTag));
        Log.i(TAG, "Rows untagged: " + String.valueOf(rowUntag));

        stats.numInserts += rowInsert;
        stats.numUpdates += rowUpdate;
        stats.numDeletes += rowDelete;
        stats.numSkippedEntries += rowSkip;
        stats.numEntries += rowInsert + rowUpdate + rowDelete;

    }

    /**
     * checks the android events for the dirty flag.
     * the flag is set by android when the event has been changed. 
     * the dirty flag is removed when an android event has been updated from calendar event
     * @param provider
     * @param account
     * @param calendarUri
     * @param facade
     * @param caldavCalendarUri
     * @param stats
     * @param notifyList
     * @return count of dirty events
     */
    private int checkDirtyAndroidEvents(ContentProviderClient provider, Account account, Uri calendarUri,
            CaldavFacade facade, URI caldavCalendarUri, SyncStats stats, ArrayList<Uri> notifyList) {
        Cursor curEvent = null;
        Cursor curAttendee = null;
        Cursor curReminder = null;
        Long EventID;
        Long CalendarID;
        AndroidEvent androidEvent = null;
        int rowDirty = 0;
        int rowInsert = 0;
        int rowUpdate = 0;
        int rowDelete = 0;

        try {
            CalendarID = ContentUris.parseId(calendarUri);
            String selection = "(" + Events.DIRTY + " = ?) AND (" + Events.CALENDAR_ID + " = ?)";
            String[] selectionArgs = new String[] { "1", CalendarID.toString() };
            curEvent = provider.query(Events.CONTENT_URI, null, selection, selectionArgs, null);

            while (curEvent.moveToNext()) {
                EventID = curEvent.getLong(curEvent.getColumnIndex(Events._ID));
                Uri returnedUri = ContentUris.withAppendedId(Events.CONTENT_URI, EventID);

                androidEvent = new AndroidEvent(account, provider, returnedUri, calendarUri);
                androidEvent.readContentValues(curEvent);

                selection = "(" + Attendees.EVENT_ID + " = ?)";
                selectionArgs = new String[] { String.valueOf(EventID) };
                curAttendee = provider.query(Attendees.CONTENT_URI, null, selection, selectionArgs, null);
                selection = "(" + Reminders.EVENT_ID + " = ?)";
                selectionArgs = new String[] { String.valueOf(EventID) };
                curReminder = provider.query(Reminders.CONTENT_URI, null, selection, selectionArgs, null);
                androidEvent.readAttendees(curAttendee);
                androidEvent.readReminder(curReminder);
                curAttendee.close();
                curReminder.close();

                String SyncID = androidEvent.ContentValues.getAsString(Events._SYNC_ID);

                boolean Deleted = false;
                int intDeleted = 0;
                intDeleted = curEvent.getInt(curEvent.getColumnIndex(Events.DELETED));
                Deleted = (intDeleted == 1);

                if (SyncID == null) {
                    // new Android event
                    String newGUID = java.util.UUID.randomUUID().toString() + "-caldavsyncadapter";
                    String calendarPath = caldavCalendarUri.getPath();
                    if (!calendarPath.endsWith("/"))
                        calendarPath += "/";

                    SyncID = calendarPath + newGUID + ".ics";

                    androidEvent.createIcs(newGUID);

                    if (facade.createEvent(URI.create(SyncID), androidEvent.getIcsEvent().toString())) {
                        //HINT: bugfix for google calendar
                        if (SyncID.contains("@"))
                            SyncID = SyncID.replace("@", "%40");
                        ContentValues values = new ContentValues();
                        values.put(Events._SYNC_ID, SyncID);

                        //google doesn't send the etag after creation
                        //HINT: my SabreDAV send always the same etag after putting a new event
                        //String LastETag = facade.getLastETag();
                        //if (!LastETag.equals("")) {
                        //   values.put(Event.ETAG, LastETag);
                        //} else {
                        //so get the etag with a new REPORT
                        CalendarEvent calendarEvent = new CalendarEvent(account, provider);
                        calendarEvent.calendarURL = caldavCalendarUri.toURL();
                        URI SyncURI = new URI(SyncID);
                        calendarEvent.setUri(SyncURI);
                        CaldavFacade.getEvent(calendarEvent);
                        values.put(Event.ETAG, calendarEvent.getETag());
                        //}
                        values.put(Event.UID, newGUID);
                        values.put(Events.DIRTY, 0);
                        values.put(Event.RAWDATA, androidEvent.getIcsEvent().toString());

                        int rowCount = provider.update(
                                asSyncAdapter(androidEvent.getUri(), account.name, account.type), values, null,
                                null);
                        if (rowCount == 1) {
                            rowInsert += 1;
                            notifyList.add(androidEvent.getUri());
                        }
                    }
                } else if (Deleted) {
                    // deleted Android event
                    if (facade.deleteEvent(URI.create(SyncID), androidEvent.getETag())) {
                        String mSelectionClause = "(" + Events._ID + "= ?)";
                        String[] mSelectionArgs = { String.valueOf(EventID) };

                        int countDeleted = provider.delete(
                                asSyncAdapter(Events.CONTENT_URI, account.name, account.type), mSelectionClause,
                                mSelectionArgs);

                        if (countDeleted == 1) {
                            rowDelete += 1;
                            notifyList.add(androidEvent.getUri());
                        }
                    }
                } else {
                    //update the android event to the server
                    String uid = androidEvent.getUID();
                    if ((uid == null) || (uid.equals(""))) {
                        //COMPAT: this is needed because in the past, the UID was not stored in the android event
                        CalendarEvent calendarEvent = new CalendarEvent(account, provider);
                        URI syncURI = new URI(SyncID);
                        calendarEvent.setUri(syncURI);
                        calendarEvent.calendarURL = caldavCalendarUri.toURL();
                        if (calendarEvent.fetchBody()) {
                            calendarEvent.readContentValues();
                            uid = calendarEvent.getUID();
                        }
                    }
                    if (uid != null) {
                        androidEvent.createIcs(uid);

                        if (facade.updateEvent(URI.create(SyncID), androidEvent.getIcsEvent().toString(),
                                androidEvent.getETag())) {
                            selection = "(" + Events._ID + "= ?)";
                            selectionArgs = new String[] { EventID.toString() };
                            androidEvent.ContentValues.put(Events.DIRTY, 0);

                            //google doesn't send the etag after update
                            String LastETag = facade.getLastETag();
                            if (!LastETag.equals("")) {
                                androidEvent.ContentValues.put(Event.ETAG, LastETag);
                            } else {
                                //so get the etag with a new REPORT
                                CalendarEvent calendarEvent = new CalendarEvent(account, provider);
                                calendarEvent.calendarURL = caldavCalendarUri.toURL();
                                URI SyncURI = new URI(SyncID);
                                calendarEvent.setUri(SyncURI);
                                CaldavFacade.getEvent(calendarEvent);
                                androidEvent.ContentValues.put(Event.ETAG, calendarEvent.getETag());
                            }
                            androidEvent.ContentValues.put(Event.RAWDATA, androidEvent.getIcsEvent().toString());
                            int RowCount = provider.update(
                                    asSyncAdapter(androidEvent.getUri(), account.name, account.type),
                                    androidEvent.ContentValues, null, null);

                            if (RowCount == 1) {
                                rowUpdate += 1;
                                notifyList.add(androidEvent.getUri());
                            }
                        } else {
                            rowDirty += 1;
                        }
                    } else {
                        rowDirty += 1;
                    }
                }
            }
            curEvent.close();

            /*if ((rowInsert > 0) || (rowUpdate > 0) || (rowDelete > 0) || (rowDirty > 0)) {
               Log.i(TAG,"Android Rows inserted: " + String.valueOf(rowInsert));
               Log.i(TAG,"Android Rows updated:  " + String.valueOf(rowUpdate));
               Log.i(TAG,"Android Rows deleted:  " + String.valueOf(rowDelete));
               Log.i(TAG,"Android Rows dirty:    " + String.valueOf(rowDirty));
            }*/

            stats.numInserts += rowInsert;
            stats.numUpdates += rowUpdate;
            stats.numDeletes += rowDelete;
            stats.numSkippedEntries += rowDirty;
            stats.numEntries += rowInsert + rowUpdate + rowDelete;
        } catch (RemoteException e) {
            e.printStackTrace();
        } catch (URISyntaxException e) {
            // TODO Automatisch generierter Erfassungsblock
            e.printStackTrace();
        } catch (ClientProtocolException e) {
            // TODO Automatisch generierter Erfassungsblock
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Automatisch generierter Erfassungsblock
            e.printStackTrace();
        } catch (CaldavProtocolException e) {
            // TODO Automatisch generierter Erfassungsblock
            e.printStackTrace();
        } catch (ParserException e) {
            // TODO Automatisch generierter Erfassungsblock
            e.printStackTrace();
        }

        return rowDirty;
    }

    /*   private Account UpgradeAccount(Account OldAccount) {
          String Username = OldAccount.name;
          String Type = OldAccount.type;
          String Password = this.mAccountManager.getPassword(OldAccount);
          String Url = this.mAccountManager.getUserData(OldAccount, AuthenticatorActivity.USER_DATA_URL_KEY);
        
          Account NewAccount = new Account(Username + AuthenticatorActivity.ACCOUNT_NAME_SPLITTER + Url, Type);
          if (this.mAccountManager.addAccountExplicitly(NewAccount, Password, null)) {
     this.mAccountManager.setUserData(NewAccount, AuthenticatorActivity.USER_DATA_URL_KEY, Url);
     this.mAccountManager.setUserData(NewAccount, AuthenticatorActivity.USER_DATA_USERNAME, Username);
          }
          this.mAccountManager.removeAccount(OldAccount, null, null);
              
          return NewAccount;
       }*/

    /*   private void dropAllEvents(Account account, ContentProviderClient provider,   Uri calendarUri) throws RemoteException {
              
          Log.i(TAG, "Deleting all calendar events for "+calendarUri);
              
          String selection = "(" + Events.CALENDAR_ID + " = ?)";
          String[] selectionArgs = new String[] {Long.toString(ContentUris.parseId(calendarUri))}; 
              
          provider.delete(asSyncAdapter(Events.CONTENT_URI, account.name, account.type), 
                selection, selectionArgs);
              
       }*/

    private static Uri asSyncAdapter(Uri uri, String account, String accountType) {
        return uri.buildUpon().appendQueryParameter(android.provider.CalendarContract.CALLER_IS_SYNCADAPTER, "true")
                .appendQueryParameter(Calendars.ACCOUNT_NAME, account)
                .appendQueryParameter(Calendars.ACCOUNT_TYPE, accountType).build();
    }

}