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