Sat, 14 Feb 2015 00:30:34 +0100
Improve style and maintenance while aknowledging bugfix ical4j failure.
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.android.entities;
24 import android.database.Cursor;
25 import android.net.Uri;
26 import android.provider.CalendarContract.Attendees;
27 import android.provider.CalendarContract.Events;
28 import android.provider.CalendarContract.Reminders;
30 import net.fortuna.ical4j.model.Calendar;
31 import net.fortuna.ical4j.model.Component;
32 import net.fortuna.ical4j.model.ComponentList;
33 import net.fortuna.ical4j.model.Date;
34 import net.fortuna.ical4j.model.DateTime;
35 import net.fortuna.ical4j.model.Dur;
36 import net.fortuna.ical4j.model.ParameterList;
37 import net.fortuna.ical4j.model.Property;
38 import net.fortuna.ical4j.model.PropertyList;
39 import net.fortuna.ical4j.model.TimeZone;
40 import net.fortuna.ical4j.model.TimeZoneRegistry;
41 import net.fortuna.ical4j.model.TimeZoneRegistryFactory;
42 import net.fortuna.ical4j.model.component.VAlarm;
43 import net.fortuna.ical4j.model.component.VEvent;
44 import net.fortuna.ical4j.model.parameter.Cn;
45 import net.fortuna.ical4j.model.parameter.PartStat;
46 import net.fortuna.ical4j.model.parameter.Role;
47 import net.fortuna.ical4j.model.parameter.Rsvp;
48 import net.fortuna.ical4j.model.parameter.Value;
49 import net.fortuna.ical4j.model.property.Action;
50 import net.fortuna.ical4j.model.property.Attendee;
51 import net.fortuna.ical4j.model.property.CalScale;
52 import net.fortuna.ical4j.model.property.Clazz;
53 import net.fortuna.ical4j.model.property.Description;
54 import net.fortuna.ical4j.model.property.DtEnd;
55 import net.fortuna.ical4j.model.property.DtStart;
56 import net.fortuna.ical4j.model.property.Duration;
57 import net.fortuna.ical4j.model.property.ExDate;
58 import net.fortuna.ical4j.model.property.ExRule;
59 import net.fortuna.ical4j.model.property.Location;
60 import net.fortuna.ical4j.model.property.Organizer;
61 import net.fortuna.ical4j.model.property.ProdId;
62 import net.fortuna.ical4j.model.property.RDate;
63 import net.fortuna.ical4j.model.property.RRule;
64 import net.fortuna.ical4j.model.property.Status;
65 import net.fortuna.ical4j.model.property.Summary;
66 import net.fortuna.ical4j.model.property.Trigger;
67 import net.fortuna.ical4j.model.property.Uid;
68 import net.fortuna.ical4j.model.property.Version;
70 import org.gege.caldavsyncadapter.caldav.entities.CalendarEvent;
72 import java.net.URISyntaxException;
73 import java.text.ParseException;
75 import static org.junit.Assert.*;
77 import com.europalab.caldavsyn.Appctxpro;
79 //import android.accounts.Account;
80 //import android.content.ContentProviderClient;
81 //import android.content.ContentValues;
82 //import android.content.SyncStats;
83 //import android.os.RemoteException;
84 //import android.provider.CalendarContract.Calendars;
85 //import org.gege.caldavsyncadapter.Event;
86 //import org.gege.caldavsyncadapter.caldav.CaldavFacade;
87 //import org.gege.caldavsyncadapter.caldav.entities.DavCalendar;
88 //import org.gege.caldavsyncadapter.syncadapter.SyncAdapter;
90 public class AndroidEvent extends org.gege.caldavsyncadapter.Event {
92 private Uri muri;
94 private Uri mAndroidCalendarUri;
96 /**
97 * the list of attendees
98 */
99 private PropertyList mAttendees = new PropertyList();
101 /**
102 * the list of reminders
103 */
104 private ComponentList mReminders = new ComponentList();
106 private Calendar mCalendar = null;
108 /* private Account mAccount = null;
109 private ContentProviderClient mProvider = null;*/
111 //public AndroidEvent(Account account, ContentProviderClient provider, Uri uri, Uri calendarUri) {
112 public AndroidEvent(Uri uri, Uri calendarUri) {
113 super();
114 this.setUri(uri);
115 /* this.mAccount = account;
116 this.mProvider = provider;*/
117 //this.setCounterpartUri(calendarUri);
118 mAndroidCalendarUri = calendarUri;
119 }
121 public Calendar getIcsEvent() {
122 return mCalendar;
123 }
125 public String getETag() {
126 String Result = "";
127 if (this.ContentValues.containsKey(ETAG))
128 Result = this.ContentValues.getAsString(ETAG);
129 return Result;
130 }
132 public void setETag(String eTag) {
133 this.ContentValues.put(ETAG, eTag);
134 }
136 public Uri getUri() {
137 return muri;
138 }
140 public void setUri(Uri uri) {
141 this.muri = uri;
142 }
144 public Uri getAndroidCalendarUri() {
145 return mAndroidCalendarUri;
146 }
148 @Override
149 public String toString() {
150 return this.getUri().toString();
151 }
153 /**
154 * reads an android event from a given cursor into {@link AndroidEvent#ContentValues}
155 *
156 * @param cur the cursor with the event
157 * @return success of this funtion
158 * @see AndroidEvent#ContentValues
159 */
160 public boolean readContentValues(Cursor cur) {
161 this.setETag(cur.getString(cur.getColumnIndex(ETAG)));
163 this.ContentValues.put(Events.EVENT_TIMEZONE, cur.getString(cur.getColumnIndex(Events.EVENT_TIMEZONE)));
164 this.ContentValues.put(Events.EVENT_END_TIMEZONE, cur.getString(cur.getColumnIndex(Events.EVENT_END_TIMEZONE)));
165 this.ContentValues.put(Events.DTSTART, cur.getLong(cur.getColumnIndex(Events.DTSTART)));
166 this.ContentValues.put(Events.DTEND, cur.getLong(cur.getColumnIndex(Events.DTEND)));
167 this.ContentValues.put(Events.ALL_DAY, cur.getLong(cur.getColumnIndex(Events.ALL_DAY)));
168 this.ContentValues.put(Events.TITLE, cur.getString(cur.getColumnIndex(Events.TITLE)));
169 this.ContentValues.put(Events.CALENDAR_ID, cur.getString(cur.getColumnIndex(Events.CALENDAR_ID)));
170 this.ContentValues.put(Events._SYNC_ID, cur.getString(cur.getColumnIndex(Events._SYNC_ID)));
171 //this.ContentValues.put(Events.SYNC_DATA1, cur.getString(cur.getColumnIndex(Events.SYNC_DATA1))); //not needed here, eTag has already been read
172 this.ContentValues.put(Events.DESCRIPTION, cur.getString(cur.getColumnIndex(Events.DESCRIPTION)));
173 this.ContentValues.put(Events.EVENT_LOCATION, cur.getString(cur.getColumnIndex(Events.EVENT_LOCATION)));
174 this.ContentValues.put(Events.ACCESS_LEVEL, cur.getInt(cur.getColumnIndex(Events.ACCESS_LEVEL)));
176 this.ContentValues.put(Events.STATUS, cur.getInt(cur.getColumnIndex(Events.STATUS)));
178 this.ContentValues.put(Events.LAST_DATE, cur.getInt(cur.getColumnIndex(Events.LAST_DATE)));
179 this.ContentValues.put(Events.DURATION, cur.getString(cur.getColumnIndex(Events.DURATION)));
181 this.ContentValues.put(Events.RDATE, cur.getString(cur.getColumnIndex(Events.RDATE)));
182 this.ContentValues.put(Events.RRULE, cur.getString(cur.getColumnIndex(Events.RRULE)));
183 this.ContentValues.put(Events.EXRULE, cur.getString(cur.getColumnIndex(Events.EXRULE)));
184 this.ContentValues.put(Events.EXDATE, cur.getString(cur.getColumnIndex(Events.EXDATE)));
185 this.ContentValues.put(Events.DIRTY, cur.getInt(cur.getColumnIndex(Events.DIRTY)));
186 this.ContentValues.put(UID, cur.getString(cur.getColumnIndex(UID)));
187 this.ContentValues.put(RAWDATA, cur.getString(cur.getColumnIndex(RAWDATA)));
189 return true;
190 }
192 /**
193 * reads the attendees from a given cursor
194 *
195 * @param cur the cursor with the attendees
196 * @return success of this function
197 * @see AndroidEvent#mAttendees
198 */
199 public boolean readAttendees(Cursor cur) {
200 Attendee attendee = null;
201 Organizer organizer = null;
202 ParameterList paraList = null;
204 String Name = "";
205 Cn cn = null;
207 String Email = "";
209 int Relationship = 0;
212 int Status = 0;
213 PartStat partstat = null;
215 int Type = 0;
216 Role role = null;
218 try {
219 while (cur.moveToNext()) {
220 Name = cur.getString(cur.getColumnIndex(Attendees.ATTENDEE_NAME));
221 Email = cur.getString(cur.getColumnIndex(Attendees.ATTENDEE_EMAIL));
222 Relationship = cur.getInt(cur.getColumnIndex(Attendees.ATTENDEE_RELATIONSHIP));
223 Type = cur.getInt(cur.getColumnIndex(Attendees.ATTENDEE_TYPE));
224 Status = cur.getInt(cur.getColumnIndex(Attendees.ATTENDEE_STATUS));
226 if (Relationship == Attendees.RELATIONSHIP_ORGANIZER) {
227 organizer = new Organizer();
228 organizer.setValue("mailto:" + Email);
229 paraList = organizer.getParameters();
230 mAttendees.add(organizer);
231 } else {
232 attendee = new Attendee();
233 attendee.setValue("mailto:" + Email);
234 paraList = attendee.getParameters();
235 mAttendees.add(attendee);
236 }
238 Rsvp rsvp = new Rsvp(true);
239 paraList.add(rsvp);
241 cn = new Cn(Name);
242 paraList.add(cn);
244 if (Status == Attendees.ATTENDEE_STATUS_INVITED)
245 partstat = new PartStat(PartStat.NEEDS_ACTION.getValue());
246 else if (Status == Attendees.ATTENDEE_STATUS_ACCEPTED)
247 partstat = new PartStat(PartStat.ACCEPTED.getValue());
248 else if (Status == Attendees.ATTENDEE_STATUS_DECLINED)
249 partstat = new PartStat(PartStat.DECLINED.getValue());
250 else if (Status == Attendees.ATTENDEE_STATUS_NONE)
251 partstat = new PartStat(PartStat.COMPLETED.getValue());
252 else if (Status == Attendees.ATTENDEE_STATUS_TENTATIVE)
253 partstat = new PartStat(PartStat.TENTATIVE.getValue());
254 else
255 partstat = new PartStat(PartStat.NEEDS_ACTION.getValue());
256 paraList.add(partstat);
258 if (Type == Attendees.TYPE_OPTIONAL)
259 role = new Role(Role.OPT_PARTICIPANT.getValue());
260 else if (Type == Attendees.TYPE_NONE)
261 role = new Role(Role.NON_PARTICIPANT.getValue()); //regular participants in android are non required?
262 else if (Type == Attendees.TYPE_REQUIRED)
263 role = new Role(Role.REQ_PARTICIPANT.getValue());
264 else
265 role = new Role(Role.NON_PARTICIPANT.getValue());
266 paraList.add(role);
267 }
269 } catch (URISyntaxException e) {
270 e.printStackTrace();
271 }
272 return true;
273 }
275 /**
276 * reads the reminders from a given cursor
277 *
278 * @param cur the cursor with the reminders
279 * @return success of this function
280 */
281 public boolean readReminder(Cursor cur) {
282 int Method;
283 int Minutes;
284 VAlarm reminder;
285 while (cur.moveToNext()) {
286 reminder = new VAlarm();
287 Method = cur.getInt(cur.getColumnIndex(Reminders.METHOD));
288 Minutes = cur.getInt(cur.getColumnIndex(Reminders.MINUTES)) * -1;
291 Dur dur = new Dur(0, 0, Minutes, 0);
292 Trigger tri = new Trigger(dur);
293 Value val = new Value(Duration.DURATION);
294 tri.getParameters().add(val);
295 reminder.getProperties().add(tri);
297 Description desc = new Description();
298 desc.setValue("caldavsyncadapter standard description");
299 reminder.getProperties().add(desc);
302 if (Method == Reminders.METHOD_EMAIL)
303 reminder.getProperties().add(Action.EMAIL);
304 else
305 reminder.getProperties().add(Action.DISPLAY);
307 this.mReminders.add(reminder);
308 }
309 return true;
310 }
312 /**
313 * generates a new ics-file.
314 * uses {@link AndroidEvent#ContentValues} as source.
315 * this should only be used when a new event has been generated within android.
316 *
317 * @param strUid the UID for this event. example: UID:e6be67c6-eff0-44f8-a1a0-6c2cb1029944-caldavsyncadapter
318 * @return success of the function
319 * @see CalendarEvent#fetchBody()
320 */
321 public boolean createIcs(String strUid) {
322 boolean Result = false;
323 TimeZone timeZone = null;
324 // Hack to bootstrap a multithreaded class loader context
325 if (Thread.currentThread().getContextClassLoader() == null)
326 Thread.currentThread().setContextClassLoader(Appctxpro.getContext().getClassLoader());
327 TimeZoneRegistry registry = TimeZoneRegistryFactory.getInstance();
328 assertNotNull(registry);
329 registry.createRegistry();
330 //TODO: do not simply create the ics-file new. take into account the RAWDATA if available
331 /*
332 * dtstart=1365598800000
333 * dtend=1365602400000
334 * eventTimezone=Europe/Berlin
335 * eventEndTimezone=null
336 * duration=null
337 * allDay=0
338 * rrule=null
339 * rdate=null
340 * exrule=null
341 * exdate=null
342 * title=Einurlner Termin
343 * description=null
344 * eventLocation=null
345 * accessLevel=0
346 * eventStatus=0
347 *
348 * calendar_id=4
349 * lastDate=-197200128
350 * sync_data1=null
351 * _sync_id=null
352 * dirty=1
353 */
355 try {
356 mCalendar = new Calendar();
357 PropertyList propCalendar = mCalendar.getProperties();
358 propCalendar.add(new ProdId("-//Ben Fortuna//iCal4j 1.0//EN"));
359 propCalendar.add(Version.VERSION_2_0);
360 propCalendar.add(CalScale.GREGORIAN);
362 VEvent event = new VEvent();
363 mCalendar.getComponents().add(event);
364 PropertyList propEvent = event.getProperties();
366 // DTSTAMP -> is created by new VEvent() automatical
367 //na
369 // CREATED
370 //na
372 // LAST-MODIFIED
373 //na
375 // SEQUENCE
376 //na
378 // DTSTART
379 long lngStart = this.ContentValues.getAsLong(Events.DTSTART);
380 String strTZStart = this.ContentValues.getAsString(Events.EVENT_TIMEZONE);
381 boolean allDay = this.ContentValues.getAsBoolean(Events.ALL_DAY);
382 if (lngStart > 0) {
383 DtStart dtStart = new DtStart();
384 if (allDay) {
385 Date dateStart = new Date();
386 dateStart.setTime(lngStart);
387 dtStart.setDate(dateStart);
388 } else {
389 DateTime datetimeStart = new DateTime();
390 datetimeStart.setTime(lngStart);
391 dtStart.setDate(datetimeStart);
393 timeZone = registry.getTimeZone(strTZStart);
394 if (timeZone == null) {
395 java.util.TimeZone systemTimeZone = TimeZone.getTimeZone(strTZStart);
396 if (systemTimeZone == null) {
397 systemTimeZone = TimeZone.getDefault();
398 }
399 timeZone = registry.getTimeZone(systemTimeZone.getID());
400 }
401 dtStart.setTimeZone(timeZone);
403 // no timezone information for allDay events
404 mCalendar.getComponents().add(timeZone.getVTimeZone());
405 }
406 propEvent.add(dtStart);
407 }
409 // DTEND
410 long lngEnd = this.ContentValues.getAsLong(Events.DTEND);
411 String strTZEnd = this.ContentValues.getAsString(Events.EVENT_END_TIMEZONE);
412 if (strTZEnd == null)
413 strTZEnd = strTZStart;
414 if (lngEnd > 0) {
415 DtEnd dtEnd = new DtEnd();
416 if (allDay) {
417 Date dateEnd = new Date();
418 dateEnd.setTime(lngEnd);
419 dtEnd.setDate(dateEnd);
420 } else {
421 DateTime datetimeEnd = new DateTime();
422 datetimeEnd.setTime(lngEnd);
423 dtEnd.setDate(datetimeEnd);
424 if (strTZEnd != null)
425 timeZone = registry.getTimeZone(strTZEnd);
426 dtEnd.setTimeZone(timeZone);
427 }
428 propEvent.add(dtEnd);
429 }
431 // DURATION
432 if (this.ContentValues.containsKey(Events.DURATION)) {
433 String strDuration = this.ContentValues.getAsString(Events.DURATION);
434 if (strDuration != null) {
435 Duration duration = new Duration();
436 duration.setValue(strDuration);
438 propEvent.add(duration);
439 }
440 }
442 //RRULE
443 if (this.ContentValues.containsKey(Events.RRULE)) {
444 String strRrule = this.ContentValues.getAsString(Events.RRULE);
445 if (strRrule != null) {
446 if (!strRrule.equals("")) {
447 RRule rrule = new RRule();
448 rrule.setValue(strRrule);
449 propEvent.add(rrule);
450 }
451 }
452 }
454 //RDATE
455 if (this.ContentValues.containsKey(Events.RDATE)) {
456 String strRdate = this.ContentValues.getAsString(Events.RDATE);
457 if (strRdate != null) {
458 if (!strRdate.equals("")) {
459 RDate rdate = new RDate();
460 rdate.setValue(strRdate);
461 propEvent.add(rdate);
462 }
463 }
464 }
466 //EXRULE
467 if (this.ContentValues.containsKey(Events.EXRULE)) {
468 String strExrule = this.ContentValues.getAsString(Events.EXRULE);
469 if (strExrule != null) {
470 if (!strExrule.equals("")) {
471 ExRule exrule = new ExRule();
472 exrule.setValue(strExrule);
473 propEvent.add(exrule);
474 }
475 }
476 }
478 //EXDATE
479 if (this.ContentValues.containsKey(Events.EXDATE)) {
480 String strExdate = this.ContentValues.getAsString(Events.EXDATE);
481 if (strExdate != null) {
482 if (!strExdate.equals("")) {
483 ExDate exdate = new ExDate();
484 exdate.setValue(strExdate);
485 propEvent.add(exdate);
486 }
487 }
488 }
490 //SUMMARY
491 if (this.ContentValues.containsKey(Events.TITLE)) {
492 String strTitle = this.ContentValues.getAsString(Events.TITLE);
493 if (strTitle != null) {
494 Summary summary = new Summary(strTitle);
495 propEvent.add(summary);
496 }
497 }
499 //DESCIPTION
500 if (this.ContentValues.containsKey(Events.DESCRIPTION)) {
501 String strDescription = this.ContentValues.getAsString(Events.DESCRIPTION);
502 if (strDescription != null) {
503 if (!strDescription.equals("")) {
504 Description description = new Description(strDescription);
505 propEvent.add(description);
506 }
507 }
508 }
510 //LOCATION
511 if (this.ContentValues.containsKey(Events.EVENT_LOCATION)) {
512 String strLocation = this.ContentValues.getAsString(Events.EVENT_LOCATION);
513 if (strLocation != null) {
514 if (!strLocation.equals("")) {
515 Location location = new Location(strLocation);
516 propEvent.add(location);
517 }
518 }
519 }
521 //CLASS / ACCESS_LEVEL
522 if (this.ContentValues.containsKey(Events.ACCESS_LEVEL)) {
523 int accessLevel = this.ContentValues.getAsInteger(Events.ACCESS_LEVEL);
524 Clazz clazz = new Clazz();
525 if (accessLevel == Events.ACCESS_PUBLIC)
526 clazz.setValue(Clazz.PUBLIC.getValue());
527 else if (accessLevel == Events.ACCESS_PRIVATE)
528 clazz.setValue(Clazz.PRIVATE.getValue());
529 else if (accessLevel == Events.ACCESS_CONFIDENTIAL)
530 clazz.setValue(Clazz.CONFIDENTIAL.getValue());
531 else
532 clazz.setValue(Clazz.PUBLIC.getValue());
534 propEvent.add(clazz);
535 }
537 //STATUS
538 if (this.ContentValues.containsKey(Events.STATUS)) {
539 int intStatus = this.ContentValues.getAsInteger(Events.STATUS);
540 if (intStatus > -1) {
541 Status status = new Status();
542 if (intStatus == Events.STATUS_CANCELED)
543 status.setValue(Status.VEVENT_CANCELLED.getValue());
544 else if (intStatus == Events.STATUS_CONFIRMED)
545 status.setValue(Status.VEVENT_CONFIRMED.getValue());
546 else if (intStatus == Events.STATUS_TENTATIVE)
547 status.setValue(Status.VEVENT_TENTATIVE.getValue());
549 propEvent.add(status);
550 }
551 }
553 //UID
554 Uid uid = new Uid(strUid);
555 propEvent.add(uid);
557 // Attendees
558 if (mAttendees.size() > 0) {
559 for (Object objProp : mAttendees) {
560 Property prop = (Property) objProp;
561 propEvent.add(prop);
562 }
563 }
565 // Reminders
566 if (mReminders.size() > 0) {
567 for (Object objComp : mReminders) {
568 VAlarm com = (VAlarm) objComp;
569 event.getAlarms().add(com);
570 }
571 }
573 } catch (ParseException e) {
574 e.printStackTrace();
575 }
577 return Result;
578 }
580 /**
581 * marks the android event as already handled
582 * @return
583 * @see AndroidEvent#cInternalTag
584 * @see SyncAdapter#synchroniseEvents(CaldavFacade, Account, ContentProviderClient, Uri, DavCalendar, SyncStats)
585 * @throws RemoteException
586 */
587 /* public boolean tagAndroidEvent() throws RemoteException {
589 ContentValues values = new ContentValues();
590 values.put(Event.INTERNALTAG, 1);
592 int RowCount = this.mProvider.update(asSyncAdapter(this.getUri(), this.mAccount.name, this.mAccount.type), values, null, null);
593 //Log.e(TAG,"Rows updated: " + RowCount.toString());
595 return (RowCount == 1);
596 }*/
598 /* private static Uri asSyncAdapter(Uri uri, String account, String accountType) {
599 return uri.buildUpon()
600 .appendQueryParameter(android.provider.CalendarContract.CALLER_IS_SYNCADAPTER,"true")
601 .appendQueryParameter(Calendars.ACCOUNT_NAME, account)
602 .appendQueryParameter(Calendars.ACCOUNT_TYPE, accountType).build();
603 }*/
604 }