some fixes
[DHBWCampusApp.git] / app / src / main / java / de / dhbwloe / campusapp / vorlesungen / CalendarManager.java
1 package de.dhbwloe.campusapp.vorlesungen;
2
3
4 import android.util.Log;
5
6 import net.fortuna.ical4j.model.Calendar;
7 import net.fortuna.ical4j.model.Component;
8 import net.fortuna.ical4j.model.Property;
9
10 import java.text.DateFormat;
11 import java.text.ParseException;
12 import java.text.SimpleDateFormat;
13 import java.util.ArrayList;
14 import java.util.Date;
15 import java.util.ListIterator;
16 import java.util.Locale;
17
18 import de.dhbwloe.campusapp.CampusAppContext;
19 import de.dhbwloe.campusapp.network.IscRequestHelper;
20 import de.dhbwloe.campusapp.search.SearchIndices;
21
22 /**
23  * Created by pk910 on 19.01.2016.
24  */
25 public class CalendarManager extends IscRequestHelper {
26     private static final String[][] PLAN_SOURCES = {
27             {"STUV", "https://www.google.com/calendar/ical/asta.dhbw.de_c0g35t6hrh16kr4ankrqg2rdm4%40group.calendar.google.com/public/basic.ics"},
28     };
29
30     private CampusAppContext AppContext;
31     private boolean bRequestRunning = false;
32     private boolean bFastSynchronisation = false;
33     private String sCourseName;
34     private String source[];
35     private ArrayList<CalendarManagerInterface> aCallbackInterfaces = new ArrayList<CalendarManagerInterface>();
36
37     public CalendarManager(CampusAppContext context, String courseName) {
38         AppContext = context;
39         sCourseName = courseName;
40
41         for(String src[] : PLAN_SOURCES) {
42             if(src[0].equalsIgnoreCase(courseName)) {
43                 source = src;
44                 break;
45             }
46         }
47     }
48
49     public void performFastSynchronisation(CalendarManagerInterface callback) {
50         performSynchronisation(callback, false);
51     }
52
53     public void performFullSynchronisation(CalendarManagerInterface callback) {
54         performSynchronisation(callback, true);
55     }
56
57     private void performSynchronisation(CalendarManagerInterface callback, boolean fullsync) {
58         if(sCourseName.length() == 0) {
59             callback.onCalendarUpdateFail("no course name");
60             return;
61         }
62
63         aCallbackInterfaces.add(callback);
64         if(bRequestRunning)
65             return;
66
67         bFastSynchronisation = !fullsync;
68         bRequestRunning = true;
69         String courseCalendarUrl;
70         if(source == null) {
71             courseCalendarUrl = "https://webmail.dhbw-loerrach.de/owa/calendar/kal-" + sCourseName + "@dhbw-loerrach.de/Kalender/calendar.ics";
72         } else {
73             courseCalendarUrl = source[1];
74         }
75         requestCalenderFromWeb(courseCalendarUrl);
76     }
77
78     @Override
79     protected void onCalendarReceived(Calendar calendar) {
80         long timeFrom, timeTo;
81         long now = (new Date()).getTime() / 1000;
82         if(bFastSynchronisation) {
83             timeFrom = now - (86400 * 5);
84             timeTo = now + (86400 * 7 * 4);
85         } else {
86             timeFrom = now - (86400 * 365 * 3);
87             timeTo = now + (86400 * 365 * 4);
88         }
89         CourseEvent[] events = AppContext.getDatabaseManager().getCourseCalendarEvents(sCourseName, timeFrom, timeTo);
90         Log.i("CMSync", "Event count: " + events.length);
91         ArrayList<CourseEvent> newEvents = new ArrayList<CourseEvent>();
92         ArrayList<SearchIndices> newIndices = new ArrayList<SearchIndices>();
93         int lastEventIndex = 0;
94         ListIterator cIterator = calendar.getComponents().listIterator();
95
96         while (cIterator.hasNext()) {
97             Component event = (Component)cIterator.next();
98             CourseEvent dbEvent;
99             Property prop;
100
101             prop = event.getProperty("UID");
102             String uid;
103             if(prop != null)
104                 uid = prop.getValue();
105             else
106                 continue;
107
108             prop = event.getProperty("SEQUENCE");
109             int sequence;
110             if(prop != null)
111                 sequence = Integer.parseInt(prop.getValue());
112             else
113                 sequence = 1;
114
115             long startTime = 0, endTime = 0;
116             prop = event.getProperty("DTSTART");
117             if(prop != null)
118                 startTime = getTimeFromTimestamp(prop.getValue());
119             else
120                 Log.i("CMSync", "Parse Event: DTSTART not found!");
121
122             prop = event.getProperty("DTEND");
123             if(prop != null)
124                 endTime = getTimeFromTimestamp(prop.getValue());
125             else
126                 Log.i("CMSync", "Parse Event: DTEND not found!");
127
128             if(!(endTime > timeFrom && startTime < timeTo)) {
129                 Log.i("CMSync", "Skip Entry: ("+timeFrom+" - "+timeTo+")   Filter: "+startTime+" - "+endTime);
130                 continue;
131             }
132
133             dbEvent = null;
134             for(int i = lastEventIndex; i < events.length; i++) {
135                 if(events[i].getUniqueId().equalsIgnoreCase(uid)) {
136                     dbEvent = events[i];
137                     break;
138                 }
139             }
140             if(dbEvent == null && lastEventIndex > 0) {
141                 for(int i = 0; i < lastEventIndex; i++) {
142                     if(events[i].getUniqueId().equalsIgnoreCase(uid)) {
143                         dbEvent = events[i];
144                         break;
145                     }
146                 }
147             }
148             if(dbEvent == null) {
149                 for(CourseEvent cevent : newEvents) {
150                     if(cevent.getUniqueId().equalsIgnoreCase(uid)) {
151                         dbEvent = cevent;
152                         break;
153                     }
154                 }
155             }
156
157             if(dbEvent == null) {
158                 // new event!
159                 Log.i("CMSync", "New Event "+uid);
160                 dbEvent = new CourseEvent(sCourseName, uid, sequence, true);
161                 newEvents.add(dbEvent);
162             } else {
163                 Log.i("CMSync", "Existing Event "+uid+" ("+dbEvent.getSequenceId()+" >= "+sequence+")");
164                 if(dbEvent.getSequenceId() >= sequence) {
165                     continue; // skip event
166                 }
167                 dbEvent.setSequenceId(sequence);
168             }
169             // perform update
170             prop = event.getProperty("SUMMARY");
171             if(prop != null)
172                 dbEvent.setEventTitle(prop.getValue());
173             else
174                 Log.i("CMSync", "Parse Event: SUMMARY not found!");
175
176             dbEvent.setEventFrom(startTime);
177             dbEvent.setEventTo(endTime);
178
179             prop = event.getProperty("LOCATION");
180             if(prop != null)
181                 dbEvent.setEventLocation(prop.getValue());
182             else
183                 Log.i("CMSync", "Parse Event: LOCATION not found!");
184
185             prop = event.getProperty("STATUS");
186             if(prop != null)
187                 dbEvent.setEventStatus(prop.getValue());
188             else
189                 Log.i("CMSync", "Parse Event: STATUS not found!");
190
191             prop = event.getProperty("RRULE");
192             if(prop != null)
193                 dbEvent.setRecurRule(prop.getValue());
194             else
195                 dbEvent.setRecurRule("");
196
197             prop = event.getProperty("EXDATE");
198             if(prop != null)
199                 dbEvent.setExcludeDates(prop.getValue());
200             else
201                 dbEvent.setExcludeDates("");
202
203             CourseGroup group = CourseGroup.GetCourseGroupByName(AppContext.getDatabaseManager(), sCourseName, dbEvent.getGroupTitle().trim());
204             if(group != null)
205                 dbEvent.setCourseGroup(group);
206
207             if(group.isNewGroup(true)) {
208                 for(CalendarManagerInterface callback : aCallbackInterfaces) {
209                     SearchIndices indices = callback.onGenerateCalendarSearchIndices(dbEvent);
210                     if(indices != null) {
211                         newIndices.add(indices);
212                     }
213                 }
214             }
215
216             dbEvent.update(AppContext.getDatabaseManager());
217             Log.i("CMSync", "Update Event: "+dbEvent.getUniqueId());
218         }
219
220         SearchIndices[] newIndicesArr = new SearchIndices[newIndices.size()];
221         newIndicesArr = newIndices.toArray(newIndicesArr);
222         AppContext.addSearchIndices(newIndicesArr);
223
224         for(CalendarManagerInterface callback : aCallbackInterfaces) {
225             callback.onCalendarUpdateDone();
226         }
227         aCallbackInterfaces.clear();
228         bRequestRunning = false;
229     }
230
231     private long getTimeFromTimestamp(String timestamp) {
232         // 20160425T090000
233         // 20160321
234         DateFormat df = new SimpleDateFormat("yyyyMMdd'T'kkmmss", Locale.ENGLISH);
235         try {
236             Date result =  df.parse(timestamp);
237             return result.getTime()/1000;
238         } catch (ParseException e) {
239             df = new SimpleDateFormat("yyyyMMdd", Locale.ENGLISH);
240             try {
241                 Date result =  df.parse(timestamp);
242                 return result.getTime()/1000;
243             } catch (ParseException e2) {
244                 Log.i("CMSync", "Failed parsing: "+timestamp);
245                 return 0;
246             }
247         }
248     }
249
250     @Override
251     protected void onCalendarRequestFail(int statusCode, String errorMessage) {
252         Log.i("CMSync", "Calendar Error: "+statusCode);
253         for(CalendarManagerInterface callback : aCallbackInterfaces) {
254             callback.onCalendarUpdateFail("error " + statusCode + ": " + errorMessage);
255         }
256         aCallbackInterfaces.clear();
257         bRequestRunning = false;
258     }
259 }