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: