Tue, 10 Feb 2015 22:40:00 +0100
Merge https://github.com/gggard/AndroidCaldavSyncAdapater/pull/206/
michael@0 | 1 | /** |
michael@0 | 2 | * Copyright (c) 2012-2013, Gerald Garcia, David Wiesner, Timo Berger |
michael@8 | 3 | * |
michael@0 | 4 | * This file is part of Andoid Caldav Sync Adapter Free. |
michael@0 | 5 | * |
michael@0 | 6 | * Andoid Caldav Sync Adapter Free is free software: you can redistribute |
michael@0 | 7 | * it and/or modify it under the terms of the GNU General Public License |
michael@0 | 8 | * as published by the Free Software Foundation, either version 3 of the |
michael@0 | 9 | * License, or at your option any later version. |
michael@0 | 10 | * |
michael@0 | 11 | * Andoid Caldav Sync Adapter Free is distributed in the hope that |
michael@0 | 12 | * it will be useful, but WITHOUT ANY WARRANTY; without even the implied |
michael@0 | 13 | * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
michael@0 | 14 | * GNU General Public License for more details. |
michael@0 | 15 | * |
michael@0 | 16 | * You should have received a copy of the GNU General Public License |
michael@0 | 17 | * along with Andoid Caldav Sync Adapter Free. |
michael@0 | 18 | * If not, see <http://www.gnu.org/licenses/>. |
michael@8 | 19 | * |
michael@0 | 20 | */ |
michael@0 | 21 | |
michael@0 | 22 | package org.gege.caldavsyncadapter.syncadapter; |
michael@0 | 23 | |
michael@0 | 24 | import android.accounts.Account; |
michael@0 | 25 | import android.accounts.AccountManager; |
michael@0 | 26 | import android.content.AbstractThreadedSyncAdapter; |
michael@0 | 27 | import android.content.ContentProviderClient; |
michael@0 | 28 | import android.content.ContentUris; |
michael@0 | 29 | import android.content.ContentValues; |
michael@0 | 30 | import android.content.Context; |
michael@0 | 31 | import android.content.SyncResult; |
michael@0 | 32 | import android.content.SyncStats; |
michael@0 | 33 | import android.content.pm.PackageManager.NameNotFoundException; |
michael@0 | 34 | import android.database.Cursor; |
michael@0 | 35 | import android.net.Uri; |
michael@0 | 36 | import android.os.Bundle; |
michael@0 | 37 | import android.os.RemoteException; |
michael@0 | 38 | import android.provider.CalendarContract.Attendees; |
michael@0 | 39 | import android.provider.CalendarContract.Calendars; |
michael@0 | 40 | import android.provider.CalendarContract.Events; |
michael@0 | 41 | import android.provider.CalendarContract.Reminders; |
michael@0 | 42 | import android.util.Log; |
michael@0 | 43 | |
michael@8 | 44 | import net.fortuna.ical4j.data.ParserException; |
michael@8 | 45 | |
michael@8 | 46 | import org.apache.http.ParseException; |
michael@8 | 47 | import org.apache.http.client.ClientProtocolException; |
michael@8 | 48 | import org.gege.caldavsyncadapter.Constants; |
michael@8 | 49 | import org.gege.caldavsyncadapter.Event; |
michael@8 | 50 | import org.gege.caldavsyncadapter.android.entities.AndroidEvent; |
michael@8 | 51 | import org.gege.caldavsyncadapter.authenticator.AuthenticatorActivity; |
michael@8 | 52 | import org.gege.caldavsyncadapter.caldav.CaldavFacade; |
michael@8 | 53 | import org.gege.caldavsyncadapter.caldav.CaldavProtocolException; |
michael@8 | 54 | import org.gege.caldavsyncadapter.caldav.entities.CalendarEvent; |
michael@8 | 55 | import org.gege.caldavsyncadapter.caldav.entities.CalendarList; |
michael@8 | 56 | import org.gege.caldavsyncadapter.caldav.entities.DavCalendar; |
michael@8 | 57 | import org.gege.caldavsyncadapter.caldav.entities.DavCalendar.CalendarSource; |
michael@8 | 58 | import org.gege.caldavsyncadapter.syncadapter.notifications.NotificationsHelper; |
michael@8 | 59 | import org.xml.sax.SAXException; |
michael@8 | 60 | |
michael@8 | 61 | import java.io.IOException; |
michael@8 | 62 | import java.net.URI; |
michael@8 | 63 | import java.net.URISyntaxException; |
michael@8 | 64 | import java.util.ArrayList; |
michael@8 | 65 | |
michael@8 | 66 | import javax.xml.parsers.ParserConfigurationException; |
michael@8 | 67 | |
michael@8 | 68 | //import java.net.MalformedURLException; |
michael@8 | 69 | //import java.security.GeneralSecurityException; |
michael@8 | 70 | |
michael@0 | 71 | public class SyncAdapter extends AbstractThreadedSyncAdapter { |
michael@0 | 72 | |
michael@8 | 73 | private static final String TAG = "SyncAdapter"; |
michael@8 | 74 | private AccountManager mAccountManager; |
michael@8 | 75 | private String mVersion = ""; |
michael@8 | 76 | private int mCountPerformSync = 0; |
michael@8 | 77 | private int mCountSyncCanceled = 0; |
michael@8 | 78 | private int mCountProviderFailed = 0; |
michael@8 | 79 | |
michael@8 | 80 | private int mCountProviderFailedMax = 3; |
michael@0 | 81 | // private Context mContext; |
michael@8 | 82 | |
michael@0 | 83 | |
michael@0 | 84 | /* private static final String[] CALENDAR_PROJECTION = new String[] { |
michael@0 | 85 | Calendars._ID, // 0 |
michael@0 | 86 | Calendars.ACCOUNT_NAME, // 1 |
michael@0 | 87 | Calendars.CALENDAR_DISPLAY_NAME, // 2 |
michael@0 | 88 | Calendars.OWNER_ACCOUNT, // 3 |
michael@0 | 89 | Calendar.CTAG // 4 |
michael@0 | 90 | };*/ |
michael@0 | 91 | |
michael@0 | 92 | /* // The indices for the projection array above. |
michael@0 | 93 | private static final int PROJECTION_ID_INDEX = 0; |
michael@0 | 94 | private static final int PROJECTION_ACCOUNT_NAME_INDEX = 1; |
michael@0 | 95 | private static final int PROJECTION_DISPLAY_NAME_INDEX = 2; |
michael@0 | 96 | private static final int PROJECTION_OWNER_ACCOUNT_INDEX = 3; |
michael@0 | 97 | */ |
michael@0 | 98 | |
michael@0 | 99 | /* |
michael@0 | 100 | private static final String[] EVENT_PROJECTION = new String[] { |
michael@0 | 101 | Events._ID, |
michael@0 | 102 | Events._SYNC_ID, |
michael@0 | 103 | Events.SYNC_DATA1, |
michael@0 | 104 | Events.CALENDAR_ID |
michael@0 | 105 | }; |
michael@0 | 106 | */ |
michael@8 | 107 | |
michael@8 | 108 | // ignore same CTag |
michael@8 | 109 | //private static final boolean FORCE_SYNCHRONIZE = false; |
michael@8 | 110 | // drop all calendar before synchro |
michael@8 | 111 | //private static final boolean DROP_CALENDAR_EVENTS = false; |
michael@8 | 112 | |
michael@8 | 113 | public SyncAdapter(Context context, boolean autoInitialize) { |
michael@8 | 114 | super(context, autoInitialize); |
michael@8 | 115 | //android.os.Debug.waitForDebugger(); |
michael@8 | 116 | mAccountManager = AccountManager.get(context); |
michael@8 | 117 | try { |
michael@8 | 118 | mVersion = context.getPackageManager() |
michael@8 | 119 | .getPackageInfo(context.getPackageName(), 0).versionName; |
michael@8 | 120 | } catch (NameNotFoundException e) { |
michael@8 | 121 | e.printStackTrace(); |
michael@8 | 122 | } |
michael@0 | 123 | // mContext = context; |
michael@8 | 124 | } |
michael@0 | 125 | |
michael@8 | 126 | @Override |
michael@8 | 127 | public void onPerformSync(Account account, Bundle extras, String authority, |
michael@8 | 128 | ContentProviderClient provider, SyncResult syncResult) { |
michael@8 | 129 | boolean bolError = false; |
michael@0 | 130 | |
michael@8 | 131 | String url = mAccountManager.getUserData(account, AuthenticatorActivity.USER_DATA_URL_KEY); |
michael@8 | 132 | String trust = mAccountManager.getUserData(account, Constants.USER_DATA_TRUST_ALL_KEY); |
michael@8 | 133 | this.mCountPerformSync += 1; |
michael@8 | 134 | Log.v(TAG, "onPerformSync() count:" + String.valueOf(this.mCountPerformSync) + " on " + account.name + " with URL " + url); |
michael@0 | 135 | |
michael@8 | 136 | CalendarList serverCalList; |
michael@0 | 137 | |
michael@8 | 138 | CalendarList androidCalList = new CalendarList(account, provider, CalendarSource.Android, url); |
michael@8 | 139 | androidCalList.readCalendarFromClient(); |
michael@8 | 140 | ArrayList<Uri> notifyList = new ArrayList<Uri>(); |
michael@0 | 141 | |
michael@8 | 142 | try { |
michael@8 | 143 | String Username = ""; |
michael@8 | 144 | String UserDataVersion = mAccountManager.getUserData(account, AuthenticatorActivity.USER_DATA_VERSION); |
michael@8 | 145 | if (UserDataVersion == null) { |
michael@8 | 146 | Username = account.name; |
michael@8 | 147 | } else { |
michael@8 | 148 | Username = mAccountManager.getUserData(account, AuthenticatorActivity.USER_DATA_USERNAME); |
michael@8 | 149 | } |
michael@0 | 150 | |
michael@8 | 151 | CaldavFacade facade = new CaldavFacade(Username, mAccountManager.getPassword(account), url, trust); |
michael@8 | 152 | facade.setAccount(account); |
michael@8 | 153 | facade.setProvider(provider); |
michael@8 | 154 | facade.setVersion(mVersion); |
michael@8 | 155 | serverCalList = facade.getCalendarList(this.getContext()); |
michael@8 | 156 | //String davProperties = facade.getLastDav(); |
michael@8 | 157 | Log.i(TAG, String.valueOf(androidCalList.getCalendarList() |
michael@8 | 158 | .size()) + " calendars found at android"); |
michael@0 | 159 | |
michael@8 | 160 | for (DavCalendar serverCalendar : serverCalList.getCalendarList()) { |
michael@8 | 161 | Log.i(TAG, "Detected calendar name=" + serverCalendar.getCalendarDisplayName() + " URI=" + serverCalendar |
michael@8 | 162 | .getURI()); |
michael@8 | 163 | |
michael@8 | 164 | Uri androidCalendarUri = serverCalendar.checkAndroidCalendarList(androidCalList, this |
michael@8 | 165 | .getContext()); |
michael@8 | 166 | |
michael@8 | 167 | // check if the adapter was able to get an existing calendar or create a new one |
michael@8 | 168 | if (androidCalendarUri != null) { |
michael@8 | 169 | // the provider seems to work correct, reset the counter |
michael@8 | 170 | mCountProviderFailed = 0; |
michael@8 | 171 | DavCalendar androidCalendar = androidCalList.getCalendarByAndroidUri(androidCalendarUri); |
michael@8 | 172 | |
michael@8 | 173 | //if ((FORCE_SYNCHRONIZE) || (androidCalendar.getcTag() == null) || (!androidCalendar.getcTag().equals(serverCalendar.getcTag()))) { |
michael@8 | 174 | if ((androidCalendar.getcTag() == null) || (!androidCalendar.getcTag() |
michael@8 | 175 | .equals(serverCalendar.getcTag()))) { |
michael@8 | 176 | Log.d(TAG, "CTag has changed, something to synchronise"); |
michael@8 | 177 | if (serverCalendar.readCalendarEvents(facade)) { |
michael@8 | 178 | this.synchroniseEvents(androidCalendar, serverCalendar, syncResult.stats, notifyList); |
michael@8 | 179 | |
michael@8 | 180 | Log.d(TAG, "Updating stored CTag"); |
michael@8 | 181 | //serverCalendar.updateAndroidCalendar(androidCalendarUri, Calendar.CTAG, serverCalendar.getcTag()); |
michael@8 | 182 | androidCalendar.setCTag(serverCalendar.getcTag(), true); |
michael@8 | 183 | } else { |
michael@8 | 184 | Log.d(TAG, "unable to read events from server calendar"); |
michael@8 | 185 | } |
michael@8 | 186 | } else { |
michael@8 | 187 | Log.d(TAG, "CTag has not changed, nothing to do"); |
michael@0 | 188 | |
michael@0 | 189 | /* this is unnecessary. "SkippedEntries" are: |
michael@0 | 190 | * Counter for tracking how many entries, either from the server or the local store, |
michael@0 | 191 | * were ignored during the sync operation. This could happen if the SyncAdapter detected |
michael@0 | 192 | * some unparsable data but decided to skip it and move on rather than failing immediately. |
michael@0 | 193 | */ |
michael@0 | 194 | |
michael@0 | 195 | /*long CalendarID = ContentUris.parseId(androidCalendarUri); |
michael@0 | 196 | String selection = "(" + Events.CALENDAR_ID + " = ?)"; |
michael@0 | 197 | String[] selectionArgs = new String[] {String.valueOf(CalendarID)}; |
michael@0 | 198 | Cursor countCursor = provider.query(Events.CONTENT_URI, new String[] {"count(*) AS count"}, |
michael@0 | 199 | selection, |
michael@0 | 200 | selectionArgs, |
michael@0 | 201 | null); |
michael@0 | 202 | |
michael@0 | 203 | countCursor.moveToFirst(); |
michael@0 | 204 | int count = countCursor.getInt(0); |
michael@0 | 205 | syncResult.stats.numSkippedEntries += count; |
michael@0 | 206 | countCursor.close();*/ |
michael@8 | 207 | |
michael@8 | 208 | } |
michael@8 | 209 | |
michael@8 | 210 | this.checkDirtyAndroidEvents(provider, account, androidCalendarUri, facade, serverCalendar |
michael@8 | 211 | .getURI(), syncResult.stats, notifyList); |
michael@8 | 212 | } else { |
michael@8 | 213 | // this happens if the data provider failes to get an existing or create a new calendar |
michael@8 | 214 | mCountProviderFailed += 1; |
michael@8 | 215 | Log.e(TAG, "failed to get an existing or create a new calendar"); |
michael@8 | 216 | syncResult.stats.numIoExceptions += 1; |
michael@8 | 217 | if (mCountProviderFailed >= mCountProviderFailedMax) { |
michael@8 | 218 | // see issue #96 |
michael@8 | 219 | NotificationsHelper.signalSyncErrors(this.getContext(), "Caldav sync error (provider failed)", "are you using CyanogenMod in Incognito Mode?"); |
michael@8 | 220 | } else { |
michael@8 | 221 | NotificationsHelper.signalSyncErrors(this.getContext(), "Caldav sync error (provider failed)", "the provider failed to get an existing or create a new calendar"); |
michael@8 | 222 | } |
michael@8 | 223 | bolError = true; |
michael@8 | 224 | } |
michael@8 | 225 | } |
michael@8 | 226 | |
michael@8 | 227 | if (!bolError) { |
michael@8 | 228 | // check whether a calendar is not synced -> delete it at android |
michael@8 | 229 | androidCalList.deleteCalendarOnClientSideOnly(this.getContext()); |
michael@8 | 230 | } |
michael@8 | 231 | |
michael@8 | 232 | // notify the ContentResolver |
michael@8 | 233 | for (Uri uri : androidCalList.getNotifyList()) { |
michael@8 | 234 | this.getContext().getContentResolver().notifyChange(uri, null); |
michael@8 | 235 | } |
michael@8 | 236 | for (Uri uri : serverCalList.getNotifyList()) { |
michael@8 | 237 | this.getContext().getContentResolver().notifyChange(uri, null); |
michael@8 | 238 | } |
michael@8 | 239 | for (Uri uri : notifyList) { |
michael@8 | 240 | this.getContext().getContentResolver().notifyChange(uri, null); |
michael@8 | 241 | } |
michael@8 | 242 | |
michael@8 | 243 | //Log.i(TAG,"Statistiks for Calendar: " + serverCalendar.getURI().toString()); |
michael@8 | 244 | //Log.i(TAG,"Statistiks for AndroidCalendar: " + androidCalendar.getAndroidCalendarUri().toString()); |
michael@8 | 245 | Log.i(TAG, "Entries: " + String.valueOf(syncResult.stats.numEntries)); |
michael@8 | 246 | Log.i(TAG, "Rows inserted: " + String.valueOf(syncResult.stats.numInserts)); |
michael@8 | 247 | Log.i(TAG, "Rows updated: " + String.valueOf(syncResult.stats.numUpdates)); |
michael@8 | 248 | Log.i(TAG, "Rows deleted: " + String.valueOf(syncResult.stats.numDeletes)); |
michael@8 | 249 | Log.i(TAG, "Rows skipped: " + String.valueOf(syncResult.stats.numSkippedEntries)); |
michael@8 | 250 | Log.i(TAG, "Io Exceptions: " + String.valueOf(syncResult.stats.numIoExceptions)); |
michael@8 | 251 | Log.i(TAG, "Parse Exceptions: " + String.valueOf(syncResult.stats.numParseExceptions)); |
michael@8 | 252 | Log.i(TAG, "Auth Exceptions: " + String.valueOf(syncResult.stats.numAuthExceptions)); |
michael@8 | 253 | Log.i(TAG, "Conflict Detected Exceptions: " + String.valueOf(syncResult.stats.numConflictDetectedExceptions)); |
michael@0 | 254 | |
michael@0 | 255 | /*} catch (final AuthenticatorException e) { |
michael@0 | 256 | syncResult.stats.numParseExceptions++; |
michael@0 | 257 | Log.e(TAG, "AuthenticatorException", e);*/ |
michael@0 | 258 | /*} catch (final OperationCanceledException e) { |
michael@0 | 259 | Log.e(TAG, "OperationCanceledExcetpion", e);*/ |
michael@0 | 260 | } catch (final IOException e) { |
michael@0 | 261 | Log.e(TAG, "IOException", e); |
michael@0 | 262 | syncResult.stats.numIoExceptions++; |
michael@0 | 263 | NotificationsHelper.signalSyncErrors(this.getContext(), "Caldav sync error (IO)", e.getMessage()); |
michael@0 | 264 | //NotificationsHelper.getCurrentSyncLog().addException(e); |
michael@0 | 265 | /*} catch (final AuthenticationException e) { |
michael@0 | 266 | //mAccountManager.invalidateAuthToken(Constants.ACCOUNT_TYPE, authtoken); |
michael@0 | 267 | syncResult.stats.numAuthExceptions++; |
michael@0 | 268 | Log.e(TAG, "AuthenticationException", e);*/ |
michael@0 | 269 | } catch (final ParseException e) { |
michael@0 | 270 | syncResult.stats.numParseExceptions++; |
michael@0 | 271 | Log.e(TAG, "ParseException", e); |
michael@8 | 272 | NotificationsHelper.signalSyncErrors(this.getContext(), "Caldav sync error (parsing)", e |
michael@8 | 273 | .getMessage()); |
michael@0 | 274 | //NotificationsHelper.getCurrentSyncLog().addException(e); |
michael@0 | 275 | /*} catch (final JSONException e) { |
michael@0 | 276 | syncResult.stats.numParseExceptions++; |
michael@0 | 277 | Log.e(TAG, "JSONException", e);*/ |
michael@8 | 278 | } catch (Exception e) { |
michael@8 | 279 | Log.e(TAG, "Updating calendar exception " + e.getClass().getName(), e); |
michael@0 | 280 | syncResult.stats.numParseExceptions++; |
michael@8 | 281 | NotificationsHelper.signalSyncErrors(this.getContext(), "Caldav sync error (general)", e |
michael@8 | 282 | .getMessage()); |
michael@0 | 283 | //NotificationsHelper.getCurrentSyncLog().addException(e); |
michael@8 | 284 | //throw new RuntimeException(e); |
michael@8 | 285 | } |
michael@8 | 286 | } |
michael@0 | 287 | |
michael@8 | 288 | public void onSyncCanceled() { |
michael@8 | 289 | //TODO: implement SyncCanceled |
michael@8 | 290 | this.mCountSyncCanceled += 1; |
michael@8 | 291 | Log.v(TAG, "onSyncCanceled() count:" + String.valueOf(this.mCountSyncCanceled)); |
michael@8 | 292 | } |
michael@0 | 293 | |
michael@8 | 294 | |
michael@8 | 295 | /** |
michael@8 | 296 | * both calender event and android event have been found. |
michael@8 | 297 | * server wins always at the moment. |
michael@8 | 298 | * |
michael@8 | 299 | * @param androidCalendar |
michael@8 | 300 | * @param serverCalendar |
michael@8 | 301 | * @param stats |
michael@8 | 302 | * @param notifyList |
michael@8 | 303 | * @throws ClientProtocolException |
michael@8 | 304 | * @throws URISyntaxException |
michael@8 | 305 | * @throws IOException |
michael@8 | 306 | * @throws ParserConfigurationException |
michael@8 | 307 | * @throws SAXException |
michael@8 | 308 | * @throws RemoteException |
michael@8 | 309 | * @throws CaldavProtocolException |
michael@8 | 310 | * @throws ParserException |
michael@8 | 311 | * @see SyncAdapter#updateAndroidEvent(ContentProviderClient, Account, AndroidEvent, CalendarEvent) |
michael@8 | 312 | * @see SyncAdapter#tagAndroidEvent(ContentProviderClient, Account, AndroidEvent) |
michael@8 | 313 | * @see SyncAdapter#untagAndroidEvents(ContentProviderClient, Account, Uri) |
michael@8 | 314 | * @see SyncAdapter#deleteUntaggedEvents(ContentProviderClient, Account, Uri) |
michael@8 | 315 | */ |
michael@8 | 316 | private void synchroniseEvents( |
michael@8 | 317 | DavCalendar androidCalendar, |
michael@8 | 318 | DavCalendar serverCalendar, |
michael@8 | 319 | SyncStats stats, |
michael@8 | 320 | ArrayList<Uri> notifyList |
michael@8 | 321 | ) throws ClientProtocolException, URISyntaxException, IOException, ParserConfigurationException, SAXException, RemoteException, CaldavProtocolException, ParserException { |
michael@0 | 322 | |
michael@0 | 323 | /*if (DROP_CALENDAR_EVENTS) { |
michael@0 | 324 | dropAllEvents(account, provider, androidCalendar.getAndroidCalendarUri()); |
michael@0 | 325 | }*/ |
michael@8 | 326 | |
michael@8 | 327 | int rowInsert = 0; |
michael@8 | 328 | int rowUpdate = 0; |
michael@8 | 329 | int rowTag = 0; |
michael@8 | 330 | int rowDelete = 0; |
michael@8 | 331 | int rowUntag = 0; |
michael@8 | 332 | int rowSkip = 0; |
michael@8 | 333 | |
michael@8 | 334 | for (CalendarEvent calendarEvent : serverCalendar.getCalendarEvents()) { |
michael@8 | 335 | try { |
michael@8 | 336 | AndroidEvent androidEvent = calendarEvent.getAndroidEvent(androidCalendar); |
michael@8 | 337 | |
michael@8 | 338 | Log.i(TAG, "Event " + calendarEvent.getUri() |
michael@8 | 339 | .toString() + " androidUri=" + androidEvent); |
michael@8 | 340 | |
michael@8 | 341 | if (androidEvent == null) { |
michael@0 | 342 | /* new android event */ |
michael@8 | 343 | if (calendarEvent.createAndroidEvent(androidCalendar)) { |
michael@8 | 344 | rowInsert += 1; |
michael@8 | 345 | androidEvent = calendarEvent.getAndroidEvent(androidCalendar); |
michael@8 | 346 | notifyList.add(androidEvent.getUri()); |
michael@8 | 347 | } else { |
michael@8 | 348 | rowSkip += 1; |
michael@8 | 349 | } |
michael@8 | 350 | } else { |
michael@0 | 351 | /* the android exists */ |
michael@8 | 352 | String androidETag = androidEvent.getETag(); |
michael@8 | 353 | if (androidETag == null) |
michael@8 | 354 | androidETag = ""; |
michael@8 | 355 | Log.d(TAG, "Event compare: " + androidETag + " <> " + calendarEvent.getETag() |
michael@8 | 356 | .toString()); |
michael@8 | 357 | if ((androidEvent.getETag() == null) || (!androidETag.equals(calendarEvent.getETag()))) { |
michael@0 | 358 | /* the android event is getting updated */ |
michael@8 | 359 | if (calendarEvent.updateAndroidEvent(androidEvent)) { |
michael@8 | 360 | rowUpdate += 1; |
michael@8 | 361 | notifyList.add(androidEvent.getUri()); |
michael@8 | 362 | } else { |
michael@8 | 363 | rowSkip += 1; |
michael@8 | 364 | } |
michael@8 | 365 | } |
michael@8 | 366 | } |
michael@8 | 367 | if (androidEvent != null) |
michael@8 | 368 | //if (androidEvent.tagAndroidEvent()) |
michael@8 | 369 | if (androidCalendar.tagAndroidEvent(androidEvent)) |
michael@8 | 370 | rowTag += 1; |
michael@0 | 371 | |
michael@0 | 372 | |
michael@8 | 373 | } catch (ParserException ex) { |
michael@8 | 374 | Log.e(TAG, "Parser exception", ex); |
michael@8 | 375 | stats.numParseExceptions++; |
michael@8 | 376 | |
michael@8 | 377 | NotificationsHelper.signalSyncErrors(getContext(), "Caldav sync error (parsing)", ex |
michael@8 | 378 | .getMessage()); |
michael@8 | 379 | //NotificationsHelper.getCurrentSyncLog().addException(ex); |
michael@8 | 380 | } catch (CaldavProtocolException ex) { |
michael@8 | 381 | Log.e(TAG, "Caldav exception", ex); |
michael@8 | 382 | stats.numParseExceptions++; |
michael@8 | 383 | |
michael@8 | 384 | NotificationsHelper.signalSyncErrors(getContext(), "Caldav sync error (caldav)", ex.getMessage()); |
michael@8 | 385 | //NotificationsHelper.getCurrentSyncLog().addException(ex); |
michael@8 | 386 | } |
michael@8 | 387 | } |
michael@8 | 388 | |
michael@8 | 389 | rowDelete = androidCalendar.deleteUntaggedEvents(); |
michael@8 | 390 | rowUntag = androidCalendar.untagAndroidEvents(); |
michael@0 | 391 | |
michael@0 | 392 | /*Log.i(TAG,"Statistiks for Calendar: " + serverCalendar.getURI().toString()); |
michael@0 | 393 | Log.i(TAG,"Statistiks for AndroidCalendar: " + androidCalendar.getAndroidCalendarUri().toString()); |
michael@0 | 394 | Log.i(TAG,"Rows inserted: " + String.valueOf(rowInsert)); |
michael@0 | 395 | Log.i(TAG,"Rows updated: " + String.valueOf(rowUpdate)); |
michael@0 | 396 | Log.i(TAG,"Rows deleted: " + String.valueOf(rowDelete)); |
michael@0 | 397 | Log.i(TAG,"Rows skipped: " + String.valueOf(rowSkip));*/ |
michael@8 | 398 | Log.i(TAG, "Rows tagged: " + String.valueOf(rowTag)); |
michael@8 | 399 | Log.i(TAG, "Rows untagged: " + String.valueOf(rowUntag)); |
michael@0 | 400 | |
michael@8 | 401 | stats.numInserts += rowInsert; |
michael@8 | 402 | stats.numUpdates += rowUpdate; |
michael@8 | 403 | stats.numDeletes += rowDelete; |
michael@8 | 404 | stats.numSkippedEntries += rowSkip; |
michael@8 | 405 | stats.numEntries += rowInsert + rowUpdate + rowDelete; |
michael@0 | 406 | |
michael@8 | 407 | } |
michael@0 | 408 | |
michael@8 | 409 | /** |
michael@8 | 410 | * checks the android events for the dirty flag. |
michael@8 | 411 | * the flag is set by android when the event has been changed. |
michael@8 | 412 | * the dirty flag is removed when an android event has been updated from calendar event |
michael@8 | 413 | * |
michael@8 | 414 | * @param provider |
michael@8 | 415 | * @param account |
michael@8 | 416 | * @param calendarUri |
michael@8 | 417 | * @param facade |
michael@8 | 418 | * @param caldavCalendarUri |
michael@8 | 419 | * @param stats |
michael@8 | 420 | * @param notifyList |
michael@8 | 421 | * @return count of dirty events |
michael@8 | 422 | */ |
michael@8 | 423 | private int checkDirtyAndroidEvents( |
michael@8 | 424 | ContentProviderClient provider, |
michael@8 | 425 | Account account, |
michael@8 | 426 | Uri calendarUri, |
michael@8 | 427 | CaldavFacade facade, |
michael@8 | 428 | URI caldavCalendarUri, |
michael@8 | 429 | SyncStats stats, |
michael@8 | 430 | ArrayList<Uri> notifyList |
michael@8 | 431 | ) { |
michael@8 | 432 | Cursor curEvent = null; |
michael@8 | 433 | Cursor curAttendee = null; |
michael@8 | 434 | Cursor curReminder = null; |
michael@8 | 435 | Long EventID; |
michael@8 | 436 | Long CalendarID; |
michael@8 | 437 | AndroidEvent androidEvent = null; |
michael@8 | 438 | int rowDirty = 0; |
michael@8 | 439 | int rowInsert = 0; |
michael@8 | 440 | int rowUpdate = 0; |
michael@8 | 441 | int rowDelete = 0; |
michael@0 | 442 | |
michael@8 | 443 | try { |
michael@8 | 444 | CalendarID = ContentUris.parseId(calendarUri); |
michael@8 | 445 | String selection = "(" + Events.DIRTY + " = ?) AND (" + Events.CALENDAR_ID + " = ?)"; |
michael@8 | 446 | String[] selectionArgs = new String[]{"1", CalendarID.toString()}; |
michael@8 | 447 | curEvent = provider.query(Events.CONTENT_URI, null, selection, selectionArgs, null); |
michael@0 | 448 | |
michael@8 | 449 | while (curEvent.moveToNext()) { |
michael@8 | 450 | EventID = curEvent.getLong(curEvent.getColumnIndex(Events._ID)); |
michael@8 | 451 | Uri returnedUri = ContentUris.withAppendedId(Events.CONTENT_URI, EventID); |
michael@8 | 452 | |
michael@8 | 453 | //androidEvent = new AndroidEvent(account, provider, returnedUri, calendarUri); |
michael@8 | 454 | androidEvent = new AndroidEvent(returnedUri, calendarUri); |
michael@8 | 455 | androidEvent.readContentValues(curEvent); |
michael@8 | 456 | |
michael@8 | 457 | selection = "(" + Attendees.EVENT_ID + " = ?)"; |
michael@8 | 458 | selectionArgs = new String[]{String.valueOf(EventID)}; |
michael@8 | 459 | curAttendee = provider.query(Attendees.CONTENT_URI, null, selection, selectionArgs, null); |
michael@8 | 460 | selection = "(" + Reminders.EVENT_ID + " = ?)"; |
michael@8 | 461 | selectionArgs = new String[]{String.valueOf(EventID)}; |
michael@8 | 462 | curReminder = provider.query(Reminders.CONTENT_URI, null, selection, selectionArgs, null); |
michael@8 | 463 | androidEvent.readAttendees(curAttendee); |
michael@8 | 464 | androidEvent.readReminder(curReminder); |
michael@8 | 465 | curAttendee.close(); |
michael@8 | 466 | curReminder.close(); |
michael@8 | 467 | |
michael@8 | 468 | String SyncID = androidEvent.ContentValues.getAsString(Events._SYNC_ID); |
michael@8 | 469 | |
michael@8 | 470 | boolean Deleted = false; |
michael@8 | 471 | int intDeleted = 0; |
michael@8 | 472 | intDeleted = curEvent.getInt(curEvent.getColumnIndex(Events.DELETED)); |
michael@8 | 473 | Deleted = (intDeleted == 1); |
michael@8 | 474 | |
michael@8 | 475 | if (SyncID == null) { |
michael@8 | 476 | // new Android event |
michael@8 | 477 | String newGUID = java.util.UUID.randomUUID().toString() + "-caldavsyncadapter"; |
michael@8 | 478 | String calendarPath = caldavCalendarUri.getPath(); |
michael@8 | 479 | if (!calendarPath.endsWith("/")) |
michael@8 | 480 | calendarPath += "/"; |
michael@8 | 481 | |
michael@8 | 482 | SyncID = calendarPath + newGUID + ".ics"; |
michael@8 | 483 | |
michael@8 | 484 | androidEvent.createIcs(newGUID); |
michael@8 | 485 | |
michael@8 | 486 | if (facade.createEvent(URI.create(SyncID), androidEvent.getIcsEvent() |
michael@8 | 487 | .toString())) { |
michael@8 | 488 | //HINT: bugfix for google calendar replace("@", "%40") |
michael@8 | 489 | if (SyncID.contains("@")) |
michael@8 | 490 | SyncID = SyncID.replace("@", "%40"); |
michael@8 | 491 | ContentValues values = new ContentValues(); |
michael@8 | 492 | values.put(Events._SYNC_ID, SyncID); |
michael@8 | 493 | |
michael@8 | 494 | //google doesn't send the etag after creation |
michael@8 | 495 | //HINT: my SabreDAV send always the same etag after putting a new event |
michael@8 | 496 | //String LastETag = facade.getLastETag(); |
michael@8 | 497 | //if (!LastETag.equals("")) { |
michael@8 | 498 | // values.put(Event.ETAG, LastETag); |
michael@8 | 499 | //} else { |
michael@8 | 500 | //so get the etag with a new REPORT |
michael@8 | 501 | CalendarEvent calendarEvent = new CalendarEvent(account, provider); |
michael@8 | 502 | calendarEvent.calendarURL = caldavCalendarUri.toURL(); |
michael@8 | 503 | URI SyncURI = new URI(SyncID); |
michael@8 | 504 | calendarEvent.setUri(SyncURI); |
michael@8 | 505 | CaldavFacade.getEvent(calendarEvent); |
michael@8 | 506 | values.put(Event.ETAG, calendarEvent.getETag()); |
michael@8 | 507 | //} |
michael@8 | 508 | values.put(Event.UID, newGUID); |
michael@8 | 509 | values.put(Events.DIRTY, 0); |
michael@8 | 510 | values.put(Event.RAWDATA, androidEvent.getIcsEvent().toString()); |
michael@8 | 511 | |
michael@8 | 512 | int rowCount = provider.update(asSyncAdapter(androidEvent.getUri(), account.name, account.type), values, null, null); |
michael@8 | 513 | if (rowCount == 1) { |
michael@8 | 514 | rowInsert += 1; |
michael@8 | 515 | notifyList.add(androidEvent.getUri()); |
michael@8 | 516 | } |
michael@8 | 517 | } |
michael@8 | 518 | } else if (Deleted) { |
michael@8 | 519 | // deleted Android event |
michael@8 | 520 | if (facade.deleteEvent(URI.create(SyncID), androidEvent.getETag())) { |
michael@8 | 521 | String mSelectionClause = "(" + Events._ID + "= ?)"; |
michael@8 | 522 | String[] mSelectionArgs = {String.valueOf(EventID)}; |
michael@8 | 523 | |
michael@8 | 524 | int countDeleted = provider.delete(asSyncAdapter(Events.CONTENT_URI, account.name, account.type), mSelectionClause, mSelectionArgs); |
michael@8 | 525 | |
michael@8 | 526 | if (countDeleted == 1) { |
michael@8 | 527 | rowDelete += 1; |
michael@8 | 528 | notifyList.add(androidEvent.getUri()); |
michael@8 | 529 | } |
michael@8 | 530 | } |
michael@8 | 531 | } else { |
michael@8 | 532 | //update the android event to the server |
michael@8 | 533 | String uid = androidEvent.getUID(); |
michael@8 | 534 | if ((uid == null) || (uid.equals(""))) { |
michael@8 | 535 | //COMPAT: this is needed because in the past, the UID was not stored in the android event |
michael@8 | 536 | CalendarEvent calendarEvent = new CalendarEvent(account, provider); |
michael@8 | 537 | URI syncURI = new URI(SyncID); |
michael@8 | 538 | calendarEvent.setUri(syncURI); |
michael@8 | 539 | calendarEvent.calendarURL = caldavCalendarUri.toURL(); |
michael@8 | 540 | if (calendarEvent.fetchBody()) { |
michael@8 | 541 | calendarEvent.readContentValues(); |
michael@8 | 542 | uid = calendarEvent.getUID(); |
michael@8 | 543 | } |
michael@8 | 544 | } |
michael@8 | 545 | if (uid != null) { |
michael@8 | 546 | androidEvent.createIcs(uid); |
michael@8 | 547 | |
michael@8 | 548 | if (facade.updateEvent(URI.create(SyncID), androidEvent.getIcsEvent() |
michael@8 | 549 | .toString(), androidEvent.getETag())) { |
michael@8 | 550 | selection = "(" + Events._ID + "= ?)"; |
michael@8 | 551 | selectionArgs = new String[]{EventID.toString()}; |
michael@8 | 552 | androidEvent.ContentValues.put(Events.DIRTY, 0); |
michael@8 | 553 | |
michael@8 | 554 | //google doesn't send the etag after update |
michael@8 | 555 | String LastETag = facade.getLastETag(); |
michael@8 | 556 | if (!LastETag.equals("")) { |
michael@8 | 557 | androidEvent.ContentValues.put(Event.ETAG, LastETag); |
michael@8 | 558 | } else { |
michael@8 | 559 | //so get the etag with a new REPORT |
michael@8 | 560 | CalendarEvent calendarEvent = new CalendarEvent(account, provider); |
michael@8 | 561 | calendarEvent.calendarURL = caldavCalendarUri.toURL(); |
michael@8 | 562 | URI SyncURI = new URI(SyncID); |
michael@8 | 563 | calendarEvent.setUri(SyncURI); |
michael@8 | 564 | CaldavFacade.getEvent(calendarEvent); |
michael@8 | 565 | androidEvent.ContentValues.put(Event.ETAG, calendarEvent.getETag()); |
michael@8 | 566 | } |
michael@8 | 567 | androidEvent.ContentValues.put(Event.RAWDATA, androidEvent.getIcsEvent() |
michael@8 | 568 | .toString()); |
michael@8 | 569 | int RowCount = provider.update(asSyncAdapter(androidEvent.getUri(), account.name, account.type), androidEvent.ContentValues, null, null); |
michael@8 | 570 | |
michael@8 | 571 | if (RowCount == 1) { |
michael@8 | 572 | rowUpdate += 1; |
michael@8 | 573 | notifyList.add(androidEvent.getUri()); |
michael@8 | 574 | } |
michael@8 | 575 | } else { |
michael@8 | 576 | rowDirty += 1; |
michael@8 | 577 | } |
michael@8 | 578 | } else { |
michael@8 | 579 | rowDirty += 1; |
michael@8 | 580 | } |
michael@8 | 581 | } |
michael@8 | 582 | } |
michael@8 | 583 | curEvent.close(); |
michael@0 | 584 | |
michael@0 | 585 | /*if ((rowInsert > 0) || (rowUpdate > 0) || (rowDelete > 0) || (rowDirty > 0)) { |
michael@0 | 586 | Log.i(TAG,"Android Rows inserted: " + String.valueOf(rowInsert)); |
michael@0 | 587 | Log.i(TAG,"Android Rows updated: " + String.valueOf(rowUpdate)); |
michael@0 | 588 | Log.i(TAG,"Android Rows deleted: " + String.valueOf(rowDelete)); |
michael@0 | 589 | Log.i(TAG,"Android Rows dirty: " + String.valueOf(rowDirty)); |
michael@0 | 590 | }*/ |
michael@8 | 591 | |
michael@8 | 592 | stats.numInserts += rowInsert; |
michael@8 | 593 | stats.numUpdates += rowUpdate; |
michael@8 | 594 | stats.numDeletes += rowDelete; |
michael@8 | 595 | stats.numSkippedEntries += rowDirty; |
michael@8 | 596 | stats.numEntries += rowInsert + rowUpdate + rowDelete; |
michael@8 | 597 | } catch (RemoteException e) { |
michael@8 | 598 | e.printStackTrace(); |
michael@8 | 599 | } catch (URISyntaxException e) { |
michael@8 | 600 | // TODO Automatisch generierter Erfassungsblock |
michael@8 | 601 | e.printStackTrace(); |
michael@8 | 602 | } catch (ClientProtocolException e) { |
michael@8 | 603 | // TODO Automatisch generierter Erfassungsblock |
michael@8 | 604 | e.printStackTrace(); |
michael@8 | 605 | } catch (IOException e) { |
michael@8 | 606 | // TODO Automatisch generierter Erfassungsblock |
michael@8 | 607 | e.printStackTrace(); |
michael@8 | 608 | } catch (CaldavProtocolException e) { |
michael@8 | 609 | // TODO Automatisch generierter Erfassungsblock |
michael@8 | 610 | e.printStackTrace(); |
michael@8 | 611 | } catch (ParserException e) { |
michael@8 | 612 | // TODO Automatisch generierter Erfassungsblock |
michael@8 | 613 | e.printStackTrace(); |
michael@8 | 614 | } |
michael@8 | 615 | |
michael@8 | 616 | return rowDirty; |
michael@8 | 617 | } |
michael@0 | 618 | |
michael@0 | 619 | /* private Account UpgradeAccount(Account OldAccount) { |
michael@0 | 620 | String Username = OldAccount.name; |
michael@0 | 621 | String Type = OldAccount.type; |
michael@0 | 622 | String Password = this.mAccountManager.getPassword(OldAccount); |
michael@0 | 623 | String Url = this.mAccountManager.getUserData(OldAccount, AuthenticatorActivity.USER_DATA_URL_KEY); |
michael@0 | 624 | |
michael@0 | 625 | Account NewAccount = new Account(Username + AuthenticatorActivity.ACCOUNT_NAME_SPLITTER + Url, Type); |
michael@0 | 626 | if (this.mAccountManager.addAccountExplicitly(NewAccount, Password, null)) { |
michael@0 | 627 | this.mAccountManager.setUserData(NewAccount, AuthenticatorActivity.USER_DATA_URL_KEY, Url); |
michael@0 | 628 | this.mAccountManager.setUserData(NewAccount, AuthenticatorActivity.USER_DATA_USERNAME, Username); |
michael@0 | 629 | } |
michael@0 | 630 | this.mAccountManager.removeAccount(OldAccount, null, null); |
michael@0 | 631 | |
michael@0 | 632 | return NewAccount; |
michael@0 | 633 | }*/ |
michael@0 | 634 | |
michael@0 | 635 | /* private void dropAllEvents(Account account, ContentProviderClient provider, Uri calendarUri) throws RemoteException { |
michael@0 | 636 | |
michael@0 | 637 | Log.i(TAG, "Deleting all calendar events for "+calendarUri); |
michael@0 | 638 | |
michael@0 | 639 | String selection = "(" + Events.CALENDAR_ID + " = ?)"; |
michael@0 | 640 | String[] selectionArgs = new String[] {Long.toString(ContentUris.parseId(calendarUri))}; |
michael@0 | 641 | |
michael@0 | 642 | provider.delete(asSyncAdapter(Events.CONTENT_URI, account.name, account.type), |
michael@0 | 643 | selection, selectionArgs); |
michael@0 | 644 | |
michael@0 | 645 | }*/ |
michael@8 | 646 | |
michael@8 | 647 | private static Uri asSyncAdapter(Uri uri, String account, String accountType) { |
michael@8 | 648 | return uri.buildUpon() |
michael@8 | 649 | .appendQueryParameter(android.provider.CalendarContract.CALLER_IS_SYNCADAPTER, "true") |
michael@8 | 650 | .appendQueryParameter(Calendars.ACCOUNT_NAME, account) |
michael@8 | 651 | .appendQueryParameter(Calendars.ACCOUNT_TYPE, accountType).build(); |
michael@8 | 652 | } |
michael@0 | 653 | |
michael@0 | 654 | } |
michael@0 | 655 |