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