src/org/gege/caldavsyncadapter/caldav/entities/CalendarEvent.java

Tue, 10 Feb 2015 18:12:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 10 Feb 2015 18:12:00 +0100
changeset 0
fb9019fb1bf7
permissions
-rw-r--r--

Import initial revisions of existing project AndroidCaldavSyncAdapater,
forked from upstream repository at 27e8a0f8495c92e0780d450bdf0c7cec77a03a55.

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

mercurial