|
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 */ |
|
21 |
|
22 package org.gege.caldavsyncadapter.caldav.entities; |
|
23 |
|
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; |
|
32 |
|
33 import javax.xml.parsers.ParserConfigurationException; |
|
34 import javax.xml.parsers.SAXParser; |
|
35 import javax.xml.parsers.SAXParserFactory; |
|
36 |
|
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; |
|
58 |
|
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; |
|
73 |
|
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; |
|
87 |
|
88 |
|
89 |
|
90 public class CalendarEvent extends org.gege.caldavsyncadapter.Event { |
|
91 private static final String TAG = "CalendarEvent"; |
|
92 |
|
93 private String stringIcs; |
|
94 |
|
95 private Calendar calendar; |
|
96 |
|
97 private Component calendarComponent; |
|
98 |
|
99 private String eTag; |
|
100 private URI muri; |
|
101 private Uri mAndroidEventUri; |
|
102 public URL calendarURL; |
|
103 |
|
104 private boolean mAllDay = false; |
|
105 private VTimeZone mVTimeZone = null; |
|
106 private TimeZone mTimeZone = null; |
|
107 |
|
108 private String mstrTimeZoneStart = ""; |
|
109 private String mstrTimeZoneEnd = ""; |
|
110 |
|
111 private Account mAccount = null; |
|
112 private ContentProviderClient mProvider = null; |
|
113 |
|
114 public CalendarEvent(Account account, ContentProviderClient provider) { |
|
115 this.mAccount = account; |
|
116 this.mProvider = provider; |
|
117 } |
|
118 |
|
119 public String getETag() { |
|
120 return eTag; |
|
121 } |
|
122 |
|
123 public void setETag(String eTag) { |
|
124 this.eTag = eTag; |
|
125 } |
|
126 |
|
127 public URI getUri() { |
|
128 return muri; |
|
129 } |
|
130 |
|
131 public void setUri(URI uri) { |
|
132 this.muri = uri; |
|
133 } |
|
134 |
|
135 public void setICSasString(String ics) { |
|
136 this.stringIcs = ics; |
|
137 } |
|
138 |
|
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))); |
|
154 |
|
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 } |
|
181 |
|
182 this.stringIcs = ics; |
|
183 return Result; |
|
184 } |
|
185 |
|
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 } |
|
195 |
|
196 /** |
|
197 * gets the Uri of the android event |
|
198 * @return |
|
199 */ |
|
200 public Uri getAndroidEventUri() { |
|
201 return mAndroidEventUri; |
|
202 } |
|
203 |
|
204 |
|
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()); |
|
215 |
|
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); |
|
231 |
|
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); |
|
246 |
|
247 return true; |
|
248 } |
|
249 |
|
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; |
|
261 |
|
262 //replaced fetchEvent() with getEvent() |
|
263 //CaldavFacade.fetchEvent(this); |
|
264 CaldavFacade.getEvent(this); |
|
265 |
|
266 boolean Parse = this.parseIcs(); |
|
267 if (!Parse) |
|
268 Error = true; |
|
269 |
|
270 return !Error; |
|
271 } |
|
272 |
|
273 public java.util.ArrayList<ContentValues> getReminders() { |
|
274 java.util.ArrayList<ContentValues> Result = new java.util.ArrayList<ContentValues>(); |
|
275 ContentValues Reminder; |
|
276 |
|
277 /* |
|
278 * http://sourceforge.net/tracker/?func=detail&aid=3021704&group_id=107024&atid=646395 |
|
279 */ |
|
280 |
|
281 net.fortuna.ical4j.model.component.VEvent event = (VEvent) this.calendarComponent; |
|
282 |
|
283 //ComponentList ComList = this.calendar.getComponents(Component.VALARM); |
|
284 ComponentList ComList = event.getAlarms(); |
|
285 |
|
286 if (ComList != null) { |
|
287 for (Object objCom : ComList) { |
|
288 Component Com = (Component) objCom; |
|
289 Reminder = new ContentValues(); |
|
290 |
|
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")) |
|
296 |
|
297 int intDuration = Duration.getMinutes() + Duration.getHours() * 60 + Duration.getDays() * 60 * 24; |
|
298 |
|
299 Reminder.put(Reminders.EVENT_ID, ContentUris.parseId(mAndroidEventUri)); |
|
300 Reminder.put(Reminders.METHOD, Reminders.METHOD_ALERT); |
|
301 Reminder.put(Reminders.MINUTES, intDuration); |
|
302 |
|
303 Result.add(Reminder); |
|
304 } |
|
305 } |
|
306 } |
|
307 return Result; |
|
308 } |
|
309 |
|
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 } |
|
331 |
|
332 |
|
333 return Result; |
|
334 } |
|
335 |
|
336 private String mstrcIcalPropertyError = "net.fortunal.ical4j.invalid:"; |
|
337 private ContentValues ReadAttendeeProperties(Property property, String Type) { |
|
338 ContentValues Attendee = null; |
|
339 |
|
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); |
|
346 |
|
347 String strCN = ""; |
|
348 String strROLE = ""; |
|
349 String strCUTYPE = ""; |
|
350 String strValue = property.getValue().replace("mailto:", ""); |
|
351 String strPARTSTAT = ""; |
|
352 |
|
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 } |
|
361 |
|
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(); |
|
370 |
|
371 if (strCN.equals("")) { |
|
372 if (!strValue.equals("")) { |
|
373 strCN = strValue; |
|
374 } |
|
375 } |
|
376 |
|
377 if (!strCN.equals("")) { |
|
378 if (strCUTYPE.equals("") || strCUTYPE.equals("INDIVIDUAL")) { |
|
379 Attendee = new ContentValues(); |
|
380 |
|
381 Attendee.put(Attendees.EVENT_ID, ContentUris.parseId(mAndroidEventUri)); |
|
382 |
|
383 Attendee.put(Attendees.ATTENDEE_NAME, strCN); |
|
384 Attendee.put(Attendees.ATTENDEE_EMAIL, strValue); |
|
385 |
|
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); |
|
396 |
|
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); |
|
403 |
|
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); |
|
416 |
|
417 } |
|
418 } |
|
419 |
|
420 return Attendee; |
|
421 } |
|
422 |
|
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 } |
|
436 |
|
437 return Result; |
|
438 } |
|
439 |
|
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 } |
|
453 |
|
454 return Result; |
|
455 } |
|
456 |
|
457 private String getDescription() { |
|
458 String Result = null; |
|
459 Property property = calendarComponent.getProperty(Property.DESCRIPTION); |
|
460 if (property != null) |
|
461 Result = property.getValue(); |
|
462 |
|
463 return Result; |
|
464 } |
|
465 |
|
466 private String getLocation() { |
|
467 String Result = null; |
|
468 Property property = calendarComponent.getProperty(Property.LOCATION); |
|
469 if (property != null) |
|
470 Result = property.getValue(); |
|
471 |
|
472 return Result; |
|
473 } |
|
474 |
|
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 } |
|
482 |
|
483 private String getRRule() { |
|
484 String Result = null; |
|
485 Property property = calendarComponent.getProperty(Property.RRULE); |
|
486 if (property != null) |
|
487 Result = property.getValue(); |
|
488 |
|
489 return Result; |
|
490 } |
|
491 |
|
492 private String getExRule() { |
|
493 String Result = null; |
|
494 Property property = calendarComponent.getProperty(Property.EXRULE); |
|
495 if (property != null) |
|
496 Result = property.getValue(); |
|
497 |
|
498 return Result; |
|
499 } |
|
500 |
|
501 private String getRDate() { |
|
502 String Result = null; |
|
503 |
|
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 } |
|
512 |
|
513 return Result; |
|
514 } |
|
515 |
|
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 } |
|
526 |
|
527 return Result; |
|
528 } |
|
529 |
|
530 private String getExDate() { |
|
531 String Result = null; |
|
532 |
|
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 } |
|
541 |
|
542 return Result; |
|
543 } |
|
544 |
|
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 } |
|
555 |
|
556 return Result; |
|
557 } |
|
558 |
|
559 private String getUid() { |
|
560 String Result = ""; |
|
561 Property prop = calendarComponent.getProperty(Property.UID); |
|
562 if (prop != null) { |
|
563 Result = prop.getValue(); |
|
564 } |
|
565 |
|
566 return Result; |
|
567 } |
|
568 |
|
569 private Long getTimestamp(Property prop) { |
|
570 Long Result = null; |
|
571 String strTimeZone = ""; |
|
572 //TimeZone timeZone = null; |
|
573 |
|
574 try { |
|
575 String strDate = prop.getValue(); |
|
576 |
|
577 Parameter parAllDay = prop.getParameter("VALUE"); |
|
578 if (parAllDay != null) { |
|
579 if (parAllDay.getValue().equals("DATE")) { |
|
580 mAllDay = true; |
|
581 strDate += "T000000Z"; |
|
582 } |
|
583 } |
|
584 |
|
585 Parameter propTZ = prop.getParameter(Property.TZID); |
|
586 if (propTZ != null) |
|
587 strTimeZone = propTZ.getValue(); |
|
588 |
|
589 //TimeZoneRegistry registry = TimeZoneRegistryFactory.getInstance().createRegistry(); |
|
590 //if (!strTimeZone.equals("")) |
|
591 // timeZone = registry.getTimeZone(strTimeZone); |
|
592 |
|
593 //if (timeZone != null) { |
|
594 if (!strTimeZone.equals("")) { |
|
595 //Result = new DateTime(strDate, timeZone); |
|
596 //Result1 = Result.getTime(); |
|
597 |
|
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)); |
|
605 |
|
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(); |
|
612 |
|
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); |
|
621 |
|
622 // subtract the offset |
|
623 Result -= jtz.getRawOffset(); |
|
624 |
|
625 // remove dst |
|
626 if (jtz.inDaylightTime(new java.util.Date(Result))) |
|
627 Result -= jtz.getDSTSavings(); |
|
628 |
|
629 } else { |
|
630 if (mTimeZone != null) { |
|
631 // subtract the offset |
|
632 Result -= mTimeZone.getRawOffset(); |
|
633 |
|
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 } |
|
653 |
|
654 } catch (ParseException e) { |
|
655 e.printStackTrace(); |
|
656 } |
|
657 |
|
658 return Result; |
|
659 } |
|
660 |
|
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 */ |
|
671 |
|
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 } |
|
679 |
|
680 return Result; |
|
681 } |
|
682 |
|
683 private long getEndTime() { |
|
684 long Result = 0; |
|
685 Property propDtEnd; |
|
686 Property propDuration; |
|
687 |
|
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); |
|
695 |
|
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; |
|
706 |
|
707 mstrTimeZoneEnd = mstrTimeZoneStart; |
|
708 } |
|
709 |
|
710 |
|
711 return Result; |
|
712 } |
|
713 |
|
714 private String getTimeZoneStart() { |
|
715 String Result = "GMT"; |
|
716 |
|
717 if (!mstrTimeZoneStart.equals("")) { |
|
718 Result = mstrTimeZoneStart; |
|
719 } |
|
720 |
|
721 return Result; |
|
722 } |
|
723 |
|
724 private String getTimeZoneEnd() { |
|
725 String Result = "GMT"; |
|
726 |
|
727 if (!mstrTimeZoneEnd.equals("")) { |
|
728 Result = mstrTimeZoneEnd; |
|
729 } |
|
730 |
|
731 return Result; |
|
732 } |
|
733 |
|
734 |
|
735 private String getDuration() { |
|
736 String Result = ""; |
|
737 Property prop = calendarComponent.getProperty("DURATION"); |
|
738 |
|
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 |
|
750 |
|
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)); |
|
758 |
|
759 if (Days > 0) |
|
760 Result += String.valueOf(Days) + "D"; |
|
761 |
|
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 } |
|
770 |
|
771 return Result; |
|
772 } |
|
773 |
|
774 private int getAllDay() { |
|
775 int Result = 0; |
|
776 |
|
777 if (mAllDay) |
|
778 Result = 1; |
|
779 |
|
780 return Result; |
|
781 } |
|
782 |
|
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; |
|
794 |
|
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); |
|
800 |
|
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 } |
|
809 |
|
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 } |
|
824 |
|
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); |
|
831 |
|
832 if (!Error) |
|
833 calendarComponent = (Component) components.get(0); |
|
834 } |
|
835 |
|
836 return !Error; |
|
837 } |
|
838 |
|
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; |
|
851 |
|
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); |
|
855 |
|
856 Cursor curAttendee = null; |
|
857 Cursor curReminder = null; |
|
858 |
|
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(); |
|
869 |
|
870 long EventID = curEvent.getLong(curEvent.getColumnIndex(Events._ID)); |
|
871 Uri returnedUri = ContentUris.withAppendedId(uriEvents, EventID); |
|
872 |
|
873 //androidEvent = new AndroidEvent(this.mAccount, this.mProvider, returnedUri, androidCalendar.getAndroidCalendarUri()); |
|
874 androidEvent = new AndroidEvent(returnedUri, androidCalendar.getAndroidCalendarUri()); |
|
875 androidEvent.readContentValues(curEvent); |
|
876 |
|
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(); |
|
889 |
|
890 return androidEvent; |
|
891 } |
|
892 |
|
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; |
|
909 |
|
910 if (BodyFetched) { |
|
911 //calendarEvent.readContentValues(calendarUri); |
|
912 this.readContentValues(); |
|
913 this.setAndroidCalendarId(ContentUris.parseId(androidCalendar.getAndroidCalendarUri())); |
|
914 |
|
915 Uri uri = this.mProvider.insert(asSyncAdapter(Events.CONTENT_URI, this.mAccount.name, this.mAccount.type), this.ContentValues); |
|
916 this.setAndroidEventUri(uri); |
|
917 |
|
918 Log.d(TAG, "Creating calendar event for " + uri.toString()); |
|
919 |
|
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 } |
|
926 |
|
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 } |
|
933 |
|
934 if ((CountAttendees > 0) || (CountReminders > 0)) { |
|
935 //the events gets dirty when attendees or reminders were added |
|
936 AndroidEvent androidEvent = this.getAndroidEvent(androidCalendar); |
|
937 |
|
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 } |
|
944 |
|
945 |
|
946 } |
|
947 return Result; |
|
948 } |
|
949 |
|
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; |
|
967 |
|
968 if (BodyFetched) { |
|
969 this.readContentValues(); |
|
970 this.setAndroidCalendarId(androidEvent.getAndroidCalendarId()); |
|
971 this.setAndroidEventUri(androidEvent.getUri()); |
|
972 |
|
973 Log.d(TAG, "AndroidEvent is dirty: " + androidEvent.ContentValues.getAsString(Events.DIRTY)); |
|
974 |
|
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)); |
|
980 |
|
981 //update the attendees and reminders |
|
982 this.updateAndroidAttendees(); |
|
983 this.updateAndroidReminder(); |
|
984 |
|
985 androidEvent.ContentValues.put(Events.DIRTY, 0); // the event is now in sync |
|
986 Log.d(TAG, "Update calendar event: for "+androidEvent.getUri()); |
|
987 |
|
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 } |
|
996 |
|
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; |
|
1006 |
|
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)); |
|
1013 |
|
1014 java.util.ArrayList<ContentValues> AttendeeList = this.getAttandees(); |
|
1015 for (ContentValues Attendee : AttendeeList) { |
|
1016 this.mProvider.insert(Attendees.CONTENT_URI, Attendee); |
|
1017 } |
|
1018 Log.d(TAG, "Attendees Inserted:" + String.valueOf(AttendeeList.size())); |
|
1019 Result = true; |
|
1020 } catch (RemoteException e) { |
|
1021 e.printStackTrace(); |
|
1022 } |
|
1023 |
|
1024 return Result; |
|
1025 } |
|
1026 |
|
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; |
|
1036 |
|
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)); |
|
1043 |
|
1044 |
|
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); |
|
1050 } |
|
1051 Log.d(TAG, "Reminders Inserted:" + String.valueOf(ReminderList.size())); |
|
1052 |
|
1053 Result = true; |
|
1054 } catch (RemoteException e) { |
|
1055 e.printStackTrace(); |
|
1056 } |
|
1057 |
|
1058 return Result; |
|
1059 } |
|
1060 |
|
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(); |
|
1066 } |
|
1067 } |
|
1068 |