diff -r 000000000000 -r fb9019fb1bf7 src/org/gege/caldavsyncadapter/caldav/entities/CalendarEvent.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/gege/caldavsyncadapter/caldav/entities/CalendarEvent.java Tue Feb 10 18:12:00 2015 +0100
@@ -0,0 +1,1068 @@
+/**
+ * Copyright (c) 2012-2013, Gerald Garcia, Timo Berger
+ *
+ * This file is part of Andoid Caldav Sync Adapter Free.
+ *
+ * Andoid Caldav Sync Adapter Free is free software: you can redistribute
+ * it and/or modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, either version 3 of the
+ * License, or at your option any later version.
+ *
+ * Andoid Caldav Sync Adapter Free is distributed in the hope that
+ * it will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Andoid Caldav Sync Adapter Free.
+ * If not, see .
+ *
+ */
+
+package org.gege.caldavsyncadapter.caldav.entities;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.net.URL;
+import java.net.URLDecoder;
+import java.text.ParseException;
+import java.util.ArrayList;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import net.fortuna.ical4j.data.CalendarBuilder;
+import net.fortuna.ical4j.data.ParserException;
+import net.fortuna.ical4j.model.Calendar;
+import net.fortuna.ical4j.model.Component;
+import net.fortuna.ical4j.model.ComponentList;
+import net.fortuna.ical4j.model.DateTime;
+import net.fortuna.ical4j.model.Dur;
+import net.fortuna.ical4j.model.Parameter;
+import net.fortuna.ical4j.model.ParameterList;
+import net.fortuna.ical4j.model.Property;
+import net.fortuna.ical4j.model.PropertyList;
+import net.fortuna.ical4j.model.TimeZone;
+import net.fortuna.ical4j.model.component.VEvent;
+import net.fortuna.ical4j.model.component.VTimeZone;
+import net.fortuna.ical4j.model.parameter.Cn;
+import net.fortuna.ical4j.model.parameter.CuType;
+import net.fortuna.ical4j.model.parameter.PartStat;
+import net.fortuna.ical4j.model.parameter.Role;
+import net.fortuna.ical4j.model.property.Clazz;
+import net.fortuna.ical4j.model.property.Status;
+import net.fortuna.ical4j.util.CompatibilityHints;
+
+import org.apache.http.client.ClientProtocolException;
+import org.gege.caldavsyncadapter.Event;
+import org.gege.caldavsyncadapter.android.entities.AndroidEvent;
+import org.gege.caldavsyncadapter.caldav.CaldavFacade;
+import org.gege.caldavsyncadapter.caldav.CaldavProtocolException;
+import org.gege.caldavsyncadapter.caldav.xml.MultiStatusHandler;
+import org.gege.caldavsyncadapter.caldav.xml.sax.MultiStatus;
+import org.gege.caldavsyncadapter.caldav.xml.sax.Prop;
+import org.gege.caldavsyncadapter.caldav.xml.sax.PropStat;
+import org.gege.caldavsyncadapter.caldav.xml.sax.Response;
+import org.gege.caldavsyncadapter.syncadapter.SyncAdapter;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+
+import android.accounts.Account;
+import android.content.ContentProviderClient;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.SyncStats;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.RemoteException;
+import android.provider.CalendarContract.Attendees;
+import android.provider.CalendarContract.Calendars;
+import android.provider.CalendarContract.Events;
+import android.provider.CalendarContract.Reminders;
+import android.util.Log;
+
+
+
+public class CalendarEvent extends org.gege.caldavsyncadapter.Event {
+ private static final String TAG = "CalendarEvent";
+
+ private String stringIcs;
+
+ private Calendar calendar;
+
+ private Component calendarComponent;
+
+ private String eTag;
+ private URI muri;
+ private Uri mAndroidEventUri;
+ public URL calendarURL;
+
+ private boolean mAllDay = false;
+ private VTimeZone mVTimeZone = null;
+ private TimeZone mTimeZone = null;
+
+ private String mstrTimeZoneStart = "";
+ private String mstrTimeZoneEnd = "";
+
+ private Account mAccount = null;
+ private ContentProviderClient mProvider = null;
+
+ public CalendarEvent(Account account, ContentProviderClient provider) {
+ this.mAccount = account;
+ this.mProvider = provider;
+ }
+
+ public String getETag() {
+ return eTag;
+ }
+
+ public void setETag(String eTag) {
+ this.eTag = eTag;
+ }
+
+ public URI getUri() {
+ return muri;
+ }
+
+ public void setUri(URI uri) {
+ this.muri = uri;
+ }
+
+ public void setICSasString(String ics) {
+ this.stringIcs = ics;
+ }
+
+ public boolean setICSasMultiStatus(String stringMultiStatus) {
+ boolean Result = false;
+ String ics = "";
+ MultiStatus multistatus;
+ ArrayList responselist;
+ Response response;
+ PropStat propstat;
+ Prop prop;
+ try {
+ SAXParserFactory factory = SAXParserFactory.newInstance();
+ SAXParser parser = factory.newSAXParser();
+ XMLReader reader = parser.getXMLReader();
+ MultiStatusHandler contentHandler = new MultiStatusHandler();
+ reader.setContentHandler(contentHandler);
+ reader.parse(new InputSource(new StringReader(stringMultiStatus)));
+
+ multistatus = contentHandler.mMultiStatus;
+ if (multistatus != null) {
+ responselist = multistatus.ResponseList;
+ if (responselist.size() == 1) {
+ response = responselist.get(0);
+ //HINT: bugfix for google calendar, zimbra replace("@", "%40")
+ if (response.href.replace("@", "%40").equals(this.getUri().getRawPath().replace("@", "%40"))) {
+ propstat = response.propstat;
+ if (propstat != null) {
+ if (propstat.status.contains("200 OK")) {
+ prop = propstat.prop;
+ ics = prop.calendardata;
+ this.setETag(prop.getetag);
+ Result = true;
+ }
+ }
+ }
+ }
+ }
+ } catch (ParserConfigurationException e1) {
+ e1.printStackTrace();
+ } catch (SAXException e1) {
+ e1.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ this.stringIcs = ics;
+ return Result;
+ }
+
+ /**
+ * sets the Uri of the android event
+ * @param uri
+ * @see org.gege.caldavsyncadapter.syncadapter.SyncAdapter#createAndroidEvent(ContentProviderClient provider, Account account, Uri calendarUri, CalendarEvent calendarEvent)
+ * @see org.gege.caldavsyncadapter.syncadapter.SyncAdapter#updateAndroidEvent(ContentProviderClient provider, Account account, AndroidEvent androidEvent, CalendarEvent calendarEvent)
+ */
+ public void setAndroidEventUri(Uri uri) {
+ mAndroidEventUri = uri;
+ }
+
+ /**
+ * gets the Uri of the android event
+ * @return
+ */
+ public Uri getAndroidEventUri() {
+ return mAndroidEventUri;
+ }
+
+
+ /**
+ * reads all ContentValues from the caldav source
+ * @param calendarUri
+ * @return
+ * @see org.gege.caldavsyncadapter.syncadapter.SyncAdapter#createAndroidEvent(ContentProviderClient provider, Account account, Uri calendarUri, CalendarEvent calendarEvent)
+ * @see org.gege.caldavsyncadapter.syncadapter.SyncAdapter#updateAndroidEvent(ContentProviderClient provider, Account account, AndroidEvent androidEvent, CalendarEvent calendarEvent)
+ */
+ public boolean readContentValues() {
+ this.ContentValues.put(Events.DTSTART, this.getStartTime());
+ this.ContentValues.put(Events.EVENT_TIMEZONE, this.getTimeZoneStart());
+
+ //if (this.getRRule().isEmpty() && this.getRDate().isEmpty()) {
+ if (this.getRRule() == null && this.getRDate() == null) {
+ //if (AllDay.equals(1)) //{
+ // values.put(Events.ALL_DAY, AllDay);
+ //} else {
+ this.ContentValues.put(Events.DTEND, this.getEndTime());
+ this.ContentValues.put(Events.EVENT_END_TIMEZONE, this.getTimeZoneEnd());
+ //}
+ } else {
+ //if (AllDay.equals(1))
+ // values.put(Events.ALL_DAY, AllDay);
+ this.ContentValues.put(Events.DURATION, this.getDuration());
+ }
+ int AllDay = this.getAllDay();
+ this.ContentValues.put(Events.ALL_DAY, AllDay);
+
+ this.ContentValues.put(Events.TITLE, this.getTitle());
+ //this.ContentValues.put(Events.CALENDAR_ID, ContentUris.parseId(calendarUri));
+ this.ContentValues.put(Events._SYNC_ID, this.getUri().toString());
+ this.ContentValues.put(ETAG, this.getETag());
+ this.ContentValues.put(Events.DESCRIPTION, this.getDescription());
+ this.ContentValues.put(Events.EVENT_LOCATION, this.getLocation());
+ this.ContentValues.put(Events.ACCESS_LEVEL, this.getAccessLevel());
+ this.ContentValues.put(Events.STATUS, this.getStatus());
+ this.ContentValues.put(Events.RDATE, this.getRDate());
+ this.ContentValues.put(Events.RRULE, this.getRRule());
+ this.ContentValues.put(Events.EXRULE, this.getExRule());
+ this.ContentValues.put(Events.EXDATE, this.getExDate());
+ this.ContentValues.put(UID, this.getUid());
+ this.ContentValues.put(RAWDATA, this.stringIcs);
+
+ return true;
+ }
+
+ /**
+ * receives a single event and parses its content
+ * @return success of this function
+ * @see CalendarEvent#parseIcs()
+ * @throws ClientProtocolException
+ * @throws IOException
+ * @throws CaldavProtocolException
+ * @throws ParserException
+ */
+ public boolean fetchBody() throws ClientProtocolException, IOException, CaldavProtocolException, ParserException {
+ boolean Error = false;
+
+ //replaced fetchEvent() with getEvent()
+ //CaldavFacade.fetchEvent(this);
+ CaldavFacade.getEvent(this);
+
+ boolean Parse = this.parseIcs();
+ if (!Parse)
+ Error = true;
+
+ return !Error;
+ }
+
+ public java.util.ArrayList getReminders() {
+ java.util.ArrayList Result = new java.util.ArrayList();
+ ContentValues Reminder;
+
+ /*
+ * http://sourceforge.net/tracker/?func=detail&aid=3021704&group_id=107024&atid=646395
+ */
+
+ net.fortuna.ical4j.model.component.VEvent event = (VEvent) this.calendarComponent;
+
+ //ComponentList ComList = this.calendar.getComponents(Component.VALARM);
+ ComponentList ComList = event.getAlarms();
+
+ if (ComList != null) {
+ for (Object objCom : ComList) {
+ Component Com = (Component) objCom;
+ Reminder = new ContentValues();
+
+ //Property ACTION = Com.getProperty("ACTION");
+ Property TRIGGER = Com.getProperty("TRIGGER");
+ if (TRIGGER != null) {
+ Dur Duration = new Dur(TRIGGER.getValue());
+ //if (ACTION.getValue().equals("DISPLAY"))
+
+ int intDuration = Duration.getMinutes() + Duration.getHours() * 60 + Duration.getDays() * 60 * 24;
+
+ Reminder.put(Reminders.EVENT_ID, ContentUris.parseId(mAndroidEventUri));
+ Reminder.put(Reminders.METHOD, Reminders.METHOD_ALERT);
+ Reminder.put(Reminders.MINUTES, intDuration);
+
+ Result.add(Reminder);
+ }
+ }
+ }
+ return Result;
+ }
+
+ public java.util.ArrayList getAttandees() {
+ java.util.ArrayList Result = new java.util.ArrayList();
+ ContentValues Attendee;
+ PropertyList Propertys = calendarComponent.getProperties(Property.ATTENDEE);
+ if (Propertys != null) {
+ for (Object objProperty : Propertys){
+ Property property = (Property) objProperty;
+ Attendee = ReadAttendeeProperties(property,Property.ATTENDEE);
+ if (Attendee != null)
+ Result.add(Attendee);
+ }
+ }
+ Propertys = calendarComponent.getProperties(Property.ORGANIZER);
+ if (Propertys != null) {
+ for (Object objProperty : Propertys){
+ Property property = (Property) objProperty;
+ Attendee = ReadAttendeeProperties(property,Property.ORGANIZER);
+ if (Attendee != null)
+ Result.add(Attendee);
+ }
+ }
+
+
+ return Result;
+ }
+
+ private String mstrcIcalPropertyError = "net.fortunal.ical4j.invalid:";
+ private ContentValues ReadAttendeeProperties(Property property, String Type) {
+ ContentValues Attendee = null;
+
+ ParameterList Parameters = property.getParameters();
+ Parameter CN = Parameters.getParameter(Cn.CN);
+ Parameter ROLE = Parameters.getParameter(Role.ROLE);
+ Parameter CUTYPE = Parameters.getParameter(CuType.CUTYPE);
+ //Parameter RSVP = Parameters.getParameter("RSVP");
+ Parameter PARTSTAT = Parameters.getParameter(PartStat.PARTSTAT);
+
+ String strCN = "";
+ String strROLE = "";
+ String strCUTYPE = "";
+ String strValue = property.getValue().replace("mailto:", "");
+ String strPARTSTAT = "";
+
+ if (strValue.startsWith(mstrcIcalPropertyError)) {
+ strValue = strValue.replace(mstrcIcalPropertyError, "");
+ try {
+ strValue = URLDecoder.decode(strValue, "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ e.printStackTrace();
+ }
+ }
+
+ if (CN != null)
+ strCN = CN.getValue();
+ if (ROLE != null)
+ strROLE = ROLE.getValue();
+ if (CUTYPE != null)
+ strCUTYPE = CUTYPE.getValue();
+ if (PARTSTAT != null)
+ strPARTSTAT = PARTSTAT.getValue();
+
+ if (strCN.equals("")) {
+ if (!strValue.equals("")) {
+ strCN = strValue;
+ }
+ }
+
+ if (!strCN.equals("")) {
+ if (strCUTYPE.equals("") || strCUTYPE.equals("INDIVIDUAL")) {
+ Attendee = new ContentValues();
+
+ Attendee.put(Attendees.EVENT_ID, ContentUris.parseId(mAndroidEventUri));
+
+ Attendee.put(Attendees.ATTENDEE_NAME, strCN);
+ Attendee.put(Attendees.ATTENDEE_EMAIL, strValue);
+
+ if (strROLE.equals("OPT-PARTICIPANT"))
+ Attendee.put(Attendees.ATTENDEE_TYPE, Attendees.TYPE_OPTIONAL);
+ else if (strROLE.equals("NON-PARTICIPANT"))
+ Attendee.put(Attendees.ATTENDEE_TYPE, Attendees.TYPE_NONE);
+ else if (strROLE.equals("REQ-PARTICIPANT"))
+ Attendee.put(Attendees.ATTENDEE_TYPE, Attendees.TYPE_REQUIRED);
+ else if (strROLE.equals("CHAIR"))
+ Attendee.put(Attendees.ATTENDEE_TYPE, Attendees.TYPE_REQUIRED);
+ else
+ Attendee.put(Attendees.ATTENDEE_TYPE, Attendees.TYPE_NONE);
+
+ if (Type.equals(Property.ATTENDEE))
+ Attendee.put(Attendees.ATTENDEE_RELATIONSHIP, Attendees.RELATIONSHIP_ATTENDEE);
+ else if (Type.equals(Property.ORGANIZER))
+ Attendee.put(Attendees.ATTENDEE_RELATIONSHIP, Attendees.RELATIONSHIP_ORGANIZER);
+ else
+ Attendee.put(Attendees.ATTENDEE_RELATIONSHIP, Attendees.RELATIONSHIP_NONE);
+
+ if (strPARTSTAT.equals(PartStat.NEEDS_ACTION.getValue()))
+ Attendee.put(Attendees.ATTENDEE_STATUS, Attendees.ATTENDEE_STATUS_INVITED);
+ else if (strPARTSTAT.equals(PartStat.ACCEPTED.getValue()))
+ Attendee.put(Attendees.ATTENDEE_STATUS, Attendees.ATTENDEE_STATUS_ACCEPTED);
+ else if (strPARTSTAT.equals(PartStat.DECLINED.getValue()))
+ Attendee.put(Attendees.ATTENDEE_STATUS, Attendees.ATTENDEE_STATUS_DECLINED);
+ else if (strPARTSTAT.equals(PartStat.COMPLETED.getValue()))
+ Attendee.put(Attendees.ATTENDEE_STATUS, Attendees.ATTENDEE_STATUS_NONE);
+ else if (strPARTSTAT.equals(PartStat.TENTATIVE.getValue()))
+ Attendee.put(Attendees.ATTENDEE_STATUS, Attendees.ATTENDEE_STATUS_TENTATIVE);
+ else
+ Attendee.put(Attendees.ATTENDEE_STATUS, Attendees.ATTENDEE_STATUS_INVITED);
+
+ }
+ }
+
+ return Attendee;
+ }
+
+ private long getAccessLevel() {
+ long Result = Events.ACCESS_DEFAULT;
+ String Value = "";
+ Property property = calendarComponent.getProperty(Property.CLASS);
+ if (property != null) {
+ Value = property.getValue();
+ if (Value.equals(Clazz.PUBLIC))
+ Result = Events.ACCESS_PUBLIC;
+ else if (Value.equals(Clazz.PRIVATE))
+ Result = Events.ACCESS_PRIVATE;
+ else if (Value.equals(Clazz.CONFIDENTIAL))
+ Result = Events.ACCESS_PRIVATE; // should be ACCESS_CONFIDENTIAL, but is not implemented within Android
+ }
+
+ return Result;
+ }
+
+ private int getStatus() {
+ int Result = -1;
+ String Value = "";
+ Property property = calendarComponent.getProperty(Property.STATUS);
+ if (property != null) {
+ Value = property.getValue();
+ if (Value.equals(Status.VEVENT_CONFIRMED.getValue()))
+ Result = Events.STATUS_CONFIRMED;
+ else if (Value.equals(Status.VEVENT_CANCELLED.getValue()))
+ Result = Events.STATUS_CANCELED;
+ else if (Value.equals(Status.VEVENT_TENTATIVE.getValue()))
+ Result = Events.STATUS_TENTATIVE;
+ }
+
+ return Result;
+ }
+
+ private String getDescription() {
+ String Result = null;
+ Property property = calendarComponent.getProperty(Property.DESCRIPTION);
+ if (property != null)
+ Result = property.getValue();
+
+ return Result;
+ }
+
+ private String getLocation() {
+ String Result = null;
+ Property property = calendarComponent.getProperty(Property.LOCATION);
+ if (property != null)
+ Result = property.getValue();
+
+ return Result;
+ }
+
+ private String getTitle() {
+ Property property = calendarComponent.getProperty(Property.SUMMARY);
+ if (property != null)
+ return property.getValue();
+ else
+ return "**unkonwn**";
+ }
+
+ private String getRRule() {
+ String Result = null;
+ Property property = calendarComponent.getProperty(Property.RRULE);
+ if (property != null)
+ Result = property.getValue();
+
+ return Result;
+ }
+
+ private String getExRule() {
+ String Result = null;
+ Property property = calendarComponent.getProperty(Property.EXRULE);
+ if (property != null)
+ Result = property.getValue();
+
+ return Result;
+ }
+
+ private String getRDate() {
+ String Result = null;
+
+ java.util.ArrayList ExDates = this.getRDates();
+ for (String Value: ExDates) {
+ if (Result == null)
+ Result = "";
+ if (!Result.isEmpty())
+ Result += ",";
+ Result += Value;
+ }
+
+ return Result;
+ }
+
+ private java.util.ArrayList getRDates() {
+ java.util.ArrayList Result = new java.util.ArrayList();
+ PropertyList Propertys = calendarComponent.getProperties(Property.RDATE);
+ if (Propertys != null) {
+ Property property;
+ for (Object objProperty : Propertys){
+ property = (Property) objProperty;
+ Result.add(property.getValue());
+ }
+ }
+
+ return Result;
+ }
+
+ private String getExDate() {
+ String Result = null;
+
+ java.util.ArrayList ExDates = this.getExDates();
+ for (String Value: ExDates) {
+ if (Result == null)
+ Result = "";
+ if (!Result.isEmpty())
+ Result += ",";
+ Result += Value;
+ }
+
+ return Result;
+ }
+
+ private java.util.ArrayList getExDates() {
+ java.util.ArrayList Result = new java.util.ArrayList();
+ PropertyList Propertys = calendarComponent.getProperties(Property.EXDATE);
+ if (Propertys != null) {
+ Property property;
+ for (Object objProperty : Propertys){
+ property = (Property) objProperty;
+ Result.add(property.getValue());
+ }
+ }
+
+ return Result;
+ }
+
+ private String getUid() {
+ String Result = "";
+ Property prop = calendarComponent.getProperty(Property.UID);
+ if (prop != null) {
+ Result = prop.getValue();
+ }
+
+ return Result;
+ }
+
+ private Long getTimestamp(Property prop) {
+ Long Result = null;
+ String strTimeZone = "";
+ //TimeZone timeZone = null;
+
+ try {
+ String strDate = prop.getValue();
+
+ Parameter parAllDay = prop.getParameter("VALUE");
+ if (parAllDay != null) {
+ if (parAllDay.getValue().equals("DATE")) {
+ mAllDay = true;
+ strDate += "T000000Z";
+ }
+ }
+
+ Parameter propTZ = prop.getParameter(Property.TZID);
+ if (propTZ != null)
+ strTimeZone = propTZ.getValue();
+
+ //TimeZoneRegistry registry = TimeZoneRegistryFactory.getInstance().createRegistry();
+ //if (!strTimeZone.equals(""))
+ // timeZone = registry.getTimeZone(strTimeZone);
+
+ //if (timeZone != null) {
+ if (!strTimeZone.equals("")) {
+ //Result = new DateTime(strDate, timeZone);
+ //Result1 = Result.getTime();
+
+ //20130331T120000
+ int Year = Integer.parseInt(strDate.substring(0, 4));
+ int Month = Integer.parseInt(strDate.substring(4, 6)) - 1;
+ int Day = Integer.parseInt(strDate.substring(6, 8));
+ int Hour = Integer.parseInt(strDate.substring(9, 11));
+ int Minute = Integer.parseInt(strDate.substring(11, 13));
+ int Second = Integer.parseInt(strDate.substring(13, 15));
+
+ // time in UTC
+ java.util.TimeZone jtz = java.util.TimeZone.getTimeZone("UTC");
+ java.util.Calendar cal = java.util.GregorianCalendar.getInstance(jtz);
+ cal.set(Year, Month, Day, Hour, Minute, Second);
+ cal.set(java.util.Calendar.MILLISECOND, 0);
+ Result = cal.getTimeInMillis();
+
+ // get the timezone
+ String[] IDs = java.util.TimeZone.getAvailableIDs();
+ Boolean Found = false;
+ for (int i = 0; i < IDs.length; i++) {
+ Found = Found || IDs[i].equals(strTimeZone);
+ }
+ if (Found) {
+ jtz = java.util.TimeZone.getTimeZone(strTimeZone);
+
+ // subtract the offset
+ Result -= jtz.getRawOffset();
+
+ // remove dst
+ if (jtz.inDaylightTime(new java.util.Date(Result)))
+ Result -= jtz.getDSTSavings();
+
+ } else {
+ if (mTimeZone != null) {
+ // subtract the offset
+ Result -= mTimeZone.getRawOffset();
+
+ // remove dst
+ if (mTimeZone.inDaylightTime(new java.util.Date(Result)))
+ Result -= mTimeZone.getDSTSavings();
+ } else {
+ // unknown Time?
+ // treat as local time, should not happen too often :)
+ Result = new DateTime(strDate).getTime();
+ }
+ }
+ } else {
+ if (strDate.endsWith("Z")) {
+ // GMT or UTC
+ Result = new DateTime(strDate).getTime();
+ } else {
+ // unknown Time?
+ // treat as local time, should not happen too often :)
+ Result = new DateTime(strDate).getTime();
+ }
+ }
+
+ } catch (ParseException e) {
+ e.printStackTrace();
+ }
+
+ return Result;
+ }
+
+ private long getStartTime() {
+ long Result = 0;
+ Property prop;
+ /*
+ * DTSTART;TZID=Europe/Berlin:20120425T080000
+ * DTSTART;TZID=(GMT+01.00) Sarajevo/Warsaw/Zagreb:20120305T104500
+ * DTSTART:20120308T093000Z
+ * DTSTART;VALUE=DATE:20120709
+ * DTSTART;TZID=Europe/Berlin:20130330T100000
+ */
+
+ prop = calendarComponent.getProperty(Property.DTSTART);
+ if (prop != null) {
+ Parameter propTZ = prop.getParameter(Property.TZID);
+ if (propTZ != null)
+ mstrTimeZoneStart = propTZ.getValue();
+ Result = getTimestamp(prop);
+ }
+
+ return Result;
+ }
+
+ private long getEndTime() {
+ long Result = 0;
+ Property propDtEnd;
+ Property propDuration;
+
+ propDtEnd = calendarComponent.getProperty(Property.DTEND);
+ propDuration = calendarComponent.getProperty(Property.DURATION);
+ if (propDtEnd != null) {
+ Parameter propTZ = propDtEnd.getParameter(Property.TZID);
+ if (propTZ != null)
+ mstrTimeZoneEnd = propTZ.getValue();
+ Result = getTimestamp(propDtEnd);
+
+ } else if (propDuration != null) {
+ Result = 0;
+ long Start = this.getStartTime();
+ String strDuration = propDuration.getValue();
+ Dur dur = new Dur(strDuration);
+ Result = Start
+ + dur.getSeconds() * 1000
+ + dur.getMinutes() * 60 * 1000
+ + dur.getHours() * 60 * 60 * 1000
+ + dur.getDays() * 60 * 60 * 24 * 1000;
+
+ mstrTimeZoneEnd = mstrTimeZoneStart;
+ }
+
+
+ return Result;
+ }
+
+ private String getTimeZoneStart() {
+ String Result = "GMT";
+
+ if (!mstrTimeZoneStart.equals("")) {
+ Result = mstrTimeZoneStart;
+ }
+
+ return Result;
+ }
+
+ private String getTimeZoneEnd() {
+ String Result = "GMT";
+
+ if (!mstrTimeZoneEnd.equals("")) {
+ Result = mstrTimeZoneEnd;
+ }
+
+ return Result;
+ }
+
+
+ private String getDuration() {
+ String Result = "";
+ Property prop = calendarComponent.getProperty("DURATION");
+
+ if (prop != null) {
+ //DURATION:PT1H
+ Result = prop.getValue();
+ } else {
+ // no DURATION given by this event. we have to calculate it by ourself
+ Result = "P";
+ long Start = this.getStartTime();
+ long End = this.getEndTime();
+ long Duration = 0;
+ if (End != 0)
+ Duration = (End - Start) / 1000; // get rid of the milliseconds, they cann't be described with RFC2445-Duration
+
+ if (Duration < 0) {
+ Duration = 0;
+ }
+ int Days = (int) Math.ceil(Duration / 24 / 60 / 60);
+ int Hours = (int) Math.ceil((Duration - (Days * 24 * 60 * 60)) / 60 / 60);
+ int Minutes = (int) Math.ceil((Duration - (Days * 24 * 60 * 60) - (Hours * 60 * 60)) / 60);
+ int Seconds = (int) (Duration - (Days * 24 * 60 * 60) - (Hours * 60 * 60) - (Minutes * 60));
+
+ if (Days > 0)
+ Result += String.valueOf(Days) + "D";
+
+ if (!mAllDay) {
+ //if a ALL_DAY event occurs, there is no need for hours, minutes and seconds (Android doesn't understand them)
+ Result += "T";
+ Result += String.valueOf(Hours) + "H";
+ Result += String.valueOf(Minutes) + "M";
+ Result += String.valueOf(Seconds) + "S";
+ }
+ }
+
+ return Result;
+ }
+
+ private int getAllDay() {
+ int Result = 0;
+
+ if (mAllDay)
+ Result = 1;
+
+ return Result;
+ }
+
+ /**
+ * opens the first items of the event
+ * @return success of this function
+ * @see AndroidEvent#createIcs()
+ * @see CalendarEvent#fetchBody()
+ * @throws CaldavProtocolException
+ * @throws IOException
+ * @throws ParserException
+ */
+ private boolean parseIcs() throws CaldavProtocolException, IOException, ParserException {
+ boolean Error = false;
+
+ CalendarBuilder builder = new CalendarBuilder();
+ CompatibilityHints.setHintEnabled(CompatibilityHints.KEY_RELAXED_UNFOLDING, true);
+ CompatibilityHints.setHintEnabled(CompatibilityHints.KEY_RELAXED_PARSING, true);
+ CompatibilityHints.setHintEnabled(CompatibilityHints.KEY_RELAXED_VALIDATION, true);
+ CompatibilityHints.setHintEnabled(CompatibilityHints.KEY_OUTLOOK_COMPATIBILITY, true);
+
+ StringReader reader = new StringReader(this.stringIcs);
+ try {
+ this.calendar = builder.build(reader);
+ } catch (ParserException ex) {
+ // ical4j fails with this: "Cannot set timezone for UTC properties"
+ // CREATED;TZID=America/New_York:20130129T140250
+ Error = true;
+ }
+
+ if (!Error) {
+ ComponentList components = null;
+ components = this.calendar.getComponents(Component.VEVENT);
+ if (components.size() == 0) {
+ components = this.calendar.getComponents(Component.VTODO);
+ if (components.size() == 0) {
+ throw new CaldavProtocolException("unknown events in ICS");
+ } else {
+ Log.e(TAG, "unsupported event TODO in ICS");
+ Error = true;
+ }
+ } else if (components.size() > 1) {
+ Log.e(TAG, "Several events in ICS -> only first will be processed");
+ }
+
+ // get the TimeZone information
+ Component mCom = this.calendar.getComponent(Component.VTIMEZONE);
+ if (mCom != null)
+ mVTimeZone = (VTimeZone) this.calendar.getComponent(Component.VTIMEZONE);
+ if (mVTimeZone != null)
+ mTimeZone = new TimeZone(mVTimeZone);
+
+ if (!Error)
+ calendarComponent = (Component) components.get(0);
+ }
+
+ return !Error;
+ }
+
+ /**
+ * searches for an android event
+ * @param androidCalendar
+ * @return the android event
+ * @throws RemoteException
+ */
+ public AndroidEvent getAndroidEvent(DavCalendar androidCalendar) throws RemoteException {
+ boolean Error = false;
+ Uri uriEvents = Events.CONTENT_URI;
+ Uri uriAttendee = Attendees.CONTENT_URI;
+ Uri uriReminder = Reminders.CONTENT_URI;
+ AndroidEvent androidEvent = null;
+
+ String selection = "(" + Events._SYNC_ID + " = ?)";
+ String[] selectionArgs = new String[] {this.getUri().toString()};
+ Cursor curEvent = this.mProvider.query(uriEvents, null, selection, selectionArgs, null);
+
+ Cursor curAttendee = null;
+ Cursor curReminder = null;
+
+ if (curEvent == null) {
+ Error = true;
+ }
+ if (!Error) {
+ if (curEvent.getCount() == 0) {
+ Error = true;
+ }
+ }
+ if (!Error) {
+ curEvent.moveToNext();
+
+ long EventID = curEvent.getLong(curEvent.getColumnIndex(Events._ID));
+ Uri returnedUri = ContentUris.withAppendedId(uriEvents, EventID);
+
+ //androidEvent = new AndroidEvent(this.mAccount, this.mProvider, returnedUri, androidCalendar.getAndroidCalendarUri());
+ androidEvent = new AndroidEvent(returnedUri, androidCalendar.getAndroidCalendarUri());
+ androidEvent.readContentValues(curEvent);
+
+ selection = "(" + Attendees.EVENT_ID + " = ?)";
+ selectionArgs = new String[] {String.valueOf(EventID)};
+ curAttendee = this.mProvider.query(uriAttendee, null, selection, selectionArgs, null);
+ selection = "(" + Reminders.EVENT_ID + " = ?)";
+ selectionArgs = new String[] {String.valueOf(EventID)};
+ curReminder = this.mProvider.query(uriReminder, null, selection, selectionArgs, null);
+ androidEvent.readAttendees(curAttendee);
+ androidEvent.readReminder(curReminder);
+ curAttendee.close();
+ curReminder.close();
+ }
+ curEvent.close();
+
+ return androidEvent;
+ }
+
+ /**
+ * creates a new androidEvent from a given calendarEvent
+ * @param androidCalendar
+ * @return
+ * @throws ClientProtocolException
+ * @throws IOException
+ * @throws CaldavProtocolException
+ * @throws RemoteException
+ * @throws ParserException
+ * @see {@link SyncAdapter#synchroniseEvents(CaldavFacade, Account, ContentProviderClient, Uri, DavCalendar, SyncStats)}
+ */
+ public boolean createAndroidEvent(DavCalendar androidCalendar) throws ClientProtocolException, IOException, CaldavProtocolException, RemoteException, ParserException {
+ boolean Result = false;
+ boolean BodyFetched = this.fetchBody();
+ int CountAttendees = 0;
+ int CountReminders = 0;
+
+ if (BodyFetched) {
+ //calendarEvent.readContentValues(calendarUri);
+ this.readContentValues();
+ this.setAndroidCalendarId(ContentUris.parseId(androidCalendar.getAndroidCalendarUri()));
+
+ Uri uri = this.mProvider.insert(asSyncAdapter(Events.CONTENT_URI, this.mAccount.name, this.mAccount.type), this.ContentValues);
+ this.setAndroidEventUri(uri);
+
+ Log.d(TAG, "Creating calendar event for " + uri.toString());
+
+ //check the attendees
+ java.util.ArrayList AttendeeList = this.getAttandees();
+ for (ContentValues Attendee : AttendeeList) {
+ this.mProvider.insert(Attendees.CONTENT_URI, Attendee);
+ CountAttendees += 1;
+ }
+
+ //check the reminders
+ java.util.ArrayList ReminderList = this.getReminders();
+ for (ContentValues Reminder : ReminderList) {
+ this.mProvider.insert(Reminders.CONTENT_URI, Reminder);
+ CountReminders += 1;
+ }
+
+ if ((CountAttendees > 0) || (CountReminders > 0)) {
+ //the events gets dirty when attendees or reminders were added
+ AndroidEvent androidEvent = this.getAndroidEvent(androidCalendar);
+
+ androidEvent.ContentValues.put(Events.DIRTY, 0);
+ int RowCount = this.mProvider.update(asSyncAdapter(androidEvent.getUri(), this.mAccount.name, this.mAccount.type), androidEvent.ContentValues, null, null);
+ Result = (RowCount == 1);
+ } else {
+ Result = true;
+ }
+
+
+ }
+ return Result;
+ }
+
+ /**
+ * the android event is getting updated
+ * @param provider
+ * @param account
+ * @param androidEvent
+ * @param calendarEvent
+ * @return
+ * @throws ClientProtocolException
+ * @throws IOException
+ * @throws CaldavProtocolException
+ * @throws RemoteException
+ * @throws ParserException
+ * @see {@link SyncAdapter#synchroniseEvents(CaldavFacade, Account, ContentProviderClient, Uri, Calendar, SyncStats)}
+ */
+ public boolean updateAndroidEvent(AndroidEvent androidEvent) throws ClientProtocolException, IOException, CaldavProtocolException, RemoteException, ParserException {
+ boolean BodyFetched = this.fetchBody();
+ int Rows = 0;
+
+ if (BodyFetched) {
+ this.readContentValues();
+ this.setAndroidCalendarId(androidEvent.getAndroidCalendarId());
+ this.setAndroidEventUri(androidEvent.getUri());
+
+ Log.d(TAG, "AndroidEvent is dirty: " + androidEvent.ContentValues.getAsString(Events.DIRTY));
+
+ if (androidEvent.checkEventValuesChanged(this.ContentValues)) {
+ // just set the raw data from server event into android event
+ if (androidEvent.ContentValues.containsKey(Event.RAWDATA))
+ androidEvent.ContentValues.remove(Event.RAWDATA);
+ androidEvent.ContentValues.put(Event.RAWDATA, this.ContentValues.getAsString(Event.RAWDATA));
+
+ //update the attendees and reminders
+ this.updateAndroidAttendees();
+ this.updateAndroidReminder();
+
+ androidEvent.ContentValues.put(Events.DIRTY, 0); // the event is now in sync
+ Log.d(TAG, "Update calendar event: for "+androidEvent.getUri());
+
+ Rows = mProvider.update(asSyncAdapter(androidEvent.getUri(), mAccount.name, mAccount.type), androidEvent.ContentValues, null, null);
+ //Log.i(TAG, "Updated calendar event: rows effected " + Rows.toString());
+ } else {
+ Log.d(TAG, "Update calendar event not needed: for "+androidEvent.getUri());
+ }
+ }
+ return (Rows == 1);
+ }
+
+ /**
+ * updates the attendees from a calendarEvent to its androidEvent.
+ * the calendarEvent has to know its androidEvent via {@link CalendarEvent#setAndroidEventUri(Uri)}
+ * @param provider
+ * @return
+ * @see SyncAdapter#updateAndroidEvent(ContentProviderClient, Account, AndroidEvent, CalendarEvent)
+ */
+ private boolean updateAndroidAttendees() {
+ boolean Result = false;
+
+ try {
+ String mSelectionClause = "(" + Attendees.EVENT_ID + " = ?)";
+ String[] mSelectionArgs = {Long.toString(ContentUris.parseId(this.getAndroidEventUri())) };
+ int RowDelete;
+ RowDelete = this.mProvider.delete(Attendees.CONTENT_URI, mSelectionClause, mSelectionArgs);
+ Log.d(TAG, "Attendees Deleted:" + String.valueOf(RowDelete));
+
+ java.util.ArrayList AttendeeList = this.getAttandees();
+ for (ContentValues Attendee : AttendeeList) {
+ this.mProvider.insert(Attendees.CONTENT_URI, Attendee);
+ }
+ Log.d(TAG, "Attendees Inserted:" + String.valueOf(AttendeeList.size()));
+ Result = true;
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+
+ return Result;
+ }
+
+ /**
+ * update the reminders from a calendarEvent to its androidEvent.
+ * the calendarEvent has to know its androidEvent via {@link CalendarEvent#setAndroidEventUri(Uri)}
+ * @param provider
+ * @return
+ * @see SyncAdapter#updateAndroidEvent(ContentProviderClient, Account, AndroidEvent, CalendarEvent)
+ */
+ private boolean updateAndroidReminder() {
+ boolean Result = false;
+
+ try {
+ String mSelectionClause = "(" + Reminders.EVENT_ID + " = ?)";
+ String[] mSelectionArgs = {Long.toString(ContentUris.parseId(this.getAndroidEventUri())) };
+ int RowDelete;
+ RowDelete = this.mProvider.delete(Reminders.CONTENT_URI, mSelectionClause, mSelectionArgs);
+ Log.d(TAG, "Reminders Deleted:" + String.valueOf(RowDelete));
+
+
+ Uri ReminderUri;
+ java.util.ArrayList ReminderList = this.getReminders();
+ for (ContentValues Reminder : ReminderList) {
+ ReminderUri = this.mProvider.insert(Reminders.CONTENT_URI, Reminder);
+ System.out.println(ReminderUri);
+ }
+ Log.d(TAG, "Reminders Inserted:" + String.valueOf(ReminderList.size()));
+
+ Result = true;
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+
+ return Result;
+ }
+
+ private static Uri asSyncAdapter(Uri uri, String account, String accountType) {
+ return uri.buildUpon()
+ .appendQueryParameter(android.provider.CalendarContract.CALLER_IS_SYNCADAPTER,"true")
+ .appendQueryParameter(Calendars.ACCOUNT_NAME, account)
+ .appendQueryParameter(Calendars.ACCOUNT_TYPE, accountType).build();
+ }
+}
+