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