Tue, 10 Feb 2015 18:12:00 +0100
Import initial revisions of existing project AndroidCaldavSyncAdapater,
forked from upstream repository at 27e8a0f8495c92e0780d450bdf0c7cec77a03a55.
1 /**
2 * Copyright (c) 2012-2013, Gerald Garcia, Timo Berger
3 *
4 * This file is part of Andoid Caldav Sync Adapter Free.
5 *
6 * Andoid Caldav Sync Adapter Free is free software: you can redistribute
7 * it and/or modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation, either version 3 of the
9 * License, or at your option any later version.
10 *
11 * Andoid Caldav Sync Adapter Free is distributed in the hope that
12 * it will be useful, but WITHOUT ANY WARRANTY; without even the implied
13 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with Andoid Caldav Sync Adapter Free.
18 * If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
22 package org.gege.caldavsyncadapter.caldav.entities;
24 import java.io.IOException;
25 import java.io.StringReader;
26 import java.io.UnsupportedEncodingException;
27 import java.net.URI;
28 import java.net.URL;
29 import java.net.URLDecoder;
30 import java.text.ParseException;
31 import java.util.ArrayList;
33 import javax.xml.parsers.ParserConfigurationException;
34 import javax.xml.parsers.SAXParser;
35 import javax.xml.parsers.SAXParserFactory;
37 import net.fortuna.ical4j.data.CalendarBuilder;
38 import net.fortuna.ical4j.data.ParserException;
39 import net.fortuna.ical4j.model.Calendar;
40 import net.fortuna.ical4j.model.Component;
41 import net.fortuna.ical4j.model.ComponentList;
42 import net.fortuna.ical4j.model.DateTime;
43 import net.fortuna.ical4j.model.Dur;
44 import net.fortuna.ical4j.model.Parameter;
45 import net.fortuna.ical4j.model.ParameterList;
46 import net.fortuna.ical4j.model.Property;
47 import net.fortuna.ical4j.model.PropertyList;
48 import net.fortuna.ical4j.model.TimeZone;
49 import net.fortuna.ical4j.model.component.VEvent;
50 import net.fortuna.ical4j.model.component.VTimeZone;
51 import net.fortuna.ical4j.model.parameter.Cn;
52 import net.fortuna.ical4j.model.parameter.CuType;
53 import net.fortuna.ical4j.model.parameter.PartStat;
54 import net.fortuna.ical4j.model.parameter.Role;
55 import net.fortuna.ical4j.model.property.Clazz;
56 import net.fortuna.ical4j.model.property.Status;
57 import net.fortuna.ical4j.util.CompatibilityHints;
59 import org.apache.http.client.ClientProtocolException;
60 import org.gege.caldavsyncadapter.Event;
61 import org.gege.caldavsyncadapter.android.entities.AndroidEvent;
62 import org.gege.caldavsyncadapter.caldav.CaldavFacade;
63 import org.gege.caldavsyncadapter.caldav.CaldavProtocolException;
64 import org.gege.caldavsyncadapter.caldav.xml.MultiStatusHandler;
65 import org.gege.caldavsyncadapter.caldav.xml.sax.MultiStatus;
66 import org.gege.caldavsyncadapter.caldav.xml.sax.Prop;
67 import org.gege.caldavsyncadapter.caldav.xml.sax.PropStat;
68 import org.gege.caldavsyncadapter.caldav.xml.sax.Response;
69 import org.gege.caldavsyncadapter.syncadapter.SyncAdapter;
70 import org.xml.sax.InputSource;
71 import org.xml.sax.SAXException;
72 import org.xml.sax.XMLReader;
74 import android.accounts.Account;
75 import android.content.ContentProviderClient;
76 import android.content.ContentUris;
77 import android.content.ContentValues;
78 import android.content.SyncStats;
79 import android.database.Cursor;
80 import android.net.Uri;
81 import android.os.RemoteException;
82 import android.provider.CalendarContract.Attendees;
83 import android.provider.CalendarContract.Calendars;
84 import android.provider.CalendarContract.Events;
85 import android.provider.CalendarContract.Reminders;
86 import android.util.Log;
90 public class CalendarEvent extends org.gege.caldavsyncadapter.Event {
91 private static final String TAG = "CalendarEvent";
93 private String stringIcs;
95 private Calendar calendar;
97 private Component calendarComponent;
99 private String eTag;
100 private URI muri;
101 private Uri mAndroidEventUri;
102 public URL calendarURL;
104 private boolean mAllDay = false;
105 private VTimeZone mVTimeZone = null;
106 private TimeZone mTimeZone = null;
108 private String mstrTimeZoneStart = "";
109 private String mstrTimeZoneEnd = "";
111 private Account mAccount = null;
112 private ContentProviderClient mProvider = null;
114 public CalendarEvent(Account account, ContentProviderClient provider) {
115 this.mAccount = account;
116 this.mProvider = provider;
117 }
119 public String getETag() {
120 return eTag;
121 }
123 public void setETag(String eTag) {
124 this.eTag = eTag;
125 }
127 public URI getUri() {
128 return muri;
129 }
131 public void setUri(URI uri) {
132 this.muri = uri;
133 }
135 public void setICSasString(String ics) {
136 this.stringIcs = ics;
137 }
139 public boolean setICSasMultiStatus(String stringMultiStatus) {
140 boolean Result = false;
141 String ics = "";
142 MultiStatus multistatus;
143 ArrayList<Response> responselist;
144 Response response;
145 PropStat propstat;
146 Prop prop;
147 try {
148 SAXParserFactory factory = SAXParserFactory.newInstance();
149 SAXParser parser = factory.newSAXParser();
150 XMLReader reader = parser.getXMLReader();
151 MultiStatusHandler contentHandler = new MultiStatusHandler();
152 reader.setContentHandler(contentHandler);
153 reader.parse(new InputSource(new StringReader(stringMultiStatus)));
155 multistatus = contentHandler.mMultiStatus;
156 if (multistatus != null) {
157 responselist = multistatus.ResponseList;
158 if (responselist.size() == 1) {
159 response = responselist.get(0);
160 //HINT: bugfix for google calendar, zimbra replace("@", "%40")
161 if (response.href.replace("@", "%40").equals(this.getUri().getRawPath().replace("@", "%40"))) {
162 propstat = response.propstat;
163 if (propstat != null) {
164 if (propstat.status.contains("200 OK")) {
165 prop = propstat.prop;
166 ics = prop.calendardata;
167 this.setETag(prop.getetag);
168 Result = true;
169 }
170 }
171 }
172 }
173 }
174 } catch (ParserConfigurationException e1) {
175 e1.printStackTrace();
176 } catch (SAXException e1) {
177 e1.printStackTrace();
178 } catch (IOException e) {
179 e.printStackTrace();
180 }
182 this.stringIcs = ics;
183 return Result;
184 }
186 /**
187 * sets the Uri of the android event
188 * @param uri
189 * @see org.gege.caldavsyncadapter.syncadapter.SyncAdapter#createAndroidEvent(ContentProviderClient provider, Account account, Uri calendarUri, CalendarEvent calendarEvent)
190 * @see org.gege.caldavsyncadapter.syncadapter.SyncAdapter#updateAndroidEvent(ContentProviderClient provider, Account account, AndroidEvent androidEvent, CalendarEvent calendarEvent)
191 */
192 public void setAndroidEventUri(Uri uri) {
193 mAndroidEventUri = uri;
194 }
196 /**
197 * gets the Uri of the android event
198 * @return
199 */
200 public Uri getAndroidEventUri() {
201 return mAndroidEventUri;
202 }
205 /**
206 * reads all ContentValues from the caldav source
207 * @param calendarUri
208 * @return
209 * @see org.gege.caldavsyncadapter.syncadapter.SyncAdapter#createAndroidEvent(ContentProviderClient provider, Account account, Uri calendarUri, CalendarEvent calendarEvent)
210 * @see org.gege.caldavsyncadapter.syncadapter.SyncAdapter#updateAndroidEvent(ContentProviderClient provider, Account account, AndroidEvent androidEvent, CalendarEvent calendarEvent)
211 */
212 public boolean readContentValues() {
213 this.ContentValues.put(Events.DTSTART, this.getStartTime());
214 this.ContentValues.put(Events.EVENT_TIMEZONE, this.getTimeZoneStart());
216 //if (this.getRRule().isEmpty() && this.getRDate().isEmpty()) {
217 if (this.getRRule() == null && this.getRDate() == null) {
218 //if (AllDay.equals(1)) //{
219 // values.put(Events.ALL_DAY, AllDay);
220 //} else {
221 this.ContentValues.put(Events.DTEND, this.getEndTime());
222 this.ContentValues.put(Events.EVENT_END_TIMEZONE, this.getTimeZoneEnd());
223 //}
224 } else {
225 //if (AllDay.equals(1))
226 // values.put(Events.ALL_DAY, AllDay);
227 this.ContentValues.put(Events.DURATION, this.getDuration());
228 }
229 int AllDay = this.getAllDay();
230 this.ContentValues.put(Events.ALL_DAY, AllDay);
232 this.ContentValues.put(Events.TITLE, this.getTitle());
233 //this.ContentValues.put(Events.CALENDAR_ID, ContentUris.parseId(calendarUri));
234 this.ContentValues.put(Events._SYNC_ID, this.getUri().toString());
235 this.ContentValues.put(ETAG, this.getETag());
236 this.ContentValues.put(Events.DESCRIPTION, this.getDescription());
237 this.ContentValues.put(Events.EVENT_LOCATION, this.getLocation());
238 this.ContentValues.put(Events.ACCESS_LEVEL, this.getAccessLevel());
239 this.ContentValues.put(Events.STATUS, this.getStatus());
240 this.ContentValues.put(Events.RDATE, this.getRDate());
241 this.ContentValues.put(Events.RRULE, this.getRRule());
242 this.ContentValues.put(Events.EXRULE, this.getExRule());
243 this.ContentValues.put(Events.EXDATE, this.getExDate());
244 this.ContentValues.put(UID, this.getUid());
245 this.ContentValues.put(RAWDATA, this.stringIcs);
247 return true;
248 }
250 /**
251 * receives a single event and parses its content
252 * @return success of this function
253 * @see CalendarEvent#parseIcs()
254 * @throws ClientProtocolException
255 * @throws IOException
256 * @throws CaldavProtocolException
257 * @throws ParserException
258 */
259 public boolean fetchBody() throws ClientProtocolException, IOException, CaldavProtocolException, ParserException {
260 boolean Error = false;
262 //replaced fetchEvent() with getEvent()
263 //CaldavFacade.fetchEvent(this);
264 CaldavFacade.getEvent(this);
266 boolean Parse = this.parseIcs();
267 if (!Parse)
268 Error = true;
270 return !Error;
271 }
273 public java.util.ArrayList<ContentValues> getReminders() {
274 java.util.ArrayList<ContentValues> Result = new java.util.ArrayList<ContentValues>();
275 ContentValues Reminder;
277 /*
278 * http://sourceforge.net/tracker/?func=detail&aid=3021704&group_id=107024&atid=646395
279 */
281 net.fortuna.ical4j.model.component.VEvent event = (VEvent) this.calendarComponent;
283 //ComponentList ComList = this.calendar.getComponents(Component.VALARM);
284 ComponentList ComList = event.getAlarms();
286 if (ComList != null) {
287 for (Object objCom : ComList) {
288 Component Com = (Component) objCom;
289 Reminder = new ContentValues();
291 //Property ACTION = Com.getProperty("ACTION");
292 Property TRIGGER = Com.getProperty("TRIGGER");
293 if (TRIGGER != null) {
294 Dur Duration = new Dur(TRIGGER.getValue());
295 //if (ACTION.getValue().equals("DISPLAY"))
297 int intDuration = Duration.getMinutes() + Duration.getHours() * 60 + Duration.getDays() * 60 * 24;
299 Reminder.put(Reminders.EVENT_ID, ContentUris.parseId(mAndroidEventUri));
300 Reminder.put(Reminders.METHOD, Reminders.METHOD_ALERT);
301 Reminder.put(Reminders.MINUTES, intDuration);
303 Result.add(Reminder);
304 }
305 }
306 }
307 return Result;
308 }
310 public java.util.ArrayList<ContentValues> getAttandees() {
311 java.util.ArrayList<ContentValues> Result = new java.util.ArrayList<ContentValues>();
312 ContentValues Attendee;
313 PropertyList Propertys = calendarComponent.getProperties(Property.ATTENDEE);
314 if (Propertys != null) {
315 for (Object objProperty : Propertys){
316 Property property = (Property) objProperty;
317 Attendee = ReadAttendeeProperties(property,Property.ATTENDEE);
318 if (Attendee != null)
319 Result.add(Attendee);
320 }
321 }
322 Propertys = calendarComponent.getProperties(Property.ORGANIZER);
323 if (Propertys != null) {
324 for (Object objProperty : Propertys){
325 Property property = (Property) objProperty;
326 Attendee = ReadAttendeeProperties(property,Property.ORGANIZER);
327 if (Attendee != null)
328 Result.add(Attendee);
329 }
330 }
333 return Result;
334 }
336 private String mstrcIcalPropertyError = "net.fortunal.ical4j.invalid:";
337 private ContentValues ReadAttendeeProperties(Property property, String Type) {
338 ContentValues Attendee = null;
340 ParameterList Parameters = property.getParameters();
341 Parameter CN = Parameters.getParameter(Cn.CN);
342 Parameter ROLE = Parameters.getParameter(Role.ROLE);
343 Parameter CUTYPE = Parameters.getParameter(CuType.CUTYPE);
344 //Parameter RSVP = Parameters.getParameter("RSVP");
345 Parameter PARTSTAT = Parameters.getParameter(PartStat.PARTSTAT);
347 String strCN = "";
348 String strROLE = "";
349 String strCUTYPE = "";
350 String strValue = property.getValue().replace("mailto:", "");
351 String strPARTSTAT = "";
353 if (strValue.startsWith(mstrcIcalPropertyError)) {
354 strValue = strValue.replace(mstrcIcalPropertyError, "");
355 try {
356 strValue = URLDecoder.decode(strValue, "UTF-8");
357 } catch (UnsupportedEncodingException e) {
358 e.printStackTrace();
359 }
360 }
362 if (CN != null)
363 strCN = CN.getValue();
364 if (ROLE != null)
365 strROLE = ROLE.getValue();
366 if (CUTYPE != null)
367 strCUTYPE = CUTYPE.getValue();
368 if (PARTSTAT != null)
369 strPARTSTAT = PARTSTAT.getValue();
371 if (strCN.equals("")) {
372 if (!strValue.equals("")) {
373 strCN = strValue;
374 }
375 }
377 if (!strCN.equals("")) {
378 if (strCUTYPE.equals("") || strCUTYPE.equals("INDIVIDUAL")) {
379 Attendee = new ContentValues();
381 Attendee.put(Attendees.EVENT_ID, ContentUris.parseId(mAndroidEventUri));
383 Attendee.put(Attendees.ATTENDEE_NAME, strCN);
384 Attendee.put(Attendees.ATTENDEE_EMAIL, strValue);
386 if (strROLE.equals("OPT-PARTICIPANT"))
387 Attendee.put(Attendees.ATTENDEE_TYPE, Attendees.TYPE_OPTIONAL);
388 else if (strROLE.equals("NON-PARTICIPANT"))
389 Attendee.put(Attendees.ATTENDEE_TYPE, Attendees.TYPE_NONE);
390 else if (strROLE.equals("REQ-PARTICIPANT"))
391 Attendee.put(Attendees.ATTENDEE_TYPE, Attendees.TYPE_REQUIRED);
392 else if (strROLE.equals("CHAIR"))
393 Attendee.put(Attendees.ATTENDEE_TYPE, Attendees.TYPE_REQUIRED);
394 else
395 Attendee.put(Attendees.ATTENDEE_TYPE, Attendees.TYPE_NONE);
397 if (Type.equals(Property.ATTENDEE))
398 Attendee.put(Attendees.ATTENDEE_RELATIONSHIP, Attendees.RELATIONSHIP_ATTENDEE);
399 else if (Type.equals(Property.ORGANIZER))
400 Attendee.put(Attendees.ATTENDEE_RELATIONSHIP, Attendees.RELATIONSHIP_ORGANIZER);
401 else
402 Attendee.put(Attendees.ATTENDEE_RELATIONSHIP, Attendees.RELATIONSHIP_NONE);
404 if (strPARTSTAT.equals(PartStat.NEEDS_ACTION.getValue()))
405 Attendee.put(Attendees.ATTENDEE_STATUS, Attendees.ATTENDEE_STATUS_INVITED);
406 else if (strPARTSTAT.equals(PartStat.ACCEPTED.getValue()))
407 Attendee.put(Attendees.ATTENDEE_STATUS, Attendees.ATTENDEE_STATUS_ACCEPTED);
408 else if (strPARTSTAT.equals(PartStat.DECLINED.getValue()))
409 Attendee.put(Attendees.ATTENDEE_STATUS, Attendees.ATTENDEE_STATUS_DECLINED);
410 else if (strPARTSTAT.equals(PartStat.COMPLETED.getValue()))
411 Attendee.put(Attendees.ATTENDEE_STATUS, Attendees.ATTENDEE_STATUS_NONE);
412 else if (strPARTSTAT.equals(PartStat.TENTATIVE.getValue()))
413 Attendee.put(Attendees.ATTENDEE_STATUS, Attendees.ATTENDEE_STATUS_TENTATIVE);
414 else
415 Attendee.put(Attendees.ATTENDEE_STATUS, Attendees.ATTENDEE_STATUS_INVITED);
417 }
418 }
420 return Attendee;
421 }
423 private long getAccessLevel() {
424 long Result = Events.ACCESS_DEFAULT;
425 String Value = "";
426 Property property = calendarComponent.getProperty(Property.CLASS);
427 if (property != null) {
428 Value = property.getValue();
429 if (Value.equals(Clazz.PUBLIC))
430 Result = Events.ACCESS_PUBLIC;
431 else if (Value.equals(Clazz.PRIVATE))
432 Result = Events.ACCESS_PRIVATE;
433 else if (Value.equals(Clazz.CONFIDENTIAL))
434 Result = Events.ACCESS_PRIVATE; // should be ACCESS_CONFIDENTIAL, but is not implemented within Android
435 }
437 return Result;
438 }
440 private int getStatus() {
441 int Result = -1;
442 String Value = "";
443 Property property = calendarComponent.getProperty(Property.STATUS);
444 if (property != null) {
445 Value = property.getValue();
446 if (Value.equals(Status.VEVENT_CONFIRMED.getValue()))
447 Result = Events.STATUS_CONFIRMED;
448 else if (Value.equals(Status.VEVENT_CANCELLED.getValue()))
449 Result = Events.STATUS_CANCELED;
450 else if (Value.equals(Status.VEVENT_TENTATIVE.getValue()))
451 Result = Events.STATUS_TENTATIVE;
452 }
454 return Result;
455 }
457 private String getDescription() {
458 String Result = null;
459 Property property = calendarComponent.getProperty(Property.DESCRIPTION);
460 if (property != null)
461 Result = property.getValue();
463 return Result;
464 }
466 private String getLocation() {
467 String Result = null;
468 Property property = calendarComponent.getProperty(Property.LOCATION);
469 if (property != null)
470 Result = property.getValue();
472 return Result;
473 }
475 private String getTitle() {
476 Property property = calendarComponent.getProperty(Property.SUMMARY);
477 if (property != null)
478 return property.getValue();
479 else
480 return "**unkonwn**";
481 }
483 private String getRRule() {
484 String Result = null;
485 Property property = calendarComponent.getProperty(Property.RRULE);
486 if (property != null)
487 Result = property.getValue();
489 return Result;
490 }
492 private String getExRule() {
493 String Result = null;
494 Property property = calendarComponent.getProperty(Property.EXRULE);
495 if (property != null)
496 Result = property.getValue();
498 return Result;
499 }
501 private String getRDate() {
502 String Result = null;
504 java.util.ArrayList<String> ExDates = this.getRDates();
505 for (String Value: ExDates) {
506 if (Result == null)
507 Result = "";
508 if (!Result.isEmpty())
509 Result += ",";
510 Result += Value;
511 }
513 return Result;
514 }
516 private java.util.ArrayList<String> getRDates() {
517 java.util.ArrayList<String> Result = new java.util.ArrayList<String>();
518 PropertyList Propertys = calendarComponent.getProperties(Property.RDATE);
519 if (Propertys != null) {
520 Property property;
521 for (Object objProperty : Propertys){
522 property = (Property) objProperty;
523 Result.add(property.getValue());
524 }
525 }
527 return Result;
528 }
530 private String getExDate() {
531 String Result = null;
533 java.util.ArrayList<String> ExDates = this.getExDates();
534 for (String Value: ExDates) {
535 if (Result == null)
536 Result = "";
537 if (!Result.isEmpty())
538 Result += ",";
539 Result += Value;
540 }
542 return Result;
543 }
545 private java.util.ArrayList<String> getExDates() {
546 java.util.ArrayList<String> Result = new java.util.ArrayList<String>();
547 PropertyList Propertys = calendarComponent.getProperties(Property.EXDATE);
548 if (Propertys != null) {
549 Property property;
550 for (Object objProperty : Propertys){
551 property = (Property) objProperty;
552 Result.add(property.getValue());
553 }
554 }
556 return Result;
557 }
559 private String getUid() {
560 String Result = "";
561 Property prop = calendarComponent.getProperty(Property.UID);
562 if (prop != null) {
563 Result = prop.getValue();
564 }
566 return Result;
567 }
569 private Long getTimestamp(Property prop) {
570 Long Result = null;
571 String strTimeZone = "";
572 //TimeZone timeZone = null;
574 try {
575 String strDate = prop.getValue();
577 Parameter parAllDay = prop.getParameter("VALUE");
578 if (parAllDay != null) {
579 if (parAllDay.getValue().equals("DATE")) {
580 mAllDay = true;
581 strDate += "T000000Z";
582 }
583 }
585 Parameter propTZ = prop.getParameter(Property.TZID);
586 if (propTZ != null)
587 strTimeZone = propTZ.getValue();
589 //TimeZoneRegistry registry = TimeZoneRegistryFactory.getInstance().createRegistry();
590 //if (!strTimeZone.equals(""))
591 // timeZone = registry.getTimeZone(strTimeZone);
593 //if (timeZone != null) {
594 if (!strTimeZone.equals("")) {
595 //Result = new DateTime(strDate, timeZone);
596 //Result1 = Result.getTime();
598 //20130331T120000
599 int Year = Integer.parseInt(strDate.substring(0, 4));
600 int Month = Integer.parseInt(strDate.substring(4, 6)) - 1;
601 int Day = Integer.parseInt(strDate.substring(6, 8));
602 int Hour = Integer.parseInt(strDate.substring(9, 11));
603 int Minute = Integer.parseInt(strDate.substring(11, 13));
604 int Second = Integer.parseInt(strDate.substring(13, 15));
606 // time in UTC
607 java.util.TimeZone jtz = java.util.TimeZone.getTimeZone("UTC");
608 java.util.Calendar cal = java.util.GregorianCalendar.getInstance(jtz);
609 cal.set(Year, Month, Day, Hour, Minute, Second);
610 cal.set(java.util.Calendar.MILLISECOND, 0);
611 Result = cal.getTimeInMillis();
613 // get the timezone
614 String[] IDs = java.util.TimeZone.getAvailableIDs();
615 Boolean Found = false;
616 for (int i = 0; i < IDs.length; i++) {
617 Found = Found || IDs[i].equals(strTimeZone);
618 }
619 if (Found) {
620 jtz = java.util.TimeZone.getTimeZone(strTimeZone);
622 // subtract the offset
623 Result -= jtz.getRawOffset();
625 // remove dst
626 if (jtz.inDaylightTime(new java.util.Date(Result)))
627 Result -= jtz.getDSTSavings();
629 } else {
630 if (mTimeZone != null) {
631 // subtract the offset
632 Result -= mTimeZone.getRawOffset();
634 // remove dst
635 if (mTimeZone.inDaylightTime(new java.util.Date(Result)))
636 Result -= mTimeZone.getDSTSavings();
637 } else {
638 // unknown Time?
639 // treat as local time, should not happen too often :)
640 Result = new DateTime(strDate).getTime();
641 }
642 }
643 } else {
644 if (strDate.endsWith("Z")) {
645 // GMT or UTC
646 Result = new DateTime(strDate).getTime();
647 } else {
648 // unknown Time?
649 // treat as local time, should not happen too often :)
650 Result = new DateTime(strDate).getTime();
651 }
652 }
654 } catch (ParseException e) {
655 e.printStackTrace();
656 }
658 return Result;
659 }
661 private long getStartTime() {
662 long Result = 0;
663 Property prop;
664 /*
665 * DTSTART;TZID=Europe/Berlin:20120425T080000
666 * DTSTART;TZID=(GMT+01.00) Sarajevo/Warsaw/Zagreb:20120305T104500
667 * DTSTART:20120308T093000Z
668 * DTSTART;VALUE=DATE:20120709
669 * DTSTART;TZID=Europe/Berlin:20130330T100000
670 */
672 prop = calendarComponent.getProperty(Property.DTSTART);
673 if (prop != null) {
674 Parameter propTZ = prop.getParameter(Property.TZID);
675 if (propTZ != null)
676 mstrTimeZoneStart = propTZ.getValue();
677 Result = getTimestamp(prop);
678 }
680 return Result;
681 }
683 private long getEndTime() {
684 long Result = 0;
685 Property propDtEnd;
686 Property propDuration;
688 propDtEnd = calendarComponent.getProperty(Property.DTEND);
689 propDuration = calendarComponent.getProperty(Property.DURATION);
690 if (propDtEnd != null) {
691 Parameter propTZ = propDtEnd.getParameter(Property.TZID);
692 if (propTZ != null)
693 mstrTimeZoneEnd = propTZ.getValue();
694 Result = getTimestamp(propDtEnd);
696 } else if (propDuration != null) {
697 Result = 0;
698 long Start = this.getStartTime();
699 String strDuration = propDuration.getValue();
700 Dur dur = new Dur(strDuration);
701 Result = Start
702 + dur.getSeconds() * 1000
703 + dur.getMinutes() * 60 * 1000
704 + dur.getHours() * 60 * 60 * 1000
705 + dur.getDays() * 60 * 60 * 24 * 1000;
707 mstrTimeZoneEnd = mstrTimeZoneStart;
708 }
711 return Result;
712 }
714 private String getTimeZoneStart() {
715 String Result = "GMT";
717 if (!mstrTimeZoneStart.equals("")) {
718 Result = mstrTimeZoneStart;
719 }
721 return Result;
722 }
724 private String getTimeZoneEnd() {
725 String Result = "GMT";
727 if (!mstrTimeZoneEnd.equals("")) {
728 Result = mstrTimeZoneEnd;
729 }
731 return Result;
732 }
735 private String getDuration() {
736 String Result = "";
737 Property prop = calendarComponent.getProperty("DURATION");
739 if (prop != null) {
740 //DURATION:PT1H
741 Result = prop.getValue();
742 } else {
743 // no DURATION given by this event. we have to calculate it by ourself
744 Result = "P";
745 long Start = this.getStartTime();
746 long End = this.getEndTime();
747 long Duration = 0;
748 if (End != 0)
749 Duration = (End - Start) / 1000; // get rid of the milliseconds, they cann't be described with RFC2445-Duration
751 if (Duration < 0) {
752 Duration = 0;
753 }
754 int Days = (int) Math.ceil(Duration / 24 / 60 / 60);
755 int Hours = (int) Math.ceil((Duration - (Days * 24 * 60 * 60)) / 60 / 60);
756 int Minutes = (int) Math.ceil((Duration - (Days * 24 * 60 * 60) - (Hours * 60 * 60)) / 60);
757 int Seconds = (int) (Duration - (Days * 24 * 60 * 60) - (Hours * 60 * 60) - (Minutes * 60));
759 if (Days > 0)
760 Result += String.valueOf(Days) + "D";
762 if (!mAllDay) {
763 //if a ALL_DAY event occurs, there is no need for hours, minutes and seconds (Android doesn't understand them)
764 Result += "T";
765 Result += String.valueOf(Hours) + "H";
766 Result += String.valueOf(Minutes) + "M";
767 Result += String.valueOf(Seconds) + "S";
768 }
769 }
771 return Result;
772 }
774 private int getAllDay() {
775 int Result = 0;
777 if (mAllDay)
778 Result = 1;
780 return Result;
781 }
783 /**
784 * opens the first items of the event
785 * @return success of this function
786 * @see AndroidEvent#createIcs()
787 * @see CalendarEvent#fetchBody()
788 * @throws CaldavProtocolException
789 * @throws IOException
790 * @throws ParserException
791 */
792 private boolean parseIcs() throws CaldavProtocolException, IOException, ParserException {
793 boolean Error = false;
795 CalendarBuilder builder = new CalendarBuilder();
796 CompatibilityHints.setHintEnabled(CompatibilityHints.KEY_RELAXED_UNFOLDING, true);
797 CompatibilityHints.setHintEnabled(CompatibilityHints.KEY_RELAXED_PARSING, true);
798 CompatibilityHints.setHintEnabled(CompatibilityHints.KEY_RELAXED_VALIDATION, true);
799 CompatibilityHints.setHintEnabled(CompatibilityHints.KEY_OUTLOOK_COMPATIBILITY, true);
801 StringReader reader = new StringReader(this.stringIcs);
802 try {
803 this.calendar = builder.build(reader);
804 } catch (ParserException ex) {
805 // ical4j fails with this: "Cannot set timezone for UTC properties"
806 // CREATED;TZID=America/New_York:20130129T140250
807 Error = true;
808 }
810 if (!Error) {
811 ComponentList components = null;
812 components = this.calendar.getComponents(Component.VEVENT);
813 if (components.size() == 0) {
814 components = this.calendar.getComponents(Component.VTODO);
815 if (components.size() == 0) {
816 throw new CaldavProtocolException("unknown events in ICS");
817 } else {
818 Log.e(TAG, "unsupported event TODO in ICS");
819 Error = true;
820 }
821 } else if (components.size() > 1) {
822 Log.e(TAG, "Several events in ICS -> only first will be processed");
823 }
825 // get the TimeZone information
826 Component mCom = this.calendar.getComponent(Component.VTIMEZONE);
827 if (mCom != null)
828 mVTimeZone = (VTimeZone) this.calendar.getComponent(Component.VTIMEZONE);
829 if (mVTimeZone != null)
830 mTimeZone = new TimeZone(mVTimeZone);
832 if (!Error)
833 calendarComponent = (Component) components.get(0);
834 }
836 return !Error;
837 }
839 /**
840 * searches for an android event
841 * @param androidCalendar
842 * @return the android event
843 * @throws RemoteException
844 */
845 public AndroidEvent getAndroidEvent(DavCalendar androidCalendar) throws RemoteException {
846 boolean Error = false;
847 Uri uriEvents = Events.CONTENT_URI;
848 Uri uriAttendee = Attendees.CONTENT_URI;
849 Uri uriReminder = Reminders.CONTENT_URI;
850 AndroidEvent androidEvent = null;
852 String selection = "(" + Events._SYNC_ID + " = ?)";
853 String[] selectionArgs = new String[] {this.getUri().toString()};
854 Cursor curEvent = this.mProvider.query(uriEvents, null, selection, selectionArgs, null);
856 Cursor curAttendee = null;
857 Cursor curReminder = null;
859 if (curEvent == null) {
860 Error = true;
861 }
862 if (!Error) {
863 if (curEvent.getCount() == 0) {
864 Error = true;
865 }
866 }
867 if (!Error) {
868 curEvent.moveToNext();
870 long EventID = curEvent.getLong(curEvent.getColumnIndex(Events._ID));
871 Uri returnedUri = ContentUris.withAppendedId(uriEvents, EventID);
873 //androidEvent = new AndroidEvent(this.mAccount, this.mProvider, returnedUri, androidCalendar.getAndroidCalendarUri());
874 androidEvent = new AndroidEvent(returnedUri, androidCalendar.getAndroidCalendarUri());
875 androidEvent.readContentValues(curEvent);
877 selection = "(" + Attendees.EVENT_ID + " = ?)";
878 selectionArgs = new String[] {String.valueOf(EventID)};
879 curAttendee = this.mProvider.query(uriAttendee, null, selection, selectionArgs, null);
880 selection = "(" + Reminders.EVENT_ID + " = ?)";
881 selectionArgs = new String[] {String.valueOf(EventID)};
882 curReminder = this.mProvider.query(uriReminder, null, selection, selectionArgs, null);
883 androidEvent.readAttendees(curAttendee);
884 androidEvent.readReminder(curReminder);
885 curAttendee.close();
886 curReminder.close();
887 }
888 curEvent.close();
890 return androidEvent;
891 }
893 /**
894 * creates a new androidEvent from a given calendarEvent
895 * @param androidCalendar
896 * @return
897 * @throws ClientProtocolException
898 * @throws IOException
899 * @throws CaldavProtocolException
900 * @throws RemoteException
901 * @throws ParserException
902 * @see {@link SyncAdapter#synchroniseEvents(CaldavFacade, Account, ContentProviderClient, Uri, DavCalendar, SyncStats)}
903 */
904 public boolean createAndroidEvent(DavCalendar androidCalendar) throws ClientProtocolException, IOException, CaldavProtocolException, RemoteException, ParserException {
905 boolean Result = false;
906 boolean BodyFetched = this.fetchBody();
907 int CountAttendees = 0;
908 int CountReminders = 0;
910 if (BodyFetched) {
911 //calendarEvent.readContentValues(calendarUri);
912 this.readContentValues();
913 this.setAndroidCalendarId(ContentUris.parseId(androidCalendar.getAndroidCalendarUri()));
915 Uri uri = this.mProvider.insert(asSyncAdapter(Events.CONTENT_URI, this.mAccount.name, this.mAccount.type), this.ContentValues);
916 this.setAndroidEventUri(uri);
918 Log.d(TAG, "Creating calendar event for " + uri.toString());
920 //check the attendees
921 java.util.ArrayList<ContentValues> AttendeeList = this.getAttandees();
922 for (ContentValues Attendee : AttendeeList) {
923 this.mProvider.insert(Attendees.CONTENT_URI, Attendee);
924 CountAttendees += 1;
925 }
927 //check the reminders
928 java.util.ArrayList<ContentValues> ReminderList = this.getReminders();
929 for (ContentValues Reminder : ReminderList) {
930 this.mProvider.insert(Reminders.CONTENT_URI, Reminder);
931 CountReminders += 1;
932 }
934 if ((CountAttendees > 0) || (CountReminders > 0)) {
935 //the events gets dirty when attendees or reminders were added
936 AndroidEvent androidEvent = this.getAndroidEvent(androidCalendar);
938 androidEvent.ContentValues.put(Events.DIRTY, 0);
939 int RowCount = this.mProvider.update(asSyncAdapter(androidEvent.getUri(), this.mAccount.name, this.mAccount.type), androidEvent.ContentValues, null, null);
940 Result = (RowCount == 1);
941 } else {
942 Result = true;
943 }
946 }
947 return Result;
948 }
950 /**
951 * the android event is getting updated
952 * @param provider
953 * @param account
954 * @param androidEvent
955 * @param calendarEvent
956 * @return
957 * @throws ClientProtocolException
958 * @throws IOException
959 * @throws CaldavProtocolException
960 * @throws RemoteException
961 * @throws ParserException
962 * @see {@link SyncAdapter#synchroniseEvents(CaldavFacade, Account, ContentProviderClient, Uri, Calendar, SyncStats)}
963 */
964 public boolean updateAndroidEvent(AndroidEvent androidEvent) throws ClientProtocolException, IOException, CaldavProtocolException, RemoteException, ParserException {
965 boolean BodyFetched = this.fetchBody();
966 int Rows = 0;
968 if (BodyFetched) {
969 this.readContentValues();
970 this.setAndroidCalendarId(androidEvent.getAndroidCalendarId());
971 this.setAndroidEventUri(androidEvent.getUri());
973 Log.d(TAG, "AndroidEvent is dirty: " + androidEvent.ContentValues.getAsString(Events.DIRTY));
975 if (androidEvent.checkEventValuesChanged(this.ContentValues)) {
976 // just set the raw data from server event into android event
977 if (androidEvent.ContentValues.containsKey(Event.RAWDATA))
978 androidEvent.ContentValues.remove(Event.RAWDATA);
979 androidEvent.ContentValues.put(Event.RAWDATA, this.ContentValues.getAsString(Event.RAWDATA));
981 //update the attendees and reminders
982 this.updateAndroidAttendees();
983 this.updateAndroidReminder();
985 androidEvent.ContentValues.put(Events.DIRTY, 0); // the event is now in sync
986 Log.d(TAG, "Update calendar event: for "+androidEvent.getUri());
988 Rows = mProvider.update(asSyncAdapter(androidEvent.getUri(), mAccount.name, mAccount.type), androidEvent.ContentValues, null, null);
989 //Log.i(TAG, "Updated calendar event: rows effected " + Rows.toString());
990 } else {
991 Log.d(TAG, "Update calendar event not needed: for "+androidEvent.getUri());
992 }
993 }
994 return (Rows == 1);
995 }
997 /**
998 * updates the attendees from a calendarEvent to its androidEvent.
999 * the calendarEvent has to know its androidEvent via {@link CalendarEvent#setAndroidEventUri(Uri)}
1000 * @param provider
1001 * @return
1002 * @see SyncAdapter#updateAndroidEvent(ContentProviderClient, Account, AndroidEvent, CalendarEvent)
1003 */
1004 private boolean updateAndroidAttendees() {
1005 boolean Result = false;
1007 try {
1008 String mSelectionClause = "(" + Attendees.EVENT_ID + " = ?)";
1009 String[] mSelectionArgs = {Long.toString(ContentUris.parseId(this.getAndroidEventUri())) };
1010 int RowDelete;
1011 RowDelete = this.mProvider.delete(Attendees.CONTENT_URI, mSelectionClause, mSelectionArgs);
1012 Log.d(TAG, "Attendees Deleted:" + String.valueOf(RowDelete));
1014 java.util.ArrayList<ContentValues> AttendeeList = this.getAttandees();
1015 for (ContentValues Attendee : AttendeeList) {
1016 this.mProvider.insert(Attendees.CONTENT_URI, Attendee);
1017 }
1018 Log.d(TAG, "Attendees Inserted:" + String.valueOf(AttendeeList.size()));
1019 Result = true;
1020 } catch (RemoteException e) {
1021 e.printStackTrace();
1022 }
1024 return Result;
1025 }
1027 /**
1028 * update the reminders from a calendarEvent to its androidEvent.
1029 * the calendarEvent has to know its androidEvent via {@link CalendarEvent#setAndroidEventUri(Uri)}
1030 * @param provider
1031 * @return
1032 * @see SyncAdapter#updateAndroidEvent(ContentProviderClient, Account, AndroidEvent, CalendarEvent)
1033 */
1034 private boolean updateAndroidReminder() {
1035 boolean Result = false;
1037 try {
1038 String mSelectionClause = "(" + Reminders.EVENT_ID + " = ?)";
1039 String[] mSelectionArgs = {Long.toString(ContentUris.parseId(this.getAndroidEventUri())) };
1040 int RowDelete;
1041 RowDelete = this.mProvider.delete(Reminders.CONTENT_URI, mSelectionClause, mSelectionArgs);
1042 Log.d(TAG, "Reminders Deleted:" + String.valueOf(RowDelete));
1045 Uri ReminderUri;
1046 java.util.ArrayList<ContentValues> ReminderList = this.getReminders();
1047 for (ContentValues Reminder : ReminderList) {
1048 ReminderUri = this.mProvider.insert(Reminders.CONTENT_URI, Reminder);
1049 System.out.println(ReminderUri);
1050 }
1051 Log.d(TAG, "Reminders Inserted:" + String.valueOf(ReminderList.size()));
1053 Result = true;
1054 } catch (RemoteException e) {
1055 e.printStackTrace();
1056 }
1058 return Result;
1059 }
1061 private static Uri asSyncAdapter(Uri uri, String account, String accountType) {
1062 return uri.buildUpon()
1063 .appendQueryParameter(android.provider.CalendarContract.CALLER_IS_SYNCADAPTER,"true")
1064 .appendQueryParameter(Calendars.ACCOUNT_NAME, account)
1065 .appendQueryParameter(Calendars.ACCOUNT_TYPE, accountType).build();
1066 }
1067 }