1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/org/gege/caldavsyncadapter/syncadapter/SyncAdapter.java Tue Feb 10 18:12:00 2015 +0100 1.3 @@ -0,0 +1,636 @@ 1.4 +/** 1.5 + * Copyright (c) 2012-2013, Gerald Garcia, David Wiesner, Timo Berger 1.6 + * 1.7 + * This file is part of Andoid Caldav Sync Adapter Free. 1.8 + * 1.9 + * Andoid Caldav Sync Adapter Free is free software: you can redistribute 1.10 + * it and/or modify it under the terms of the GNU General Public License 1.11 + * as published by the Free Software Foundation, either version 3 of the 1.12 + * License, or at your option any later version. 1.13 + * 1.14 + * Andoid Caldav Sync Adapter Free is distributed in the hope that 1.15 + * it will be useful, but WITHOUT ANY WARRANTY; without even the implied 1.16 + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1.17 + * GNU General Public License for more details. 1.18 + * 1.19 + * You should have received a copy of the GNU General Public License 1.20 + * along with Andoid Caldav Sync Adapter Free. 1.21 + * If not, see <http://www.gnu.org/licenses/>. 1.22 + * 1.23 + */ 1.24 + 1.25 +package org.gege.caldavsyncadapter.syncadapter; 1.26 + 1.27 +import java.io.IOException; 1.28 +import java.net.URI; 1.29 +//import java.net.MalformedURLException; 1.30 +import java.net.URISyntaxException; 1.31 +import java.util.ArrayList; 1.32 +//import java.security.GeneralSecurityException; 1.33 + 1.34 +import javax.xml.parsers.ParserConfigurationException; 1.35 + 1.36 +import net.fortuna.ical4j.data.ParserException; 1.37 + 1.38 +import org.apache.http.ParseException; 1.39 +import org.apache.http.client.ClientProtocolException; 1.40 +import org.gege.caldavsyncadapter.Event; 1.41 +import org.gege.caldavsyncadapter.android.entities.AndroidEvent; 1.42 +import org.gege.caldavsyncadapter.authenticator.AuthenticatorActivity; 1.43 +import org.gege.caldavsyncadapter.caldav.CaldavFacade; 1.44 +import org.gege.caldavsyncadapter.caldav.CaldavProtocolException; 1.45 +import org.gege.caldavsyncadapter.caldav.entities.DavCalendar; 1.46 +import org.gege.caldavsyncadapter.caldav.entities.CalendarEvent; 1.47 +import org.gege.caldavsyncadapter.caldav.entities.CalendarList; 1.48 +import org.gege.caldavsyncadapter.caldav.entities.DavCalendar.CalendarSource; 1.49 +import org.gege.caldavsyncadapter.syncadapter.notifications.NotificationsHelper; 1.50 +import org.xml.sax.SAXException; 1.51 + 1.52 +import android.accounts.Account; 1.53 +import android.accounts.AccountManager; 1.54 +import android.content.AbstractThreadedSyncAdapter; 1.55 +import android.content.ContentProviderClient; 1.56 +import android.content.ContentUris; 1.57 +import android.content.ContentValues; 1.58 +import android.content.Context; 1.59 +import android.content.SyncResult; 1.60 +import android.content.SyncStats; 1.61 +import android.content.pm.PackageManager.NameNotFoundException; 1.62 +import android.database.Cursor; 1.63 +import android.net.Uri; 1.64 +import android.os.Bundle; 1.65 +import android.os.RemoteException; 1.66 +import android.provider.CalendarContract.Attendees; 1.67 +import android.provider.CalendarContract.Calendars; 1.68 +import android.provider.CalendarContract.Events; 1.69 +import android.provider.CalendarContract.Reminders; 1.70 +import android.util.Log; 1.71 + 1.72 +public class SyncAdapter extends AbstractThreadedSyncAdapter { 1.73 + 1.74 + private static final String TAG = "SyncAdapter"; 1.75 + private AccountManager mAccountManager; 1.76 + private String mVersion = ""; 1.77 + private int mCountPerformSync = 0; 1.78 + private int mCountSyncCanceled = 0; 1.79 + private int mCountProviderFailed = 0; 1.80 + 1.81 + private int mCountProviderFailedMax = 3; 1.82 +// private Context mContext; 1.83 + 1.84 + 1.85 +/* private static final String[] CALENDAR_PROJECTION = new String[] { 1.86 + Calendars._ID, // 0 1.87 + Calendars.ACCOUNT_NAME, // 1 1.88 + Calendars.CALENDAR_DISPLAY_NAME, // 2 1.89 + Calendars.OWNER_ACCOUNT, // 3 1.90 + Calendar.CTAG // 4 1.91 + };*/ 1.92 + 1.93 +/* // The indices for the projection array above. 1.94 + private static final int PROJECTION_ID_INDEX = 0; 1.95 + private static final int PROJECTION_ACCOUNT_NAME_INDEX = 1; 1.96 + private static final int PROJECTION_DISPLAY_NAME_INDEX = 2; 1.97 + private static final int PROJECTION_OWNER_ACCOUNT_INDEX = 3; 1.98 +*/ 1.99 + 1.100 +/* 1.101 + private static final String[] EVENT_PROJECTION = new String[] { 1.102 + Events._ID, 1.103 + Events._SYNC_ID, 1.104 + Events.SYNC_DATA1, 1.105 + Events.CALENDAR_ID 1.106 + }; 1.107 +*/ 1.108 + 1.109 + // ignore same CTag 1.110 + //private static final boolean FORCE_SYNCHRONIZE = false; 1.111 + // drop all calendar before synchro 1.112 + //private static final boolean DROP_CALENDAR_EVENTS = false; 1.113 + 1.114 + public SyncAdapter(Context context, boolean autoInitialize) { 1.115 + super(context, autoInitialize); 1.116 + //android.os.Debug.waitForDebugger(); 1.117 + mAccountManager = AccountManager.get(context); 1.118 + try { 1.119 + mVersion = context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionName; 1.120 + } catch (NameNotFoundException e) { 1.121 + e.printStackTrace(); 1.122 + } 1.123 +// mContext = context; 1.124 + } 1.125 + 1.126 + @Override 1.127 + public void onPerformSync(Account account, Bundle extras, String authority, 1.128 + ContentProviderClient provider, SyncResult syncResult) { 1.129 + boolean bolError = false; 1.130 + 1.131 + String url = mAccountManager.getUserData(account, AuthenticatorActivity.USER_DATA_URL_KEY); 1.132 + this.mCountPerformSync += 1; 1.133 + Log.v(TAG, "onPerformSync() count:" + String.valueOf(this.mCountPerformSync) + " on " + account.name + " with URL " + url); 1.134 + 1.135 + CalendarList serverCalList; 1.136 + 1.137 + CalendarList androidCalList = new CalendarList(account, provider, CalendarSource.Android, url); 1.138 + androidCalList.readCalendarFromClient(); 1.139 + ArrayList<Uri> notifyList = new ArrayList<Uri>(); 1.140 + 1.141 + try { 1.142 + String Username = ""; 1.143 + String UserDataVersion = mAccountManager.getUserData(account, AuthenticatorActivity.USER_DATA_VERSION); 1.144 + if (UserDataVersion == null) { 1.145 + Username = account.name; 1.146 + } else { 1.147 + Username = mAccountManager.getUserData(account, AuthenticatorActivity.USER_DATA_USERNAME); 1.148 + } 1.149 + 1.150 + CaldavFacade facade = new CaldavFacade(Username, mAccountManager.getPassword(account), url); 1.151 + facade.setAccount(account); 1.152 + facade.setProvider(provider); 1.153 + facade.setVersion(mVersion); 1.154 + serverCalList = facade.getCalendarList(this.getContext()); 1.155 + //String davProperties = facade.getLastDav(); 1.156 + Log.i(TAG, String.valueOf(androidCalList.getCalendarList().size()) + " calendars found at android"); 1.157 + 1.158 + for (DavCalendar serverCalendar : serverCalList.getCalendarList()) { 1.159 + Log.i(TAG, "Detected calendar name=" + serverCalendar.getCalendarDisplayName() + " URI=" + serverCalendar.getURI()); 1.160 + 1.161 + Uri androidCalendarUri = serverCalendar.checkAndroidCalendarList(androidCalList, this.getContext()); 1.162 + 1.163 + // check if the adapter was able to get an existing calendar or create a new one 1.164 + if (androidCalendarUri != null) { 1.165 + // the provider seems to work correct, reset the counter 1.166 + mCountProviderFailed = 0; 1.167 + DavCalendar androidCalendar = androidCalList.getCalendarByAndroidUri(androidCalendarUri); 1.168 + 1.169 + //if ((FORCE_SYNCHRONIZE) || (androidCalendar.getcTag() == null) || (!androidCalendar.getcTag().equals(serverCalendar.getcTag()))) { 1.170 + if ((androidCalendar.getcTag() == null) || (!androidCalendar.getcTag().equals(serverCalendar.getcTag()))) { 1.171 + Log.d(TAG, "CTag has changed, something to synchronise"); 1.172 + if (serverCalendar.readCalendarEvents(facade)) { 1.173 + this.synchroniseEvents(androidCalendar, serverCalendar, syncResult.stats, notifyList); 1.174 + 1.175 + Log.d(TAG, "Updating stored CTag"); 1.176 + //serverCalendar.updateAndroidCalendar(androidCalendarUri, Calendar.CTAG, serverCalendar.getcTag()); 1.177 + androidCalendar.setCTag(serverCalendar.getcTag(), true); 1.178 + } else { 1.179 + Log.d(TAG, "unable to read events from server calendar"); 1.180 + } 1.181 + } else { 1.182 + Log.d(TAG, "CTag has not changed, nothing to do"); 1.183 + 1.184 + /* this is unnecessary. "SkippedEntries" are: 1.185 + * Counter for tracking how many entries, either from the server or the local store, 1.186 + * were ignored during the sync operation. This could happen if the SyncAdapter detected 1.187 + * some unparsable data but decided to skip it and move on rather than failing immediately. 1.188 + */ 1.189 + 1.190 + /*long CalendarID = ContentUris.parseId(androidCalendarUri); 1.191 + String selection = "(" + Events.CALENDAR_ID + " = ?)"; 1.192 + String[] selectionArgs = new String[] {String.valueOf(CalendarID)}; 1.193 + Cursor countCursor = provider.query(Events.CONTENT_URI, new String[] {"count(*) AS count"}, 1.194 + selection, 1.195 + selectionArgs, 1.196 + null); 1.197 + 1.198 + countCursor.moveToFirst(); 1.199 + int count = countCursor.getInt(0); 1.200 + syncResult.stats.numSkippedEntries += count; 1.201 + countCursor.close();*/ 1.202 + 1.203 + } 1.204 + 1.205 + this.checkDirtyAndroidEvents(provider, account, androidCalendarUri, facade, serverCalendar.getURI(), syncResult.stats, notifyList); 1.206 + } else { 1.207 + // this happens if the data provider failes to get an existing or create a new calendar 1.208 + mCountProviderFailed += 1; 1.209 + Log.e(TAG, "failed to get an existing or create a new calendar"); 1.210 + syncResult.stats.numIoExceptions += 1; 1.211 + if (mCountProviderFailed >= mCountProviderFailedMax) { 1.212 + // see issue #96 1.213 + NotificationsHelper.signalSyncErrors(this.getContext(), "Caldav sync error (provider failed)", "are you using CyanogenMod in Incognito Mode?"); 1.214 + } else { 1.215 + NotificationsHelper.signalSyncErrors(this.getContext(), "Caldav sync error (provider failed)", "the provider failed to get an existing or create a new calendar"); 1.216 + } 1.217 + bolError = true; 1.218 + } 1.219 + } 1.220 + 1.221 + if (!bolError) { 1.222 + // check whether a calendar is not synced -> delete it at android 1.223 + androidCalList.deleteCalendarOnClientSideOnly(this.getContext()); 1.224 + } 1.225 + 1.226 + // notify the ContentResolver 1.227 + for (Uri uri : androidCalList.getNotifyList()) { 1.228 + this.getContext().getContentResolver().notifyChange(uri, null); 1.229 + } 1.230 + for (Uri uri : serverCalList.getNotifyList()) { 1.231 + this.getContext().getContentResolver().notifyChange(uri, null); 1.232 + } 1.233 + for (Uri uri : notifyList) { 1.234 + this.getContext().getContentResolver().notifyChange(uri, null); 1.235 + } 1.236 + 1.237 + //Log.i(TAG,"Statistiks for Calendar: " + serverCalendar.getURI().toString()); 1.238 + //Log.i(TAG,"Statistiks for AndroidCalendar: " + androidCalendar.getAndroidCalendarUri().toString()); 1.239 + Log.i(TAG,"Entries: " + String.valueOf(syncResult.stats.numEntries)); 1.240 + Log.i(TAG,"Rows inserted: " + String.valueOf(syncResult.stats.numInserts)); 1.241 + Log.i(TAG,"Rows updated: " + String.valueOf(syncResult.stats.numUpdates)); 1.242 + Log.i(TAG,"Rows deleted: " + String.valueOf(syncResult.stats.numDeletes)); 1.243 + Log.i(TAG,"Rows skipped: " + String.valueOf(syncResult.stats.numSkippedEntries)); 1.244 + Log.i(TAG,"Io Exceptions: " + String.valueOf(syncResult.stats.numIoExceptions)); 1.245 + Log.i(TAG,"Parse Exceptions: " + String.valueOf(syncResult.stats.numParseExceptions)); 1.246 + Log.i(TAG,"Auth Exceptions: " + String.valueOf(syncResult.stats.numAuthExceptions)); 1.247 + Log.i(TAG,"Conflict Detected Exceptions: " + String.valueOf(syncResult.stats.numConflictDetectedExceptions)); 1.248 + 1.249 + /*} catch (final AuthenticatorException e) { 1.250 + syncResult.stats.numParseExceptions++; 1.251 + Log.e(TAG, "AuthenticatorException", e);*/ 1.252 + /*} catch (final OperationCanceledException e) { 1.253 + Log.e(TAG, "OperationCanceledExcetpion", e);*/ 1.254 + } catch (final IOException e) { 1.255 + Log.e(TAG, "IOException", e); 1.256 + syncResult.stats.numIoExceptions++; 1.257 + NotificationsHelper.signalSyncErrors(this.getContext(), "Caldav sync error (IO)", e.getMessage()); 1.258 + //NotificationsHelper.getCurrentSyncLog().addException(e); 1.259 + /*} catch (final AuthenticationException e) { 1.260 + //mAccountManager.invalidateAuthToken(Constants.ACCOUNT_TYPE, authtoken); 1.261 + syncResult.stats.numAuthExceptions++; 1.262 + Log.e(TAG, "AuthenticationException", e);*/ 1.263 + } catch (final ParseException e) { 1.264 + syncResult.stats.numParseExceptions++; 1.265 + Log.e(TAG, "ParseException", e); 1.266 + NotificationsHelper.signalSyncErrors(this.getContext(), "Caldav sync error (parsing)", e.getMessage()); 1.267 + //NotificationsHelper.getCurrentSyncLog().addException(e); 1.268 + /*} catch (final JSONException e) { 1.269 + syncResult.stats.numParseExceptions++; 1.270 + Log.e(TAG, "JSONException", e);*/ 1.271 + } catch (Exception e) { 1.272 + Log.e(TAG, "Updating calendar exception " + e.getClass().getName(), e); 1.273 + syncResult.stats.numParseExceptions++; 1.274 + NotificationsHelper.signalSyncErrors(this.getContext(), "Caldav sync error (general)", e.getMessage()); 1.275 + //NotificationsHelper.getCurrentSyncLog().addException(e); 1.276 + //throw new RuntimeException(e); 1.277 + } 1.278 + } 1.279 + 1.280 + public void onSyncCanceled () { 1.281 + //TODO: implement SyncCanceled 1.282 + this.mCountSyncCanceled += 1; 1.283 + Log.v(TAG, "onSyncCanceled() count:" + String.valueOf(this.mCountSyncCanceled)); 1.284 + } 1.285 + 1.286 + 1.287 + /** 1.288 + * both calender event and android event have been found. 1.289 + * server wins always at the moment. 1.290 + * @param androidCalendar 1.291 + * @param serverCalendar 1.292 + * @param stats 1.293 + * @param notifyList 1.294 + * @throws ClientProtocolException 1.295 + * @throws URISyntaxException 1.296 + * @throws IOException 1.297 + * @throws ParserConfigurationException 1.298 + * @throws SAXException 1.299 + * @throws RemoteException 1.300 + * @throws CaldavProtocolException 1.301 + * @throws ParserException 1.302 + * @see SyncAdapter#updateAndroidEvent(ContentProviderClient, Account, AndroidEvent, CalendarEvent) 1.303 + * @see SyncAdapter#tagAndroidEvent(ContentProviderClient, Account, AndroidEvent) 1.304 + * @see SyncAdapter#untagAndroidEvents(ContentProviderClient, Account, Uri) 1.305 + * @see SyncAdapter#deleteUntaggedEvents(ContentProviderClient, Account, Uri) 1.306 + */ 1.307 + private void synchroniseEvents( 1.308 + DavCalendar androidCalendar, 1.309 + DavCalendar serverCalendar, 1.310 + SyncStats stats, 1.311 + ArrayList<Uri> notifyList 1.312 + ) throws ClientProtocolException, URISyntaxException, IOException, ParserConfigurationException, SAXException, RemoteException, CaldavProtocolException, ParserException { 1.313 + 1.314 + /*if (DROP_CALENDAR_EVENTS) { 1.315 + dropAllEvents(account, provider, androidCalendar.getAndroidCalendarUri()); 1.316 + }*/ 1.317 + 1.318 + int rowInsert = 0; 1.319 + int rowUpdate = 0; 1.320 + int rowTag = 0; 1.321 + int rowDelete = 0; 1.322 + int rowUntag = 0; 1.323 + int rowSkip = 0; 1.324 + 1.325 + for (CalendarEvent calendarEvent : serverCalendar.getCalendarEvents()) { 1.326 + try { 1.327 + AndroidEvent androidEvent = calendarEvent.getAndroidEvent(androidCalendar); 1.328 + 1.329 + Log.i(TAG, "Event " + calendarEvent.getUri().toString()+ " androidUri="+androidEvent); 1.330 + 1.331 + if (androidEvent == null) { 1.332 + /* new android event */ 1.333 + if (calendarEvent.createAndroidEvent(androidCalendar)) { 1.334 + rowInsert += 1; 1.335 + androidEvent = calendarEvent.getAndroidEvent(androidCalendar); 1.336 + notifyList.add(androidEvent.getUri()); 1.337 + } else { 1.338 + rowSkip += 1; 1.339 + } 1.340 + } else { 1.341 + /* the android exists */ 1.342 + String androidETag = androidEvent.getETag(); 1.343 + if (androidETag == null) 1.344 + androidETag = ""; 1.345 + Log.d(TAG, "Event compare: " + androidETag + " <> " + calendarEvent.getETag().toString()); 1.346 + if ((androidEvent.getETag() == null) || (!androidETag.equals(calendarEvent.getETag()))) { 1.347 + /* the android event is getting updated */ 1.348 + if (calendarEvent.updateAndroidEvent(androidEvent)) { 1.349 + rowUpdate += 1; 1.350 + notifyList.add(androidEvent.getUri()); 1.351 + } else { 1.352 + rowSkip += 1; 1.353 + } 1.354 + } 1.355 + } 1.356 + if (androidEvent != null) 1.357 + //if (androidEvent.tagAndroidEvent()) 1.358 + if (androidCalendar.tagAndroidEvent(androidEvent)) 1.359 + rowTag += 1; 1.360 + 1.361 + 1.362 + } catch (ParserException ex) { 1.363 + Log.e(TAG, "Parser exception", ex); 1.364 + stats.numParseExceptions++; 1.365 + 1.366 + NotificationsHelper.signalSyncErrors(getContext(), "Caldav sync error (parsing)", ex.getMessage()); 1.367 + //NotificationsHelper.getCurrentSyncLog().addException(ex); 1.368 + } catch (CaldavProtocolException ex) { 1.369 + Log.e(TAG, "Caldav exception", ex); 1.370 + stats.numParseExceptions++; 1.371 + 1.372 + NotificationsHelper.signalSyncErrors(getContext(), "Caldav sync error (caldav)", ex.getMessage()); 1.373 + //NotificationsHelper.getCurrentSyncLog().addException(ex); 1.374 + } 1.375 + } 1.376 + 1.377 + rowDelete = androidCalendar.deleteUntaggedEvents(); 1.378 + rowUntag = androidCalendar.untagAndroidEvents(); 1.379 + 1.380 + /*Log.i(TAG,"Statistiks for Calendar: " + serverCalendar.getURI().toString()); 1.381 + Log.i(TAG,"Statistiks for AndroidCalendar: " + androidCalendar.getAndroidCalendarUri().toString()); 1.382 + Log.i(TAG,"Rows inserted: " + String.valueOf(rowInsert)); 1.383 + Log.i(TAG,"Rows updated: " + String.valueOf(rowUpdate)); 1.384 + Log.i(TAG,"Rows deleted: " + String.valueOf(rowDelete)); 1.385 + Log.i(TAG,"Rows skipped: " + String.valueOf(rowSkip));*/ 1.386 + Log.i(TAG,"Rows tagged: " + String.valueOf(rowTag)); 1.387 + Log.i(TAG,"Rows untagged: " + String.valueOf(rowUntag)); 1.388 + 1.389 + stats.numInserts += rowInsert; 1.390 + stats.numUpdates += rowUpdate; 1.391 + stats.numDeletes += rowDelete; 1.392 + stats.numSkippedEntries += rowSkip; 1.393 + stats.numEntries += rowInsert + rowUpdate + rowDelete; 1.394 + 1.395 + } 1.396 + 1.397 + /** 1.398 + * checks the android events for the dirty flag. 1.399 + * the flag is set by android when the event has been changed. 1.400 + * the dirty flag is removed when an android event has been updated from calendar event 1.401 + * @param provider 1.402 + * @param account 1.403 + * @param calendarUri 1.404 + * @param facade 1.405 + * @param caldavCalendarUri 1.406 + * @param stats 1.407 + * @param notifyList 1.408 + * @return count of dirty events 1.409 + */ 1.410 + private int checkDirtyAndroidEvents( 1.411 + ContentProviderClient provider, 1.412 + Account account, 1.413 + Uri calendarUri, 1.414 + CaldavFacade facade, 1.415 + URI caldavCalendarUri, 1.416 + SyncStats stats, 1.417 + ArrayList<Uri> notifyList 1.418 + ) { 1.419 + Cursor curEvent = null; 1.420 + Cursor curAttendee = null; 1.421 + Cursor curReminder = null; 1.422 + Long EventID; 1.423 + Long CalendarID; 1.424 + AndroidEvent androidEvent = null; 1.425 + int rowDirty = 0; 1.426 + int rowInsert = 0; 1.427 + int rowUpdate = 0; 1.428 + int rowDelete = 0; 1.429 + 1.430 + try { 1.431 + CalendarID = ContentUris.parseId(calendarUri); 1.432 + String selection = "(" + Events.DIRTY + " = ?) AND (" + Events.CALENDAR_ID + " = ?)"; 1.433 + String[] selectionArgs = new String[] {"1", CalendarID.toString()}; 1.434 + curEvent = provider.query(Events.CONTENT_URI, null, selection, selectionArgs, null); 1.435 + 1.436 + while (curEvent.moveToNext()) { 1.437 + EventID = curEvent.getLong(curEvent.getColumnIndex(Events._ID)); 1.438 + Uri returnedUri = ContentUris.withAppendedId(Events.CONTENT_URI, EventID); 1.439 + 1.440 + //androidEvent = new AndroidEvent(account, provider, returnedUri, calendarUri); 1.441 + androidEvent = new AndroidEvent(returnedUri, calendarUri); 1.442 + androidEvent.readContentValues(curEvent); 1.443 + 1.444 + selection = "(" + Attendees.EVENT_ID + " = ?)"; 1.445 + selectionArgs = new String[] {String.valueOf(EventID)}; 1.446 + curAttendee = provider.query(Attendees.CONTENT_URI, null, selection, selectionArgs, null); 1.447 + selection = "(" + Reminders.EVENT_ID + " = ?)"; 1.448 + selectionArgs = new String[] {String.valueOf(EventID)}; 1.449 + curReminder = provider.query(Reminders.CONTENT_URI, null, selection, selectionArgs, null); 1.450 + androidEvent.readAttendees(curAttendee); 1.451 + androidEvent.readReminder(curReminder); 1.452 + curAttendee.close(); 1.453 + curReminder.close(); 1.454 + 1.455 + String SyncID = androidEvent.ContentValues.getAsString(Events._SYNC_ID); 1.456 + 1.457 + boolean Deleted = false; 1.458 + int intDeleted = 0; 1.459 + intDeleted = curEvent.getInt(curEvent.getColumnIndex(Events.DELETED)); 1.460 + Deleted = (intDeleted == 1); 1.461 + 1.462 + if (SyncID == null) { 1.463 + // new Android event 1.464 + String newGUID = java.util.UUID.randomUUID().toString() + "-caldavsyncadapter"; 1.465 + String calendarPath = caldavCalendarUri.getPath(); 1.466 + if (!calendarPath.endsWith("/")) 1.467 + calendarPath += "/"; 1.468 + 1.469 + SyncID = calendarPath + newGUID + ".ics"; 1.470 + 1.471 + androidEvent.createIcs(newGUID); 1.472 + 1.473 + if (facade.createEvent(URI.create(SyncID), androidEvent.getIcsEvent().toString())) { 1.474 + //HINT: bugfix for google calendar replace("@", "%40") 1.475 + if (SyncID.contains("@")) 1.476 + SyncID = SyncID.replace("@", "%40"); 1.477 + ContentValues values = new ContentValues(); 1.478 + values.put(Events._SYNC_ID, SyncID); 1.479 + 1.480 + //google doesn't send the etag after creation 1.481 + //HINT: my SabreDAV send always the same etag after putting a new event 1.482 + //String LastETag = facade.getLastETag(); 1.483 + //if (!LastETag.equals("")) { 1.484 + // values.put(Event.ETAG, LastETag); 1.485 + //} else { 1.486 + //so get the etag with a new REPORT 1.487 + CalendarEvent calendarEvent = new CalendarEvent(account, provider); 1.488 + calendarEvent.calendarURL = caldavCalendarUri.toURL(); 1.489 + URI SyncURI = new URI(SyncID); 1.490 + calendarEvent.setUri(SyncURI); 1.491 + CaldavFacade.getEvent(calendarEvent); 1.492 + values.put(Event.ETAG, calendarEvent.getETag()); 1.493 + //} 1.494 + values.put(Event.UID, newGUID); 1.495 + values.put(Events.DIRTY, 0); 1.496 + values.put(Event.RAWDATA, androidEvent.getIcsEvent().toString()); 1.497 + 1.498 + int rowCount = provider.update(asSyncAdapter(androidEvent.getUri(), account.name, account.type), values, null, null); 1.499 + if (rowCount == 1) { 1.500 + rowInsert += 1; 1.501 + notifyList.add(androidEvent.getUri()); 1.502 + } 1.503 + } 1.504 + } else if (Deleted) { 1.505 + // deleted Android event 1.506 + if (facade.deleteEvent(URI.create(SyncID), androidEvent.getETag())) { 1.507 + String mSelectionClause = "(" + Events._ID + "= ?)"; 1.508 + String[] mSelectionArgs = {String.valueOf(EventID)}; 1.509 + 1.510 + int countDeleted = provider.delete(asSyncAdapter(Events.CONTENT_URI, account.name, account.type), mSelectionClause, mSelectionArgs); 1.511 + 1.512 + if (countDeleted == 1) { 1.513 + rowDelete += 1; 1.514 + notifyList.add(androidEvent.getUri()); 1.515 + } 1.516 + } 1.517 + } else { 1.518 + //update the android event to the server 1.519 + String uid = androidEvent.getUID(); 1.520 + if ((uid == null) || (uid.equals(""))) { 1.521 + //COMPAT: this is needed because in the past, the UID was not stored in the android event 1.522 + CalendarEvent calendarEvent = new CalendarEvent(account, provider); 1.523 + URI syncURI = new URI(SyncID); 1.524 + calendarEvent.setUri(syncURI); 1.525 + calendarEvent.calendarURL = caldavCalendarUri.toURL(); 1.526 + if (calendarEvent.fetchBody()) { 1.527 + calendarEvent.readContentValues(); 1.528 + uid = calendarEvent.getUID(); 1.529 + } 1.530 + } 1.531 + if (uid != null) { 1.532 + androidEvent.createIcs(uid); 1.533 + 1.534 + if (facade.updateEvent(URI.create(SyncID), androidEvent.getIcsEvent().toString(), androidEvent.getETag())) { 1.535 + selection = "(" + Events._ID + "= ?)"; 1.536 + selectionArgs = new String[] {EventID.toString()}; 1.537 + androidEvent.ContentValues.put(Events.DIRTY, 0); 1.538 + 1.539 + //google doesn't send the etag after update 1.540 + String LastETag = facade.getLastETag(); 1.541 + if (!LastETag.equals("")) { 1.542 + androidEvent.ContentValues.put(Event.ETAG, LastETag); 1.543 + } else { 1.544 + //so get the etag with a new REPORT 1.545 + CalendarEvent calendarEvent = new CalendarEvent(account, provider); 1.546 + calendarEvent.calendarURL = caldavCalendarUri.toURL(); 1.547 + URI SyncURI = new URI(SyncID); 1.548 + calendarEvent.setUri(SyncURI); 1.549 + CaldavFacade.getEvent(calendarEvent); 1.550 + androidEvent.ContentValues.put(Event.ETAG, calendarEvent.getETag()); 1.551 + } 1.552 + androidEvent.ContentValues.put(Event.RAWDATA, androidEvent.getIcsEvent().toString()); 1.553 + int RowCount = provider.update(asSyncAdapter(androidEvent.getUri(), account.name, account.type), androidEvent.ContentValues, null, null); 1.554 + 1.555 + if (RowCount == 1) { 1.556 + rowUpdate += 1; 1.557 + notifyList.add(androidEvent.getUri()); 1.558 + } 1.559 + } else { 1.560 + rowDirty += 1; 1.561 + } 1.562 + } else { 1.563 + rowDirty += 1; 1.564 + } 1.565 + } 1.566 + } 1.567 + curEvent.close(); 1.568 + 1.569 + /*if ((rowInsert > 0) || (rowUpdate > 0) || (rowDelete > 0) || (rowDirty > 0)) { 1.570 + Log.i(TAG,"Android Rows inserted: " + String.valueOf(rowInsert)); 1.571 + Log.i(TAG,"Android Rows updated: " + String.valueOf(rowUpdate)); 1.572 + Log.i(TAG,"Android Rows deleted: " + String.valueOf(rowDelete)); 1.573 + Log.i(TAG,"Android Rows dirty: " + String.valueOf(rowDirty)); 1.574 + }*/ 1.575 + 1.576 + stats.numInserts += rowInsert; 1.577 + stats.numUpdates += rowUpdate; 1.578 + stats.numDeletes += rowDelete; 1.579 + stats.numSkippedEntries += rowDirty; 1.580 + stats.numEntries += rowInsert + rowUpdate + rowDelete; 1.581 + } catch (RemoteException e) { 1.582 + e.printStackTrace(); 1.583 + } catch (URISyntaxException e) { 1.584 + // TODO Automatisch generierter Erfassungsblock 1.585 + e.printStackTrace(); 1.586 + } catch (ClientProtocolException e) { 1.587 + // TODO Automatisch generierter Erfassungsblock 1.588 + e.printStackTrace(); 1.589 + } catch (IOException e) { 1.590 + // TODO Automatisch generierter Erfassungsblock 1.591 + e.printStackTrace(); 1.592 + } catch (CaldavProtocolException e) { 1.593 + // TODO Automatisch generierter Erfassungsblock 1.594 + e.printStackTrace(); 1.595 + } catch (ParserException e) { 1.596 + // TODO Automatisch generierter Erfassungsblock 1.597 + e.printStackTrace(); 1.598 + } 1.599 + 1.600 + return rowDirty; 1.601 + } 1.602 + 1.603 +/* private Account UpgradeAccount(Account OldAccount) { 1.604 + String Username = OldAccount.name; 1.605 + String Type = OldAccount.type; 1.606 + String Password = this.mAccountManager.getPassword(OldAccount); 1.607 + String Url = this.mAccountManager.getUserData(OldAccount, AuthenticatorActivity.USER_DATA_URL_KEY); 1.608 + 1.609 + Account NewAccount = new Account(Username + AuthenticatorActivity.ACCOUNT_NAME_SPLITTER + Url, Type); 1.610 + if (this.mAccountManager.addAccountExplicitly(NewAccount, Password, null)) { 1.611 + this.mAccountManager.setUserData(NewAccount, AuthenticatorActivity.USER_DATA_URL_KEY, Url); 1.612 + this.mAccountManager.setUserData(NewAccount, AuthenticatorActivity.USER_DATA_USERNAME, Username); 1.613 + } 1.614 + this.mAccountManager.removeAccount(OldAccount, null, null); 1.615 + 1.616 + return NewAccount; 1.617 + }*/ 1.618 + 1.619 +/* private void dropAllEvents(Account account, ContentProviderClient provider, Uri calendarUri) throws RemoteException { 1.620 + 1.621 + Log.i(TAG, "Deleting all calendar events for "+calendarUri); 1.622 + 1.623 + String selection = "(" + Events.CALENDAR_ID + " = ?)"; 1.624 + String[] selectionArgs = new String[] {Long.toString(ContentUris.parseId(calendarUri))}; 1.625 + 1.626 + provider.delete(asSyncAdapter(Events.CONTENT_URI, account.name, account.type), 1.627 + selection, selectionArgs); 1.628 + 1.629 + }*/ 1.630 + 1.631 + private static Uri asSyncAdapter(Uri uri, String account, String accountType) { 1.632 + return uri.buildUpon() 1.633 + .appendQueryParameter(android.provider.CalendarContract.CALLER_IS_SYNCADAPTER,"true") 1.634 + .appendQueryParameter(Calendars.ACCOUNT_NAME, account) 1.635 + .appendQueryParameter(Calendars.ACCOUNT_TYPE, accountType).build(); 1.636 + } 1.637 + 1.638 +} 1.639 +