From 9a28e7b4c1520f629721693a04b4978fec9692e7 Mon Sep 17 00:00:00 2001 From: pk910 Date: Sun, 21 Feb 2016 21:41:20 +0100 Subject: [PATCH] alpha 0.0.1 --- app/build.gradle | 13 +- .../dhbwloe/campusapp/CampusAppContext.java | 6 +- .../dhbwloe/campusapp/CampusAppFragment.java | 18 + .../main/java/de/dhbwloe/campusapp/Tools.java | 19 + .../campusapp/database/DatabaseManager.java | 30 +- .../VorlesungsplanDatabaseHelper.java | 274 ++++++++++++--- .../campusapp/fragments/Dashboard.java | 24 +- .../campusapp/fragments/DashboardMensa.java | 104 ++++++ ...Wochenplan.java => DashboardNewsDhbw.java} | 18 +- .../fragments/DashboardNewsStuv.java | 31 ++ .../fragments/DashboardVorlesungsplan.java | 229 ++++++++++++ .../de/dhbwloe/campusapp/fragments/Mensa.java | 97 +++--- .../campusapp/fragments/MensaTagesplan.java | 14 +- .../campusapp/fragments/Vorlesungsplan.java | 89 +++++ .../fragments/VorlesungsplanExams.java | 36 ++ .../fragments/VorlesungsplanGroups.java | 109 ++++++ .../VorlesungsplanGroupsListAdapter.java | 77 ++++ .../VorlesungsplanGroupsListItem.java | 68 ++++ .../fragments/VorlesungsplanUpcoming.java | 96 +++++ .../VorlesungsplanUpcomingCourseListItem.java | 42 +++ .../VorlesungsplanUpcomingDayListAdapter.java | 65 ++++ .../VorlesungsplanUpcomingDayListItem.java | 72 ++++ .../campusapp/fragments/WifiSettings.java | 329 +++++++----------- .../vorlesungen/CalendarManager.java | 4 +- .../campusapp/vorlesungen/CourseEvent.java | 97 +++++- .../campusapp/vorlesungen/CourseGroup.java | 23 ++ .../campusapp/wifi/SecureLoginManager.java | 220 ++++++++++++ .../wifi/SecureLoginSocketFactory.java | 95 +++++ .../campusapp/wifi/SecureLoginTask.java | 20 ++ .../wifi/SecureLoginTrustManager.java | 31 ++ .../wifi/WifiConfigurationManager.java | 72 ++++ .../campusapp/wifi/WifiNetworkSettings.java | 264 ++++++++++++++ app/src/main/res/drawable/dhbw_mensa.jpg | Bin 0 -> 35929 bytes app/src/main/res/drawable/header_dhbw.png | Bin 0 -> 40000 bytes app/src/main/res/drawable/header_kalender.jpg | Bin 0 -> 30399 bytes app/src/main/res/drawable/header_stuv.png | Bin 0 -> 30672 bytes app/src/main/res/drawable/platzhalter.png | Bin 0 -> 5648 bytes .../res/layout-land/fragment_dashboard.xml | 184 ++++++++++ .../main/res/layout/fragment_dashboard.xml | 174 ++++++++- .../res/layout/fragment_dashboard_mensa.xml | 28 ++ .../layout/fragment_dashboard_mensa_menu.xml | 41 +++ ...n.xml => fragment_dashboard_news_dhbw.xml} | 4 +- .../layout/fragment_dashboard_news_stuv.xml | 13 + .../layout/fragment_dashboard_timetable.xml | 78 +++++ app/src/main/res/layout/fragment_mensa.xml | 15 +- .../res/layout/fragment_mensa_weekend.xml | 14 + .../res/layout/fragment_vorlesungsplan.xml | 33 +- .../layout/fragment_vorlesungsplan_exams.xml | 13 + .../layout/fragment_vorlesungsplan_groups.xml | 21 ++ .../fragment_vorlesungsplan_groups_course.xml | 85 +++++ .../fragment_vorlesungsplan_upcoming.xml | 15 + ...ragment_vorlesungsplan_upcoming_course.xml | 73 ++++ .../fragment_vorlesungsplan_upcoming_day.xml | 35 ++ app/src/main/res/values/colors.xml | 5 + app/src/main/res/values/strings.xml | 38 ++ 55 files changed, 3214 insertions(+), 341 deletions(-) create mode 100644 app/src/main/java/de/dhbwloe/campusapp/fragments/DashboardMensa.java rename app/src/main/java/de/dhbwloe/campusapp/fragments/{MensaWochenplan.java => DashboardNewsDhbw.java} (60%) create mode 100644 app/src/main/java/de/dhbwloe/campusapp/fragments/DashboardNewsStuv.java create mode 100644 app/src/main/java/de/dhbwloe/campusapp/fragments/DashboardVorlesungsplan.java create mode 100644 app/src/main/java/de/dhbwloe/campusapp/fragments/VorlesungsplanExams.java create mode 100644 app/src/main/java/de/dhbwloe/campusapp/fragments/VorlesungsplanGroups.java create mode 100644 app/src/main/java/de/dhbwloe/campusapp/fragments/VorlesungsplanGroupsListAdapter.java create mode 100644 app/src/main/java/de/dhbwloe/campusapp/fragments/VorlesungsplanGroupsListItem.java create mode 100644 app/src/main/java/de/dhbwloe/campusapp/fragments/VorlesungsplanUpcoming.java create mode 100644 app/src/main/java/de/dhbwloe/campusapp/fragments/VorlesungsplanUpcomingCourseListItem.java create mode 100644 app/src/main/java/de/dhbwloe/campusapp/fragments/VorlesungsplanUpcomingDayListAdapter.java create mode 100644 app/src/main/java/de/dhbwloe/campusapp/fragments/VorlesungsplanUpcomingDayListItem.java create mode 100644 app/src/main/java/de/dhbwloe/campusapp/wifi/SecureLoginManager.java create mode 100644 app/src/main/java/de/dhbwloe/campusapp/wifi/SecureLoginSocketFactory.java create mode 100644 app/src/main/java/de/dhbwloe/campusapp/wifi/SecureLoginTask.java create mode 100644 app/src/main/java/de/dhbwloe/campusapp/wifi/SecureLoginTrustManager.java create mode 100644 app/src/main/java/de/dhbwloe/campusapp/wifi/WifiConfigurationManager.java create mode 100644 app/src/main/java/de/dhbwloe/campusapp/wifi/WifiNetworkSettings.java create mode 100644 app/src/main/res/drawable/dhbw_mensa.jpg create mode 100644 app/src/main/res/drawable/header_dhbw.png create mode 100644 app/src/main/res/drawable/header_kalender.jpg create mode 100644 app/src/main/res/drawable/header_stuv.png create mode 100644 app/src/main/res/drawable/platzhalter.png create mode 100644 app/src/main/res/layout-land/fragment_dashboard.xml create mode 100644 app/src/main/res/layout/fragment_dashboard_mensa.xml create mode 100644 app/src/main/res/layout/fragment_dashboard_mensa_menu.xml rename app/src/main/res/layout/{fragment_mensa_wochenplan.xml => fragment_dashboard_news_dhbw.xml} (74%) create mode 100644 app/src/main/res/layout/fragment_dashboard_news_stuv.xml create mode 100644 app/src/main/res/layout/fragment_dashboard_timetable.xml create mode 100644 app/src/main/res/layout/fragment_mensa_weekend.xml create mode 100644 app/src/main/res/layout/fragment_vorlesungsplan_exams.xml create mode 100644 app/src/main/res/layout/fragment_vorlesungsplan_groups.xml create mode 100644 app/src/main/res/layout/fragment_vorlesungsplan_groups_course.xml create mode 100644 app/src/main/res/layout/fragment_vorlesungsplan_upcoming.xml create mode 100644 app/src/main/res/layout/fragment_vorlesungsplan_upcoming_course.xml create mode 100644 app/src/main/res/layout/fragment_vorlesungsplan_upcoming_day.xml diff --git a/app/build.gradle b/app/build.gradle index 6414d6d..f86b286 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -33,7 +33,7 @@ android { dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) - testCompile 'junit:junit:4.12' + testCompile 'junit:junit:4.11' compile 'org.mnode.ical4j:ical4j:1.0.5' compile 'backport-util-concurrent:backport-util-concurrent:3.1' compile 'commons-codec:commons-codec:1.8' @@ -44,6 +44,10 @@ dependencies { compile 'com.android.support:appcompat-v7:23.1.1' compile 'com.android.support:design:23.1.1' compile 'com.android.support:support-v4:23.1.1' + compile 'com.squareup.okhttp3:mockwebserver:3.1.2' + compile 'com.squareup.mimecraft:mimecraft:1.1.1' + compile 'com.android.support:cardview-v7:23.1.1' + compile 'com.android.support:recyclerview-v7:23.1.1' } @@ -58,3 +62,10 @@ dependencies { + + + + + + + diff --git a/app/src/main/java/de/dhbwloe/campusapp/CampusAppContext.java b/app/src/main/java/de/dhbwloe/campusapp/CampusAppContext.java index 89cbc7c..11d3999 100644 --- a/app/src/main/java/de/dhbwloe/campusapp/CampusAppContext.java +++ b/app/src/main/java/de/dhbwloe/campusapp/CampusAppContext.java @@ -14,7 +14,6 @@ import de.dhbwloe.campusapp.fragments.FirstRun; import de.dhbwloe.campusapp.fragments.Impressum; import de.dhbwloe.campusapp.fragments.Mensa; import de.dhbwloe.campusapp.fragments.MensaCard; -import de.dhbwloe.campusapp.fragments.MensaWochenplan; import de.dhbwloe.campusapp.fragments.News; import de.dhbwloe.campusapp.fragments.PopupFragment; import de.dhbwloe.campusapp.fragments.SplashScreen; @@ -167,4 +166,9 @@ public class CampusAppContext { return oContextVariables; } + public String getResString(int id) { + String str = oMainActivity.getResources().getString(id); + return str; + } + } diff --git a/app/src/main/java/de/dhbwloe/campusapp/CampusAppFragment.java b/app/src/main/java/de/dhbwloe/campusapp/CampusAppFragment.java index e0aead8..4707a73 100644 --- a/app/src/main/java/de/dhbwloe/campusapp/CampusAppFragment.java +++ b/app/src/main/java/de/dhbwloe/campusapp/CampusAppFragment.java @@ -4,6 +4,8 @@ import android.os.Bundle; import android.support.v4.app.Fragment; import android.view.View; +import java.lang.reflect.Field; + import de.dhbwloe.campusapp.search.SearchIndices; /** @@ -23,4 +25,20 @@ public abstract class CampusAppFragment extends Fragment { return new SearchIndices[0]; } + @Override + public void onDetach() { + super.onDetach(); + + try { + Field childFragmentManager = Fragment.class.getDeclaredField("mChildFragmentManager"); + childFragmentManager.setAccessible(true); + childFragmentManager.set(this, null); + + } catch (NoSuchFieldException e) { + throw new RuntimeException(e); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + } + } diff --git a/app/src/main/java/de/dhbwloe/campusapp/Tools.java b/app/src/main/java/de/dhbwloe/campusapp/Tools.java index 02c8d7a..168446e 100644 --- a/app/src/main/java/de/dhbwloe/campusapp/Tools.java +++ b/app/src/main/java/de/dhbwloe/campusapp/Tools.java @@ -1,5 +1,9 @@ package de.dhbwloe.campusapp; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; + import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -51,4 +55,19 @@ public class Tools { return decrypted; } + public static void removeAllChildViews(ViewGroup viewGroup) { + for (int i = 0; i < viewGroup.getChildCount(); i++) { + View child = viewGroup.getChildAt(i); + if (child instanceof ViewGroup) { + if (child instanceof AdapterView) { + viewGroup.removeView(child); + return; + } + removeAllChildViews(((ViewGroup) child)); + } else { + viewGroup.removeView(child); + } + } + } + } diff --git a/app/src/main/java/de/dhbwloe/campusapp/database/DatabaseManager.java b/app/src/main/java/de/dhbwloe/campusapp/database/DatabaseManager.java index ccf9a04..fed7eac 100644 --- a/app/src/main/java/de/dhbwloe/campusapp/database/DatabaseManager.java +++ b/app/src/main/java/de/dhbwloe/campusapp/database/DatabaseManager.java @@ -6,6 +6,7 @@ import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.util.Log; +import net.fortuna.ical4j.model.Component; import net.fortuna.ical4j.model.DateList; import net.fortuna.ical4j.model.DateTime; import net.fortuna.ical4j.model.Period; @@ -31,7 +32,7 @@ import de.dhbwloe.campusapp.vorlesungen.CourseGroup; */ public class DatabaseManager { private static final String DATABASE_NAME = "DHBWLoe.CampusApp.DEV"; - private static final int DATABASE_VERSION = 1; + private static final int DATABASE_VERSION = 2; private CampusAppContext AppContext; private SQLiteDatabase database; private NewsDatabaseHelper newsDBHelper; @@ -67,7 +68,8 @@ public class DatabaseManager { if(version < DATABASE_VERSION) upgradeTables(version, DATABASE_VERSION); - + //database.execSQL("DELETE FROM CourseCalendar"); + //database.execSQL("DELETE FROM CourseCalendarEvent"); } private void upgradeTables(int oldVersion, int newVersion) { @@ -162,10 +164,14 @@ public class DatabaseManager { } if(oldVersion < 2 && newVersion >= 2) { // Version 2 + database.execSQL("ALTER TABLE CourseCalendarEvent ADD EventType INT;"); + } + if(oldVersion < 3 && newVersion >= 3) { + // Version 3 } - database.execSQL("UPDATE Version SET Version = "+Integer.toString(newVersion)); + database.execSQL("UPDATE Version SET Version = " + Integer.toString(newVersion)); } public void addSearchIndices(SearchIndices[] indices) { @@ -356,11 +362,11 @@ public class DatabaseManager { return nfcCardData; } - public void updateCourseCalendar(CourseEvent event) { + public void updateCourseCalendar(CourseEvent event, Component cevent) { openDatabase(); if(vorlesungsplanDBHelper == null) vorlesungsplanDBHelper = new VorlesungsplanDatabaseHelper(AppContext, database); - vorlesungsplanDBHelper.updateCourseCalendar(event); + vorlesungsplanDBHelper.updateCourseCalendar(event, cevent); } public CourseEvent[] getCourseCalendarEvents(String coursename, long timeFrom, long timeTo) { @@ -370,6 +376,13 @@ public class DatabaseManager { return vorlesungsplanDBHelper.getCourseCalendarEvents(coursename, timeFrom, timeTo); } + public CourseEvent[] getCourseCalendarTimetable(String coursename, long timeFrom, int days) { + openDatabase(); + if(vorlesungsplanDBHelper == null) + vorlesungsplanDBHelper = new VorlesungsplanDatabaseHelper(AppContext, database); + return vorlesungsplanDBHelper.getCourseCalendarTimetable(coursename, timeFrom, days); + } + public CourseGroup getCourseGroup(int courseGroupId) { openDatabase(); if(vorlesungsplanDBHelper == null) @@ -391,6 +404,13 @@ public class DatabaseManager { return vorlesungsplanDBHelper.addCourseGroup(coursename, groupname); } + public CourseGroup[] getCourseGroups(String coursename, Date notBefore) { + openDatabase(); + if(vorlesungsplanDBHelper == null) + vorlesungsplanDBHelper = new VorlesungsplanDatabaseHelper(AppContext, database); + return vorlesungsplanDBHelper.getCourseGroups(coursename, notBefore); + } + public void updateMensaTagesplan(MensaTagesplan plan) { openDatabase(); if(mensaplanDBHelper == null) diff --git a/app/src/main/java/de/dhbwloe/campusapp/database/VorlesungsplanDatabaseHelper.java b/app/src/main/java/de/dhbwloe/campusapp/database/VorlesungsplanDatabaseHelper.java index e38b87d..f4d5c8a 100644 --- a/app/src/main/java/de/dhbwloe/campusapp/database/VorlesungsplanDatabaseHelper.java +++ b/app/src/main/java/de/dhbwloe/campusapp/database/VorlesungsplanDatabaseHelper.java @@ -3,18 +3,25 @@ package de.dhbwloe.campusapp.database; import android.content.ContentValues; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; +import android.os.Bundle; import android.util.Log; +import net.fortuna.ical4j.model.Component; import net.fortuna.ical4j.model.DateList; import net.fortuna.ical4j.model.DateTime; import net.fortuna.ical4j.model.Period; +import net.fortuna.ical4j.model.PeriodList; import net.fortuna.ical4j.model.Recur; import net.fortuna.ical4j.model.parameter.Value; import net.fortuna.ical4j.model.property.RRule; import java.text.ParseException; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; +import java.util.Iterator; +import java.util.List; +import java.util.TimeZone; import de.dhbwloe.campusapp.CampusAppContext; import de.dhbwloe.campusapp.vorlesungen.CourseEvent; @@ -33,7 +40,7 @@ public class VorlesungsplanDatabaseHelper { } - public void updateCourseCalendar(CourseEvent event) { + public void updateCourseCalendar(CourseEvent event, Component cevent) { boolean isExisting = false; String[] whereArgs = new String[] { event.getCourseName(), @@ -63,7 +70,7 @@ public class VorlesungsplanDatabaseHelper { updateValues.put("CourseGroupId", event.getCourseGroup().getGroupId()); database.update("CourseCalendar", updateValues, "CourseName = ? AND UniqueId = ?", whereArgs); - updateCourseCalendarEventTable(event, true); + updateCourseCalendarEventTable(event, true, cevent); } catch(Exception e) { e.printStackTrace(); } @@ -85,7 +92,7 @@ public class VorlesungsplanDatabaseHelper { long id = database.insertOrThrow("CourseCalendar", null, indexValues); event.setEventId((int) id); - updateCourseCalendarEventTable(event, false); + updateCourseCalendarEventTable(event, false, cevent); } catch(Exception e) { e.printStackTrace(); } @@ -93,61 +100,39 @@ public class VorlesungsplanDatabaseHelper { } - private void updateCourseCalendarEventTable(CourseEvent event, boolean clear) { + private void updateCourseCalendarEventTable(CourseEvent dbevent, boolean clear, Component event) { if(clear) { String[] whereArgs = { - Integer.toString(event.getEventId()) + Integer.toString(dbevent.getEventId()) }; database.rawQuery("DELETE FROM CourseCalendarEvent WHERE EventId = ?", whereArgs); } - String rrule = event.getRecurRule(); - String exdates = event.getExcludeDates(); + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd'T'hhmmss"); + Date startDate = new Date((dbevent.getEventFrom())*1000); + Date endDate = new Date(startDate.getTime() + ((long)86400 * 365)*1000); + Period period = new Period(new DateTime(startDate), new DateTime(endDate)); + PeriodList recurrances = event.calculateRecurrenceSet(period); + + Log.i("DBM", "Update events table for " + dbevent.getUniqueId() + " ("+dbevent.getEventTitle()+")"); + Log.i("DBM", "events: " + recurrances.size()); + + for (Iterator iter = recurrances.iterator(); iter.hasNext();) { + Period eventper = iter.next(); - if(rrule != null && rrule.length() > 0) { - try { - Log.i("DBM", "RRule: " + rrule); - RRule rule = new RRule(rrule); - Recur recur = rule.getRecur(); - Date startDate = new Date((event.getEventFrom())*1000); - Date endDate = new Date(startDate.getTime() + (86400 * 365)*1000); - - DateTime startDateTime = new DateTime(startDate); - DateList dates = recur.getDates(startDateTime, new Period(startDateTime, new DateTime(endDate)), Value.DATE); - - Log.i("DBM", "Update events table for " + event.getUniqueId()); - Log.i("DBM", "events: " +dates.size()); - - for(int i = 0; i < dates.size(); i++) { - net.fortuna.ical4j.model.Date date = (net.fortuna.ical4j.model.Date) dates.get(i); - long eventStartTime = date.getTime() / 1000; - long eventEndTime = (eventStartTime + (event.getEventTo() - event.getEventFrom())) / 1000; - - try { - ContentValues indexValues = new ContentValues(); - indexValues.put("EventId", event.getEventId()); - indexValues.put("EventFrom", eventStartTime); - indexValues.put("EventTo", eventEndTime); - database.insertOrThrow("CourseCalendarEvent", null, indexValues); - } catch(Exception e) { - e.printStackTrace(); - } - } - - } catch (ParseException e) { - } - } else { try { + Log.i("DBM Add", dateFormat.format(eventper.getStart()) + " - " + dateFormat.format(eventper.getEnd())); + ContentValues indexValues = new ContentValues(); - indexValues.put("EventId", event.getEventId()); - indexValues.put("EventFrom", event.getEventFrom()); - indexValues.put("EventTo", event.getEventTo()); + indexValues.put("EventId", dbevent.getEventId()); + indexValues.put("EventFrom", eventper.getStart().getTime() / 1000); + indexValues.put("EventTo", eventper.getEnd().getTime() / 1000); + indexValues.put("EventType", dbevent.getCourseTypeId()); database.insertOrThrow("CourseCalendarEvent", null, indexValues); } catch(Exception e) { - e.printStackTrace(); + //e.printStackTrace(); } } - } public CourseEvent[] getCourseCalendarEvents(String coursename, long timeFrom, long timeTo) { @@ -156,7 +141,16 @@ public class VorlesungsplanDatabaseHelper { Long.toString(timeFrom), Long.toString(timeTo) }; - Cursor resultSet = database.rawQuery("SELECT Id, CourseName, UniqueId, SequenceId, EventFrom, EventTo, EventTitle, EventLocation, EventStatus, RecurRule, ExcludeDates, CourseGroupId FROM CourseCalendar WHERE CourseName = ? AND EventTo >= ? AND EventFrom <= ?", whereArgs); + Cursor resultSet = database.rawQuery( + "SELECT " + + "Id, CourseName, UniqueId, SequenceId, CourseCalendarEvent.EventFrom, CourseCalendarEvent.EventTo, EventTitle, EventLocation, EventStatus, RecurRule, ExcludeDates, CourseGroupId, EventType " + + "FROM " + + "CourseCalendarEvent " + + "LEFT JOIN " + + "CourseCalendar ON CourseCalendar.Id = CourseCalendarEvent.EventId "+ + "WHERE " + + "CourseName = ? AND CourseCalendarEvent.EventTo >= ? AND CourseCalendarEvent.EventFrom <= ? ", + whereArgs); ArrayList events = new ArrayList(); if(resultSet.moveToFirst()) { int[] columnIndexes = { @@ -171,7 +165,8 @@ public class VorlesungsplanDatabaseHelper { resultSet.getColumnIndex("EventStatus"), resultSet.getColumnIndex("RecurRule"), resultSet.getColumnIndex("ExcludeDates"), - resultSet.getColumnIndex("CourseGroupId") + resultSet.getColumnIndex("CourseGroupId"), + resultSet.getColumnIndex("EventType") }; do { int groupId = resultSet.getInt(columnIndexes[11]); @@ -180,9 +175,12 @@ public class VorlesungsplanDatabaseHelper { group = CourseGroup.GetCourseGroupById(AppContext.getDatabaseManager(), groupId); else group = null; + + int eventType = resultSet.getInt(columnIndexes[12]); + CourseEvent event = new CourseEvent(resultSet.getInt(columnIndexes[0]), resultSet.getString(columnIndexes[1]), resultSet.getString(columnIndexes[2]), resultSet.getInt(columnIndexes[3]), resultSet.getLong(columnIndexes[4]), resultSet.getLong(columnIndexes[5]), resultSet.getString(columnIndexes[6]), resultSet.getString(columnIndexes[7]), - resultSet.getString(columnIndexes[8]), resultSet.getString(columnIndexes[9]), resultSet.getString(columnIndexes[10]), group); + resultSet.getString(columnIndexes[8]), resultSet.getString(columnIndexes[9]), resultSet.getString(columnIndexes[10]), group, eventType); events.add(event); } while (resultSet.moveToNext()); @@ -194,6 +192,98 @@ public class VorlesungsplanDatabaseHelper { return eventsArr; } + public CourseEvent[] getCourseCalendarTimetable(String coursename, long timeFrom, int days) { + String[] whereArgs; + whereArgs = new String[] { + coursename, + Long.toString(timeFrom), + Integer.toString(days) + }; + ArrayList events = new ArrayList(); + + Cursor resultSet = database.rawQuery( + "SELECT " + + "COUNT(*) as EventCount, " + + "strftime('%Y%m%d', datetime(CourseCalendarEvent.EventTo, 'unixepoch')) as DayId " + + "FROM "+ + "CourseCalendarEvent " + + "LEFT JOIN CourseCalendar ON CourseCalendar.Id = CourseCalendarEvent.EventId " + + "WHERE " + + "CourseCalendar.CourseName = ? AND " + + "CourseCalendarEvent.EventTo >= ? " + + "GROUP BY DayId " + + "ORDER BY DayId ASC " + + "LIMIT ? ", + whereArgs); + if(resultSet.moveToFirst()) { + do { + int eventCount = resultSet.getInt(0); + int dayId = resultSet.getInt(1); + + if(eventCount == 0) + continue; + + whereArgs = new String[] { + Integer.toString(dayId), + coursename + }; + + Cursor resultSet2 = database.rawQuery( + "SELECT " + + "CourseCalendar.Id, CourseCalendar.CourseName, CourseCalendar.UniqueId, CourseCalendar.SequenceId, " + + "CourseCalendarEvent.EventFrom, CourseCalendarEvent.EventTo, CourseCalendar.EventTitle, CourseCalendar.EventLocation, " + + "CourseCalendar.EventStatus, CourseCalendar.RecurRule, CourseCalendar.ExcludeDates, CourseCalendar.CourseGroupId, EventType, " + + "strftime('%Y%m%d', datetime(CourseCalendarEvent.EventTo, 'unixepoch')) as DayId " + + "FROM " + + "CourseCalendarEvent " + + "LEFT JOIN CourseCalendar ON CourseCalendarEvent.EventId = CourseCalendar.Id " + + "WHERE DayId = ? AND " + + "CourseCalendar.CourseName = ? " + + "ORDER BY CourseCalendarEvent.EventFrom ASC", + whereArgs); + + int[] columnIndexes = { + resultSet2.getColumnIndex("Id"), + resultSet2.getColumnIndex("CourseName"), + resultSet2.getColumnIndex("UniqueId"), + resultSet2.getColumnIndex("SequenceId"), + resultSet2.getColumnIndex("EventFrom"), + resultSet2.getColumnIndex("EventTo"), + resultSet2.getColumnIndex("EventTitle"), + resultSet2.getColumnIndex("EventLocation"), + resultSet2.getColumnIndex("EventStatus"), + resultSet2.getColumnIndex("RecurRule"), + resultSet2.getColumnIndex("ExcludeDates"), + resultSet2.getColumnIndex("CourseGroupId"), + resultSet2.getColumnIndex("EventType") + }; + resultSet2.moveToFirst(); + do { + int groupId = resultSet2.getInt(columnIndexes[11]); + CourseGroup group; + if(groupId > 0) + group = CourseGroup.GetCourseGroupById(AppContext.getDatabaseManager(), groupId); + else + group = null; + int eventType = resultSet2.getInt(columnIndexes[12]); + CourseEvent event = new CourseEvent(resultSet2.getInt(columnIndexes[0]), resultSet2.getString(columnIndexes[1]), resultSet2.getString(columnIndexes[2]), resultSet2.getInt(columnIndexes[3]), + resultSet2.getLong(columnIndexes[4]), resultSet2.getLong(columnIndexes[5]), resultSet2.getString(columnIndexes[6]), resultSet2.getString(columnIndexes[7]), + resultSet2.getString(columnIndexes[8]), resultSet2.getString(columnIndexes[9]), resultSet2.getString(columnIndexes[10]), group, eventType); + + events.add(event); + } while (resultSet2.moveToNext()); + resultSet2.close(); + } while (resultSet.moveToNext()); + resultSet.close(); + } + + CourseEvent[] eventsArr = new CourseEvent[events.size()]; + eventsArr = events.toArray(eventsArr); + return eventsArr; + } + + + public CourseGroup getCourseGroup(int courseGroupId) { CourseGroup coursegroup = null; String[] whereArgs = new String[] { @@ -242,4 +332,92 @@ public class VorlesungsplanDatabaseHelper { return null; } + public CourseGroup[] getCourseGroups(String coursename, Date notBefore) { + String[] whereArgs; + whereArgs = new String[] { + coursename + }; + ArrayList groups = new ArrayList(); + + /* + Cursor resultSet = database.rawQuery( + "SELECT CourseCalendarGroup.GroupId, CourseCalendarGroup.CourseName, CourseCalendarGroup.GroupName, t1.FirstEvent, t1.LastEvent, t1.EventCount FROM " + + "CourseCalendarGroup, ( " + + "SELECT CourseCalendar.CourseGroupId, MIN(CourseCalendarEvent.EventFrom) as FirstEvent, MAX(CourseCalendarEvent.EventTo) as LastEvent, COUNT(*) as EventCount " + + "FROM CourseCalendarEvent " + + "LEFT JOIN CourseCalendar ON CourseCalendar.Id = CourseCalendarEvent.EventId " + + "GROUP BY CourseCalendar.CourseGroupId " + + ") as t1 " + + "WHERE " + + "CourseCalendarGroup.GroupId = t1.CourseGroupId AND " + + "CourseName = ? AND t1.LastEvent > ? " + + "ORDER BY FirstEvent ASC", + whereArgs); + */ + Cursor resultSet = database.rawQuery( + "SELECT " + + "CourseCalendarGroup.GroupId, CourseCalendarGroup.CourseName, CourseCalendarGroup.GroupName, " + + "MIN(CourseCalendarEvent.EventFrom) as FirstEvent, " + + "MAX(" + + "CASE WHEN CourseCalendarEvent.EventType IN (0, 1) " + + "THEN CourseCalendarEvent.EventFrom " + + "ELSE 0 " + + "END) as LastEvent, " + + "MAX(CourseCalendarEvent.EventFrom) as LastEventFilter, " + + "COUNT(*) as EventCount, " + + "MIN(" + + "CASE WHEN CourseCalendarEvent.EventFrom < strftime('%s', 'now') " + + "THEN (strftime('%s', 'now')+(86400*365*5)+CourseCalendarEvent.EventFrom) " + + "ELSE CourseCalendarEvent.EventFrom " + + "END) as NextEvent, " + + "MIN(" + + "CASE WHEN CourseCalendarEvent.EventFrom < strftime('%s', 'now') OR CourseCalendarEvent.EventType NOT IN (2) " + + "THEN (strftime('%s', 'now')+(86400*365*5)+CourseCalendarEvent.EventFrom) " + + "ELSE CourseCalendarEvent.EventFrom " + + "END) as NextKlausurEvent, " + + "MIN(CASE WHEN CourseCalendarEvent.EventFrom <= strftime('%s', 'now') THEN 1 ELSE 0 END) as EventCompleted " + + "FROM " + + "CourseCalendarEvent " + + "LEFT JOIN CourseCalendar ON CourseCalendar.Id = CourseCalendarEvent.EventId " + + "LEFT JOIN CourseCalendarGroup ON CourseCalendarGroup.GroupId = CourseCalendar.CourseGroupId " + + "WHERE " + + "CourseCalendar.CourseName = ? " + + "GROUP BY CourseCalendarGroup.GroupId, CourseCalendarGroup.CourseName, CourseCalendarGroup.GroupName " + + "HAVING LastEventFilter > " + Long.toString((notBefore == null ? 0L : notBefore.getTime()/1000)) + " " + + "ORDER BY EventCompleted DESC, NextEvent ASC ", + whereArgs); + if(resultSet.moveToFirst()) { + int[] columnIndexes = { + resultSet.getColumnIndex("GroupId"), + resultSet.getColumnIndex("CourseName"), + resultSet.getColumnIndex("GroupName"), + resultSet.getColumnIndex("FirstEvent"), + resultSet.getColumnIndex("LastEvent"), + resultSet.getColumnIndex("EventCount"), + resultSet.getColumnIndex("NextEvent"), + resultSet.getColumnIndex("EventCompleted"), + resultSet.getColumnIndex("NextKlausurEvent"), + }; + do { + CourseGroup coursegroup = new CourseGroup(resultSet.getInt(columnIndexes[0]), resultSet.getString(columnIndexes[1]), resultSet.getString(columnIndexes[2])); + Bundle extraData = coursegroup.getExtraData(); + + extraData.putLong("FirstEvent", resultSet.getLong(columnIndexes[3])); + extraData.putLong("LastEvent", resultSet.getLong(columnIndexes[4])); + extraData.putInt("EventCount", resultSet.getInt(columnIndexes[5])); + extraData.putLong("NextEvent", resultSet.getLong(columnIndexes[6])); + extraData.putLong("NextKlausurEvent", resultSet.getLong(columnIndexes[8])); + extraData.putBoolean("EventCompleted", (resultSet.getInt(columnIndexes[7]) == 1)); + + groups.add(coursegroup); + + } while (resultSet.moveToNext()); + resultSet.close(); + } + + CourseGroup[] groupsArr = new CourseGroup[groups.size()]; + groupsArr = groups.toArray(groupsArr); + return groupsArr; + } + } diff --git a/app/src/main/java/de/dhbwloe/campusapp/fragments/Dashboard.java b/app/src/main/java/de/dhbwloe/campusapp/fragments/Dashboard.java index 5bea0bc..79d1f65 100644 --- a/app/src/main/java/de/dhbwloe/campusapp/fragments/Dashboard.java +++ b/app/src/main/java/de/dhbwloe/campusapp/fragments/Dashboard.java @@ -3,6 +3,10 @@ package de.dhbwloe.campusapp.fragments; import de.dhbwloe.campusapp.CampusAppFragment; import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentTransaction; +import android.support.v7.widget.CardView; +import android.view.InflateException; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -11,6 +15,7 @@ import de.dhbwloe.campusapp.R; import de.dhbwloe.campusapp.search.SearchIndices; public class Dashboard extends CampusAppFragment { + private static View view; /* implement this for search results ;) */ public static SearchIndices[] GetSearchIndices() { return new SearchIndices[] { @@ -28,15 +33,30 @@ public class Dashboard extends CampusAppFragment { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + + AppContext.getDatabaseManager().setRuntimeCache("CourseName", "tif13a"); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_dashboard, container, false); + if (view != null) { + ViewGroup parent = (ViewGroup) view.getParent(); + if (parent != null) + parent.removeView(view); + } + + try { + view = inflater.inflate(R.layout.fragment_dashboard, container, false); + } catch (InflateException e) { + } AppContext.setTitle("Dashboard"); - + String kursTag = AppContext.getDatabaseManager().getRuntimeCache("CourseName"); + if(kursTag == null || kursTag.isEmpty()) { + CardView timetable = (CardView) view.findViewById(R.id.card_timetable); + timetable.setVisibility(View.GONE); + } return view; } diff --git a/app/src/main/java/de/dhbwloe/campusapp/fragments/DashboardMensa.java b/app/src/main/java/de/dhbwloe/campusapp/fragments/DashboardMensa.java new file mode 100644 index 0000000..2d473a5 --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/fragments/DashboardMensa.java @@ -0,0 +1,104 @@ +package de.dhbwloe.campusapp.fragments; + + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; +import android.widget.TextView; + +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; + +import de.dhbwloe.campusapp.CampusAppContext; +import de.dhbwloe.campusapp.CampusAppFragment; +import de.dhbwloe.campusapp.R; +import de.dhbwloe.campusapp.mensaplan.MensaTagesplan; + +/** + * A simple {@link Fragment} subclass. + */ +public class DashboardMensa extends CampusAppFragment { + private View view; + + public DashboardMensa() { + // Required empty public constructor + } + + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + view = inflater.inflate(R.layout.fragment_dashboard_mensa, container, false); + refreshMensaMenue(); + return view; + } + + + private void refreshMensaMenue() { + if(AppContext == null) + AppContext = CampusAppContext.getInstance(); + Calendar cal = Calendar.getInstance(); + cal.setTime(new Date()); + cal.set(Calendar.HOUR_OF_DAY, 0); + cal.set(Calendar.MINUTE, 0); + cal.set(Calendar.SECOND, 0); + cal.set(Calendar.MILLISECOND, 0); + long now = (new Date()).getTime() / 1000; + long mensaCloseTime = (3600 * 13); // 13 uhr + long timeFrom = cal.getTime().getTime() / 1000; + if(now > timeFrom + mensaCloseTime) + timeFrom += 86400; + long timeTo = timeFrom + (86400 * 3) + 86399; // +3days for weekend + + MensaTagesplan[] dayplans = AppContext.getDatabaseManager().getMensaTagesplan(timeFrom, timeTo); + if(dayplans.length == 0) { + + return; + } + MensaTagesplan dayplan = dayplans[0]; + TextView dateTextView = (TextView) view.findViewById(R.id.menuDate); + dateTextView.setText(getDateText(dayplan.getPlanDate(), now)); + + LinearLayout menueContainer = (LinearLayout) view.findViewById(R.id.menuList); + menueContainer.removeAllViews(); + + MensaTagesplan cmenue = dayplan; + int i = 0; + LayoutInflater li = LayoutInflater.from(AppContext.getMainActivity()); + do { + RelativeLayout menu = (RelativeLayout) li.inflate(R.layout.fragment_dashboard_mensa_menu, null, false); + menueContainer.addView(menu); + + TextView menuName = (TextView) menu.findViewById(R.id.menuName); + TextView menuPrice = (TextView) menu.findViewById(R.id.menuPrice); + + menuName.setText(cmenue.getName()); + double price = cmenue.getStudentPrice(); + menuPrice.setText(Double.toString(price)); + } while(i < dayplans.length && (cmenue = dayplans[++i]) != null && cmenue.getPlanDate() == dayplan.getPlanDate()); + } + + private String getDateText(long date, long now) { + String datetext; + if(now < date+86400) + datetext = AppContext.getResString(R.string.dashboard_calendar_today); + else if(now < date + (86400*2)) + datetext = AppContext.getResString(R.string.dashboard_calendar_tomorrow); + else { + int weekdayResIds[] = new int[] { R.string.week_sunday, R.string.week_monday, R.string.week_tuesday, R.string.week_wednesday, R.string.week_thursday, R.string.week_friday, R.string.week_saturday }; + Calendar cal = Calendar.getInstance(); + cal.setTime(new Date(date * 1000)); + int dow = cal.get(Calendar.DAY_OF_WEEK); + SimpleDateFormat dateFormat = new SimpleDateFormat("dd.MM"); + + datetext = AppContext.getResString(weekdayResIds[dow]) + ", " + dateFormat.format(cal.getTime()); + } + return datetext; + } + +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/fragments/MensaWochenplan.java b/app/src/main/java/de/dhbwloe/campusapp/fragments/DashboardNewsDhbw.java similarity index 60% rename from app/src/main/java/de/dhbwloe/campusapp/fragments/MensaWochenplan.java rename to app/src/main/java/de/dhbwloe/campusapp/fragments/DashboardNewsDhbw.java index 35917f0..3f1c3c2 100644 --- a/app/src/main/java/de/dhbwloe/campusapp/fragments/MensaWochenplan.java +++ b/app/src/main/java/de/dhbwloe/campusapp/fragments/DashboardNewsDhbw.java @@ -3,29 +3,29 @@ package de.dhbwloe.campusapp.fragments; import android.os.Bundle; import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentActivity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import de.dhbwloe.campusapp.CampusAppFragment; import de.dhbwloe.campusapp.R; -import de.dhbwloe.campusapp.search.SearchIndices; /** * A simple {@link Fragment} subclass. */ -public class MensaWochenplan extends CampusAppFragment { +public class DashboardNewsDhbw extends CampusAppFragment { - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.fragment_mensa_wochenplan, container, false); - AppContext.setTitle("Mensa Wochenplan: "); + public DashboardNewsDhbw() { + // Required empty public constructor + } - return view; + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + // Inflate the layout for this fragment + return inflater.inflate(R.layout.fragment_dashboard_news_dhbw, container, false); } } diff --git a/app/src/main/java/de/dhbwloe/campusapp/fragments/DashboardNewsStuv.java b/app/src/main/java/de/dhbwloe/campusapp/fragments/DashboardNewsStuv.java new file mode 100644 index 0000000..73d83ec --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/fragments/DashboardNewsStuv.java @@ -0,0 +1,31 @@ +package de.dhbwloe.campusapp.fragments; + + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import de.dhbwloe.campusapp.CampusAppFragment; +import de.dhbwloe.campusapp.R; + +/** + * A simple {@link Fragment} subclass. + */ +public class DashboardNewsStuv extends CampusAppFragment { + + + public DashboardNewsStuv() { + // Required empty public constructor + } + + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + // Inflate the layout for this fragment + return inflater.inflate(R.layout.fragment_dashboard_news_stuv, container, false); + } + +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/fragments/DashboardVorlesungsplan.java b/app/src/main/java/de/dhbwloe/campusapp/fragments/DashboardVorlesungsplan.java new file mode 100644 index 0000000..e67b364 --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/fragments/DashboardVorlesungsplan.java @@ -0,0 +1,229 @@ +package de.dhbwloe.campusapp.fragments; + + +import android.os.Build; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.RelativeLayout; +import android.widget.TextView; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.List; + +import de.dhbwloe.campusapp.CampusAppContext; +import de.dhbwloe.campusapp.CampusAppFragment; +import de.dhbwloe.campusapp.R; +import de.dhbwloe.campusapp.Tools; +import de.dhbwloe.campusapp.vorlesungen.CourseEvent; + +/** + * A simple {@link Fragment} subclass. + */ +public class DashboardVorlesungsplan extends CampusAppFragment { + private static int lastGeneratedViewId = 572000; + private View view; + private List events1 = new ArrayList(); + private List events2 = new ArrayList(); + private String courseName; + + public DashboardVorlesungsplan() { + // Required empty public constructor + } + + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + view = inflater.inflate(R.layout.fragment_dashboard_timetable, container, false); + String kursTag = AppContext.getDatabaseManager().getRuntimeCache("CourseName"); + if(kursTag == null || kursTag.isEmpty()) { + return null; + } + courseName = kursTag; + return view; + } + + @Override + public void onResume() { + super.onResume(); + if(courseName != null) + loadViewData(); + } + + private int getDayId(long time) { + Date date = new Date(time * 1000); + return getDayId(date); + } + private int getDayId(Date date) { + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd"); + return Integer.parseInt(dateFormat.format(date)); + } + + private void loadViewData() { + if(AppContext == null) + AppContext = CampusAppContext.getInstance(); + long now = (new Date()).getTime() / 1000; + long tomorrow = now + 86400; + + CourseEvent events[] = AppContext.getDatabaseManager().getCourseCalendarTimetable(courseName, now, 2); + events1.clear(); + events2.clear(); + if(events.length > 0) { + int firstDayId = getDayId(events[0].getEventTo()); + for (int i = 0; i < events.length; i++) { + if (i == 0 || getDayId(events[i].getEventTo()) == firstDayId) + events1.add(events[i]); + else + events2.add(events[i]); + } + } + + RelativeLayout container; + + container = (RelativeLayout)view.findViewById(R.id.date1Overview); + buildDayView(container, events1); + container.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Bundle args = new Bundle(); + if (events1.size() > 0) { + long date = events1.get(0).getEventTo(); + args.putLong("date", date); + } + AppContext.getNavigationManager().navigatePage("Vorlesungsplan", args); + } + }); + + container = (RelativeLayout)view.findViewById(R.id.date2Overview); + buildDayView(container, events2); + container.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Bundle args = new Bundle(); + if (events1.size() > 0) { + long date = events2.get(0).getEventTo(); + args.putLong("date", date); + } + AppContext.getNavigationManager().navigatePage("Vorlesungsplan", args); + } + }); + + int todayDayId = getDayId(now); + int tomorrowDayId = getDayId(tomorrow); + Date date; + + TextView date1 = (TextView) view.findViewById(R.id.dateView1); + if(events1.size() > 0) { + date = new Date(events1.get(0).getEventTo() * 1000); + date1.setText(getDateText(date, todayDayId, tomorrowDayId)); + } else + date1.setText(""); + + TextView date2 = (TextView) view.findViewById(R.id.dateView2); + if(events2.size() > 0) { + date = new Date(events2.get(0).getEventTo() * 1000); + date2.setText(getDateText(date, todayDayId, tomorrowDayId)); + } else + date2.setText(""); + } + + private void buildDayView(RelativeLayout container, List events) { + container.removeAllViews(); + + Date date; + TextView leftCol, rightCol = null; + RelativeLayout.LayoutParams params; + SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm"); + int firstLeftColId = 0; + + if(events.size() > 0) { + leftCol = new TextView(AppContext.getMainActivity()); + params = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, + RelativeLayout.LayoutParams.WRAP_CONTENT); + params.setMargins(8, 4, 0, 0); + leftCol.setLayoutParams(params); + leftCol.setId(generateViewId()); + firstLeftColId = leftCol.getId(); + container.addView(leftCol); + } else { + leftCol = null; + } + + for(int i = 0; i < events.size(); i++) { + CourseEvent event = events.get(i); + date = new Date(event.getEventFrom()*1000); + leftCol.setText(dateFormat.format(date)); + + rightCol = new TextView(AppContext.getMainActivity()); + rightCol.setId(generateViewId()); + params = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, + RelativeLayout.LayoutParams.WRAP_CONTENT); + params.setMargins(12, 12, 0, 0); + /* + android:layout_alignTop="@+id/date1time2" + android:layout_toRightOf="@+id/date1time1" + android:layout_toEndOf="@+id/date1time1" + */ + params.addRule(RelativeLayout.ALIGN_TOP, leftCol.getId()); + params.addRule(RelativeLayout.RIGHT_OF, firstLeftColId); + params.addRule(RelativeLayout.END_OF, firstLeftColId); + rightCol.setLayoutParams(params); + + String coursetitle = ""; + if(event.getCourseType() == CourseEvent.CourseType.COURSETYPE_KLAUSUR) + coursetitle += AppContext.getResString(R.string.dashboard_calendar_klausur); + coursetitle += event.getEventShortTitle(); + rightCol.setText(coursetitle); + + + leftCol = new TextView(AppContext.getMainActivity()); + leftCol.setId(generateViewId()); + params = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, + RelativeLayout.LayoutParams.WRAP_CONTENT); + params.setMargins(8, -8, 0, 0); + params.addRule(RelativeLayout.BELOW, rightCol.getId()); + leftCol.setLayoutParams(params); + + date = new Date(event.getEventTo()*1000); + leftCol.setText(dateFormat.format(date)); + + + container.addView(rightCol); + container.addView(leftCol); + } + } + + private String getDateText(Date eventDate, int todayDayId, int tomorrowDayId) { + int date1DayId = getDayId(eventDate); + String datetext; + if(date1DayId == todayDayId) + datetext = AppContext.getResString(R.string.dashboard_calendar_today); + else if(date1DayId == tomorrowDayId) + datetext = AppContext.getResString(R.string.dashboard_calendar_tomorrow); + else { + int weekdayResIds[] = new int[] { R.string.week_sunday, R.string.week_monday, R.string.week_tuesday, R.string.week_wednesday, R.string.week_thursday, R.string.week_friday, R.string.week_saturday }; + Calendar cal = Calendar.getInstance(); + cal.setTime(eventDate); + int dow = cal.get(Calendar.DAY_OF_WEEK); + SimpleDateFormat dateFormat = new SimpleDateFormat("dd.MM"); + + datetext = AppContext.getResString(weekdayResIds[dow]) + ", " + dateFormat.format(eventDate); + } + return datetext; + } + + private int generateViewId() { + if (Build.VERSION.SDK_INT >= 17) + return View.generateViewId(); + else + return lastGeneratedViewId++; + } + +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/fragments/Mensa.java b/app/src/main/java/de/dhbwloe/campusapp/fragments/Mensa.java index ccc7785..2d641de 100644 --- a/app/src/main/java/de/dhbwloe/campusapp/fragments/Mensa.java +++ b/app/src/main/java/de/dhbwloe/campusapp/fragments/Mensa.java @@ -15,6 +15,7 @@ import android.view.ViewGroup; import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Calendar; import java.util.Date; import java.util.List; @@ -41,7 +42,6 @@ public class Mensa extends CampusAppFragment { private TabLayout tabLayout; private ViewPager viewPager; - private boolean viewDayplan; private long viewDate; @Override @@ -56,34 +56,10 @@ public class Mensa extends CampusAppFragment { long date; if(showdate != null && !showdate.isEmpty() && (date = Long.parseLong(showdate)) > 0) { viewDate = date; - viewDayplan = true; - viewmodeSet = true; - } - if(!viewmodeSet) { - String viewmode = arguments.getString("viewmode"); - if(viewmode != null && !viewmode.isEmpty()) { - if(viewmode.equalsIgnoreCase("day")) - viewDayplan = true; - else - viewDayplan = false; - viewmodeSet = true; - } - } - } - if(!viewmodeSet) { - String lastViewmode = AppContext.getDatabaseManager().getRuntimeCache("MensaplanViewMode"); - if(lastViewmode != null && !lastViewmode.isEmpty()) { - if(lastViewmode.equalsIgnoreCase("day")) - viewDayplan = true; - else - viewDayplan = false; - viewmodeSet = true; } } - if(!viewmodeSet) - viewDayplan = true; - AppContext.setTitle("Mensa "+(viewDayplan ? "Tagesplan" : "Wochenplan")); + AppContext.setTitle("Mensa Speiseplan"); /* toolbar = (Toolbar) view.findViewById(R.id.toolbar); @@ -106,14 +82,30 @@ public class Mensa extends CampusAppFragment { long[] showDates; long now = (new Date()).getTime()/1000; - if(viewDayplan) - showDates = AppContext.getDatabaseManager().getDaysWithPlanData(now - (30 * 86400), now + (30 * 86400)); - else - showDates = AppContext.getDatabaseManager().getDaysWithPlanData(now - (365 * 86400), now + (30 * 86400)); + if(viewDate < now) + now = viewDate; + + int weekdayResIds[] = new int[] { R.string.week_monday_short, R.string.week_tuesday_short, R.string.week_wednesday_short, R.string.week_thursday_short, R.string.week_friday_short, R.string.week_saturday_short, R.string.week_sunday_short }; + Calendar cal = Calendar.getInstance(); + cal.setTime(new Date(now * 1000)); + cal.set(Calendar.HOUR_OF_DAY, 0); + cal.set(Calendar.MINUTE, 0); + cal.set(Calendar.SECOND, 0); + cal.set(Calendar.MILLISECOND, 0); + long midnight = cal.getTimeInMillis() / 1000; + int dow = cal.get(Calendar.DAY_OF_WEEK); + dow -= 2; + if(dow < 0) // monday should be 0! + dow += 7; + long subTimeToWeekStart = (86400 * dow); + long showDateFrom = midnight - subTimeToWeekStart; + + showDates = AppContext.getDatabaseManager().getDaysWithPlanData(showDateFrom, now + (30 * 86400)); int activeItem = -1; long activeItemDiff = -1; Bundle args = getArguments(); + int lastWeekday = 0; for(int i = 0; i < showDates.length; i++) { long date = showDates[i]; Bundle bundle = new Bundle(); @@ -121,10 +113,12 @@ public class Mensa extends CampusAppFragment { bundle.putAll(args); String title; SimpleDateFormat sdf; - if(viewDayplan) - sdf = new SimpleDateFormat("dd.MM."); - else - sdf = new SimpleDateFormat("WW"); + + cal.setTime(new Date(date * 1000)); + int weekday = cal.get(Calendar.DAY_OF_WEEK); + weekday -= 2; // monday should be 0! + if(weekday < 0) + weekday += 7; if(viewDate > 0) { long diff = Math.abs(viewDate - date); @@ -134,16 +128,21 @@ public class Mensa extends CampusAppFragment { } } - title = sdf.format(new Date(date * 1000)); + if(lastWeekday > weekday) { + adapter.addFragment(null, null); + } + lastWeekday = weekday; + + title = AppContext.getResString(weekdayResIds[weekday]); bundle.putLong("date", date); adapter.addFragment(bundle, title); } viewPager.setAdapter(adapter); viewPager.setCurrentItem(activeItem); - if(viewDayplan) { - MensaTagesplan tagesplan = (MensaTagesplan)adapter.getItem(activeItem); - tagesplan.onSetActive(); - } + + MensaTagesplan tagesplan = (MensaTagesplan)adapter.getItem(activeItem); + tagesplan.onSetActive(); + viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override @@ -153,10 +152,8 @@ public class Mensa extends CampusAppFragment { @Override public void onPageSelected(int position) { - if(viewDayplan) { - MensaTagesplan tagesplan = (MensaTagesplan)adapter.getItem(position); - tagesplan.onSetActive(); - } + MensaTagesplan tagesplan = (MensaTagesplan)adapter.getItem(position); + tagesplan.onSetActive(); } @Override @@ -184,10 +181,8 @@ public class Mensa extends CampusAppFragment { } if(mFragmentList.get(position) != null) return mFragmentList.get(position); - if(viewDayplan) - fragment = new MensaTagesplan(); - else - fragment = new MensaWochenplan(); + fragment = new MensaTagesplan(); + fragment.setArguments(mFragmentDataList.get(position)); mFragmentList.set(position, fragment); return fragment; @@ -199,6 +194,14 @@ public class Mensa extends CampusAppFragment { } public void addFragment(Bundle fragmentdata, String title) { + if(title == null) { + Bundle bnd = new Bundle(); + bnd.putBoolean("parentIsMensaFragment", true); + mFragmentDataList.add(bnd); + mFragmentTitleList.add(""); + return; + } + fragmentdata.putBoolean("parentIsMensaFragment", true); mFragmentDataList.add(fragmentdata); mFragmentTitleList.add(title); diff --git a/app/src/main/java/de/dhbwloe/campusapp/fragments/MensaTagesplan.java b/app/src/main/java/de/dhbwloe/campusapp/fragments/MensaTagesplan.java index 1d1022a..0a6f53c 100644 --- a/app/src/main/java/de/dhbwloe/campusapp/fragments/MensaTagesplan.java +++ b/app/src/main/java/de/dhbwloe/campusapp/fragments/MensaTagesplan.java @@ -16,6 +16,7 @@ import java.util.ArrayList; import java.util.Calendar; import java.util.Date; +import de.dhbwloe.campusapp.CampusAppContext; import de.dhbwloe.campusapp.CampusAppFragment; import de.dhbwloe.campusapp.R; @@ -26,9 +27,13 @@ public class MensaTagesplan extends CampusAppFragment { private ArrayList tagesplanMenueItems = new ArrayList<>(); private Date viewPlanDate; private boolean setActiveOnLoad = false; + private boolean redirectPage = true; public void onSetActive() { + if(AppContext == null) + AppContext = CampusAppContext.getInstance(); if(viewPlanDate == null) { + AppContext.setTitle(""); setActiveOnLoad = true; return; } @@ -40,6 +45,9 @@ public class MensaTagesplan extends CampusAppFragment { Bundle args = getArguments(); Date planDay = null; if(args != null) { + boolean mensaparent = args.getBoolean("parentIsMensaFragment"); + if(mensaparent) + redirectPage = false; long plandate = args.getLong("date"); if(plandate > 0) { planDay = new Date(plandate * 1000); @@ -70,7 +78,11 @@ public class MensaTagesplan extends CampusAppFragment { View view = inflater.inflate(R.layout.fragment_mensa_tagesplan, container, false); if(viewPlanDate == null) { - AppContext.getNavigationManager().navigatePage("Mensa", null, false); // silent redirect + if(redirectPage) + AppContext.getNavigationManager().navigatePage("Mensa", null, false); // silent redirect + else { + return inflater.inflate(R.layout.fragment_mensa_weekend, container, false); + } return null; } if(setActiveOnLoad) { diff --git a/app/src/main/java/de/dhbwloe/campusapp/fragments/Vorlesungsplan.java b/app/src/main/java/de/dhbwloe/campusapp/fragments/Vorlesungsplan.java index caf8c88..6428e63 100644 --- a/app/src/main/java/de/dhbwloe/campusapp/fragments/Vorlesungsplan.java +++ b/app/src/main/java/de/dhbwloe/campusapp/fragments/Vorlesungsplan.java @@ -2,11 +2,21 @@ package de.dhbwloe.campusapp.fragments; import android.os.Bundle; +import android.support.design.widget.TabLayout; import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentPagerAdapter; +import android.support.v4.view.ViewPager; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.List; + import de.dhbwloe.campusapp.CampusAppFragment; import de.dhbwloe.campusapp.R; import de.dhbwloe.campusapp.search.SearchIndices; @@ -15,6 +25,10 @@ import de.dhbwloe.campusapp.search.SearchIndices; * A simple {@link Fragment} subclass. */ public class Vorlesungsplan extends CampusAppFragment { + public interface VorlesungsplanFragment { + void setActive(); + } + /* implement this for search results ;) */ public static SearchIndices[] GetSearchIndices() { return new SearchIndices[] { @@ -25,6 +39,13 @@ public class Vorlesungsplan extends CampusAppFragment { setDescription("Vorlesungsplan dienes Kurses"); addKeyWord("vorlesung, vorlesungen, plan, vorlesungsplan, stundenplan, termin, termine, kursplan, blockplan, block, zeit, zeiten"); }}, + new SearchIndices("Vorlesungsplan", true) {{ + setUpdateTime(1); + setTarget("#Vorlesungsplan#view=exams"); + setTitle("Vorlesungsplan"); + setDescription("Klausurübersicht dienes Kurses"); + addKeyWord("klausuren, prüfungen, exams, termine, zeitpunkt, plan, ergebnisse"); + }}, }; } @@ -34,7 +55,75 @@ public class Vorlesungsplan extends CampusAppFragment { View view = inflater.inflate(R.layout.fragment_vorlesungsplan, container, false); AppContext.setTitle("Vorlesungsplan"); + ViewPager pager = (ViewPager) view.findViewById(R.id.viewpager); + setupViewPager(pager); + + TabLayout tabLayout = (TabLayout) view.findViewById(R.id.tabs); + tabLayout.setupWithViewPager(pager); + return view; } + private void setupViewPager(ViewPager viewPager) { + final ViewPagerAdapter adapter = new ViewPagerAdapter(getChildFragmentManager()); + + adapter.addFragment(new VorlesungsplanUpcoming(), AppContext.getResString(R.string.vorlesungsplan_upcoming)); + adapter.addFragment(new VorlesungsplanGroups(), AppContext.getResString(R.string.vorlesungsplan_groups)); + adapter.addFragment(new VorlesungsplanExams(), AppContext.getResString(R.string.vorlesungsplan_exams)); + + viewPager.setAdapter(adapter); + viewPager.setCurrentItem(0); + + VorlesungsplanFragment fragment = (VorlesungsplanFragment)adapter.getItem(0); + fragment.setActive(); + + + viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + + } + + @Override + public void onPageSelected(int position) { + VorlesungsplanFragment fragment = (VorlesungsplanFragment)adapter.getItem(position); + fragment.setActive(); + } + + @Override + public void onPageScrollStateChanged(int state) { + + } + }); + } + + class ViewPagerAdapter extends FragmentPagerAdapter { + private final List mFragmentList = new ArrayList<>(); + private final List mFragmentTitleList = new ArrayList<>(); + + public ViewPagerAdapter(FragmentManager manager) { + super(manager); + } + + @Override + public Fragment getItem(int position) { + return mFragmentList.get(position); + } + + @Override + public int getCount() { + return mFragmentList.size(); + } + + public void addFragment(Fragment fragment, String title) { + mFragmentList.add(fragment); + mFragmentTitleList.add(title); + } + + @Override + public CharSequence getPageTitle(int position) { + return mFragmentTitleList.get(position); + } + } + } diff --git a/app/src/main/java/de/dhbwloe/campusapp/fragments/VorlesungsplanExams.java b/app/src/main/java/de/dhbwloe/campusapp/fragments/VorlesungsplanExams.java new file mode 100644 index 0000000..9dc6c02 --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/fragments/VorlesungsplanExams.java @@ -0,0 +1,36 @@ +package de.dhbwloe.campusapp.fragments; + + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import de.dhbwloe.campusapp.CampusAppFragment; +import de.dhbwloe.campusapp.R; + +/** + * A simple {@link Fragment} subclass. + */ +public class VorlesungsplanExams extends CampusAppFragment implements Vorlesungsplan.VorlesungsplanFragment { + + + public VorlesungsplanExams() { + // Required empty public constructor + } + + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + // Inflate the layout for this fragment + return inflater.inflate(R.layout.fragment_vorlesungsplan_exams, container, false); + } + + @Override + public void setActive() { + + } + +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/fragments/VorlesungsplanGroups.java b/app/src/main/java/de/dhbwloe/campusapp/fragments/VorlesungsplanGroups.java new file mode 100644 index 0000000..a685709 --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/fragments/VorlesungsplanGroups.java @@ -0,0 +1,109 @@ +package de.dhbwloe.campusapp.fragments; + + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.CheckBox; +import android.widget.CompoundButton; +import android.widget.ListView; + +import java.util.ArrayList; +import java.util.Date; + +import de.dhbwloe.campusapp.CampusAppFragment; +import de.dhbwloe.campusapp.R; +import de.dhbwloe.campusapp.vorlesungen.CourseGroup; + +/** + * A simple {@link Fragment} subclass. + */ +public class VorlesungsplanGroups extends CampusAppFragment implements Vorlesungsplan.VorlesungsplanFragment { + private String coursename; + private View view; + private VorlesungsplanGroupsListAdapter listAdapter; + private ArrayList listItems = new ArrayList(); + private boolean showOldCourses = false; + + public VorlesungsplanGroups() { + // Required empty public constructor + } + + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + String kursTag = AppContext.getDatabaseManager().getRuntimeCache("CourseName"); + if(kursTag == null || kursTag.isEmpty()) + return null; + coursename = kursTag; + + view = inflater.inflate(R.layout.fragment_vorlesungsplan_groups, container, false); + + ListView listView = (ListView) view.findViewById(R.id.listView); + listAdapter = new VorlesungsplanGroupsListAdapter(view.getContext(), R.layout.fragment_vorlesungsplan_groups_course, listItems); + listView.setAdapter(listAdapter); + + CheckBox showOldChkBox = (CheckBox) view.findViewById(R.id.showOldCourses); + showOldChkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + showOldCourses = isChecked; + updateCoursesList(); + } + }); + + return view; + } + + @Override + public void setActive() { + + } + + + @Override + public void onResume() { + super.onResume(); + if(coursename != null) + updateCoursesList(); + } + + private void updateCoursesList() { + Date notBefore; + if(showOldCourses) + notBefore = null; + else + notBefore = new Date(); + + CourseGroup[] groups = AppContext.getDatabaseManager().getCourseGroups(coursename, notBefore); + listItems.clear(); + for(CourseGroup group : groups) { + Bundle extraData = group.getExtraData(); + long nextKlausur = extraData.getLong("NextKlausurEvent"); + long lastEvent = extraData.getLong("LastEvent"); + + // wenn nextKlausur über 5 jahrevon LastEvent entfernt liegt gibt es in diesem kurs KEINE Klausur. + // der fiktive Wert musste zur technischen funktionalität der Datenbankabfrage eingefügt werden. + if(nextKlausur - lastEvent > (86400 * 365 * 5)) + nextKlausur = 0; + + VorlesungsplanGroupsListItem item = new VorlesungsplanGroupsListItem( + group.getGroupId(), + coursename, + group.getGroupName(), + extraData.getLong("FirstEvent"), + lastEvent, + extraData.getInt("EventCount"), + extraData.getLong("NextEvent"), + nextKlausur + ); + listItems.add(item); + } + if(listAdapter != null) + listAdapter.notifyDataSetChanged(); + } + +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/fragments/VorlesungsplanGroupsListAdapter.java b/app/src/main/java/de/dhbwloe/campusapp/fragments/VorlesungsplanGroupsListAdapter.java new file mode 100644 index 0000000..d7c5010 --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/fragments/VorlesungsplanGroupsListAdapter.java @@ -0,0 +1,77 @@ +package de.dhbwloe.campusapp.fragments; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.LinearLayout; +import android.widget.TextView; + +import java.util.ArrayList; + +import de.dhbwloe.campusapp.CampusAppContext; +import de.dhbwloe.campusapp.R; + +/** + * Created by pk910 on 20.02.2016. + */ +public class VorlesungsplanGroupsListAdapter extends ArrayAdapter { + private Context context; + private int layoutResourceId; + private ArrayList data = new ArrayList(); + + public VorlesungsplanGroupsListAdapter(Context context, int layoutResourceId, ArrayList data) { + super(context, layoutResourceId, data); + this.layoutResourceId = layoutResourceId; + this.context = context; + this.data = data; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + View row = convertView; + RecordHolder holder = null; + + if (row == null) { + LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + row = inflater.inflate(layoutResourceId, parent, false); + + holder = new RecordHolder(row); + row.setTag(holder); + } else { + holder = (RecordHolder) row.getTag(); + } + + final VorlesungsplanGroupsListAdapter that = this; + final VorlesungsplanGroupsListItem item = data.get(position); + + holder.groupName.setText(item.getGroupName()); + holder.nextEvent.setText(item.getNextEvent()); + holder.lastEvent.setText(item.getLastEvent()); + + String klausurDate = item.getNextKlausurEvent(); + if(klausurDate == null) + klausurDate = CampusAppContext.getInstance().getResString(R.string.vorlesungsplan_groups_noklausur); + + holder.klausurEvent.setText(klausurDate); + + return row; + } + + static class RecordHolder { + TextView groupName; + TextView nextEvent; + TextView lastEvent; + TextView klausurEvent; + TextView klausurName; + + public RecordHolder(View view) { + this.groupName = (TextView) view.findViewById(R.id.courseTitle); + this.nextEvent = (TextView) view.findViewById(R.id.nextEvent); + this.lastEvent = (TextView) view.findViewById(R.id.lastEvent); + this.klausurEvent = (TextView) view.findViewById(R.id.klausurEvent); + this.klausurName = (TextView) view.findViewById(R.id.klausurName); + } + } +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/fragments/VorlesungsplanGroupsListItem.java b/app/src/main/java/de/dhbwloe/campusapp/fragments/VorlesungsplanGroupsListItem.java new file mode 100644 index 0000000..01d6ee7 --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/fragments/VorlesungsplanGroupsListItem.java @@ -0,0 +1,68 @@ +package de.dhbwloe.campusapp.fragments; + +import java.text.SimpleDateFormat; +import java.util.Date; + +/** + * Created by pk910 on 20.02.2016. + */ +public class VorlesungsplanGroupsListItem { + private int groupId; + private String courseName; + private String groupName; + private long firstEvent, lastEvent, nextEvent, nextKlausurEvent; + private int eventCount; + + public VorlesungsplanGroupsListItem(int groupid, String coursename, String groupname, long firstevent, long lastevent, int eventcount, long nextevent, long nextklausur) { + groupId = groupid; + courseName = coursename; + groupName = groupname; + firstEvent = firstevent; + lastEvent = lastevent; + eventCount = eventcount; + nextEvent = nextevent; + nextKlausurEvent = nextklausur; + } + + public int getGroupId() { + return groupId; + } + + public String getCourseName() { + return courseName; + } + + public String getGroupName() { + return groupName; + } + + public String getFirstEvent() { + SimpleDateFormat dateFormat = new SimpleDateFormat("dd.MM.yyyy HH:mm"); + Date date = new Date(firstEvent*1000); + return dateFormat.format(date); + } + + public String getLastEvent() { + SimpleDateFormat dateFormat = new SimpleDateFormat("dd.MM.yyyy HH:mm"); + Date date = new Date(lastEvent*1000); + return dateFormat.format(date); + } + + public String getNextEvent() { + SimpleDateFormat dateFormat = new SimpleDateFormat("dd.MM.yyyy HH:mm"); + Date date = new Date(nextEvent*1000); + return dateFormat.format(date); + } + + public String getNextKlausurEvent() { + if(nextKlausurEvent == 0) + return null; + SimpleDateFormat dateFormat = new SimpleDateFormat("dd.MM.yyyy HH:mm"); + Date date = new Date(nextKlausurEvent*1000); + return dateFormat.format(date); + } + + public int getEventCount() { + return eventCount; + } +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/fragments/VorlesungsplanUpcoming.java b/app/src/main/java/de/dhbwloe/campusapp/fragments/VorlesungsplanUpcoming.java new file mode 100644 index 0000000..29bbee5 --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/fragments/VorlesungsplanUpcoming.java @@ -0,0 +1,96 @@ +package de.dhbwloe.campusapp.fragments; + + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ListView; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; + +import de.dhbwloe.campusapp.CampusAppContext; +import de.dhbwloe.campusapp.CampusAppFragment; +import de.dhbwloe.campusapp.R; +import de.dhbwloe.campusapp.vorlesungen.CourseEvent; + +/** + * A simple {@link Fragment} subclass. + */ +public class VorlesungsplanUpcoming extends CampusAppFragment implements Vorlesungsplan.VorlesungsplanFragment { + private View view; + private String courseName; + private ArrayList upcomingDays = new ArrayList(); + private VorlesungsplanUpcomingDayListAdapter upcomingDaysAdapter; + + public VorlesungsplanUpcoming() { + // Required empty public constructor + } + + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + view = inflater.inflate(R.layout.fragment_vorlesungsplan_upcoming, container, false); + + String kursTag = AppContext.getDatabaseManager().getRuntimeCache("CourseName"); + if(kursTag == null || kursTag.isEmpty()) { + return null; + } + courseName = kursTag; + + ListView listView = (ListView) view.findViewById(R.id.listView); + upcomingDaysAdapter = new VorlesungsplanUpcomingDayListAdapter(view.getContext(), R.layout.fragment_vorlesungsplan_upcoming_day, upcomingDays); + listView.setAdapter(upcomingDaysAdapter); + + return view; + } + + @Override + public void setActive() { + + } + + @Override + public void onResume() { + super.onResume(); + if(courseName != null) + refreshUpcomingEvents(); + } + + private int getDayId(long time) { + Date date = new Date(time * 1000); + return getDayId(date); + } + private int getDayId(Date date) { + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd"); + return Integer.parseInt(dateFormat.format(date)); + } + + private void refreshUpcomingEvents() { + if(AppContext == null) + AppContext = CampusAppContext.getInstance(); + long now = (new Date()).getTime() / 1000; + + CourseEvent events[] = AppContext.getDatabaseManager().getCourseCalendarTimetable(courseName, now, 365); + int lastDayId = 0; + VorlesungsplanUpcomingDayListItem lastDayItem = null; + upcomingDays.clear(); + for(int i = 0; i < events.length; i++) { + int dayId = getDayId(events[i].getEventTo()); + if(dayId > lastDayId) { + lastDayItem = new VorlesungsplanUpcomingDayListItem(events[i].getEventTo()); + upcomingDays.add(lastDayItem); + lastDayId = dayId; + } + lastDayItem.addCourseEvent(events[i]); + } + if(upcomingDaysAdapter != null) { + upcomingDaysAdapter.notifyDataSetChanged(); + } + } + +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/fragments/VorlesungsplanUpcomingCourseListItem.java b/app/src/main/java/de/dhbwloe/campusapp/fragments/VorlesungsplanUpcomingCourseListItem.java new file mode 100644 index 0000000..e706e3f --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/fragments/VorlesungsplanUpcomingCourseListItem.java @@ -0,0 +1,42 @@ +package de.dhbwloe.campusapp.fragments; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.TextView; + +import java.text.SimpleDateFormat; +import java.util.Date; + +import de.dhbwloe.campusapp.CampusAppContext; +import de.dhbwloe.campusapp.R; +import de.dhbwloe.campusapp.vorlesungen.CourseEvent; + +/** + * Created by pk910 on 20.02.2016. + */ +public class VorlesungsplanUpcomingCourseListItem { + private CourseEvent event; + + public VorlesungsplanUpcomingCourseListItem(CourseEvent event) { + this.event = event; + } + + public void updateContainerView(View view) { + + SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm"); + TextView timeFrom = (TextView) view.findViewById(R.id.timeFrom); + TextView timeTo = (TextView) view.findViewById(R.id.timeTo); + TextView location = (TextView) view.findViewById(R.id.location); + TextView courseTitle = (TextView) view.findViewById(R.id.courseTitle); + + Date dateFrom = (new Date(event.getEventFrom() * 1000)); + timeFrom.setText(dateFormat.format(dateFrom)); + + Date dateTo = (new Date(event.getEventTo() * 1000)); + timeTo.setText(dateFormat.format(dateTo)); + + location.setText(event.getEventLocation()); + courseTitle.setText(event.getEventTitle()); + } +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/fragments/VorlesungsplanUpcomingDayListAdapter.java b/app/src/main/java/de/dhbwloe/campusapp/fragments/VorlesungsplanUpcomingDayListAdapter.java new file mode 100644 index 0000000..90916f7 --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/fragments/VorlesungsplanUpcomingDayListAdapter.java @@ -0,0 +1,65 @@ +package de.dhbwloe.campusapp.fragments; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.LinearLayout; +import android.widget.ListView; +import android.widget.RelativeLayout; +import android.widget.TextView; + +import java.util.ArrayList; + +import de.dhbwloe.campusapp.R; + +/** + * Created by pk910 on 20.02.2016. + */ +public class VorlesungsplanUpcomingDayListAdapter extends ArrayAdapter { + private Context context; + private int layoutResourceId; + private ArrayList data = new ArrayList(); + + public VorlesungsplanUpcomingDayListAdapter(Context context, int layoutResourceId, ArrayList data) { + super(context, layoutResourceId, data); + this.layoutResourceId = layoutResourceId; + this.context = context; + this.data = data; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + View row = convertView; + RecordHolder holder = null; + + if (row == null) { + LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + row = inflater.inflate(layoutResourceId, parent, false); + + holder = new RecordHolder(row); + row.setTag(holder); + } else { + holder = (RecordHolder) row.getTag(); + } + + final VorlesungsplanUpcomingDayListAdapter that = this; + final VorlesungsplanUpcomingDayListItem item = data.get(position); + + item.addCourseEventsToContainer(holder.container); + holder.cardDate.setText(item.getFormatedDate()); + + return row; + } + + static class RecordHolder { + TextView cardDate; + LinearLayout container; + + public RecordHolder(View view) { + this.cardDate = (TextView) view.findViewById(R.id.cardDate); + this.container = (LinearLayout) view.findViewById(R.id.cardCourses); + } + } +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/fragments/VorlesungsplanUpcomingDayListItem.java b/app/src/main/java/de/dhbwloe/campusapp/fragments/VorlesungsplanUpcomingDayListItem.java new file mode 100644 index 0000000..d8d1104 --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/fragments/VorlesungsplanUpcomingDayListItem.java @@ -0,0 +1,72 @@ +package de.dhbwloe.campusapp.fragments; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.LinearLayout; +import android.widget.ListView; +import android.widget.RelativeLayout; +import android.widget.TextView; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; + +import de.dhbwloe.campusapp.CampusAppContext; +import de.dhbwloe.campusapp.R; +import de.dhbwloe.campusapp.Tools; +import de.dhbwloe.campusapp.vorlesungen.CourseEvent; + +/** + * Created by pk910 on 20.02.2016. + */ +public class VorlesungsplanUpcomingDayListItem { + private long cardDate; + private ArrayList courses = new ArrayList(); + + public VorlesungsplanUpcomingDayListItem(long date) { + cardDate = date; + } + + public void addCourseEventsToContainer(LinearLayout container) { + int courseEntries = container.getChildCount(); + + CampusAppContext AppContext = CampusAppContext.getInstance(); + LayoutInflater inflater = (LayoutInflater) AppContext.getMainActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE); + while(courseEntries < courses.size()) { + View view = inflater.inflate(R.layout.fragment_vorlesungsplan_upcoming_course, null); + container.addView(view); + courseEntries++; + } + + int i; + for(i = 0; i < courses.size(); i++) { + View child = container.getChildAt(i); + courses.get(i).updateContainerView(child); + child.setVisibility(View.VISIBLE); + } + for(;i < courseEntries; i++) { + View child = container.getChildAt(i); + child.setVisibility(View.GONE); + } + } + + public String getFormatedDate() { + SimpleDateFormat dateFormat = new SimpleDateFormat("dd.MM."); + Date date = new Date(cardDate * 1000); + + int weekdayResIds[] = new int[] { R.string.week_saturday, R.string.week_sunday, R.string.week_monday, R.string.week_tuesday, R.string.week_wednesday, R.string.week_thursday, R.string.week_friday }; + Calendar cal = Calendar.getInstance(); + cal.setTime(date); + int dow = cal.get(Calendar.DAY_OF_WEEK); + + return CampusAppContext.getInstance().getResString(weekdayResIds[dow]) + ", " + dateFormat.format(date); + } + + public void addCourseEvent(CourseEvent event) { + VorlesungsplanUpcomingCourseListItem courseItem = new VorlesungsplanUpcomingCourseListItem(event); + courses.add(courseItem); + } + +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/fragments/WifiSettings.java b/app/src/main/java/de/dhbwloe/campusapp/fragments/WifiSettings.java index a9bd972..1ce7001 100644 --- a/app/src/main/java/de/dhbwloe/campusapp/fragments/WifiSettings.java +++ b/app/src/main/java/de/dhbwloe/campusapp/fragments/WifiSettings.java @@ -12,6 +12,7 @@ import android.content.res.Resources; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.wifi.ScanResult; +import android.net.wifi.SupplicantState; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiEnterpriseConfig; import android.net.wifi.WifiInfo; @@ -44,6 +45,8 @@ import de.dhbwloe.campusapp.CampusAppContext; import de.dhbwloe.campusapp.CampusAppFragment; import de.dhbwloe.campusapp.R; import de.dhbwloe.campusapp.search.SearchIndices; +import de.dhbwloe.campusapp.wifi.WifiConfigurationManager; +import de.dhbwloe.campusapp.wifi.WifiNetworkSettings; /** * A simple {@link Fragment} subclass. @@ -62,88 +65,56 @@ public class WifiSettings extends CampusAppFragment { }; } - private enum WifiNetworkAuthenticationTypes { - WIFI_AUTHTYPE_NONE, - WIFI_AUTHTYPE_WEP, - WIFI_AUTHTYPE_WPA, - WIFI_AUTHTYPE_WPA_ENTERPRISE, - }; - - private class WifiNetworkSettingsSet { - String name; - String ssid; - WifiNetworkAuthenticationTypes authType; - int[] authAlgorithms; - int eapMethod; - int phase2Method; - - String caCertStr; - String caName; - - String usernameSuffix; - String username; - String password; - - TabLayout.Tab tab; - boolean isInScanResult = false; - - public WifiNetworkSettingsSet(String name, String ssid, WifiNetworkAuthenticationTypes authType) { - this.name = name; - this.ssid = ssid; - this.authType = authType; - } - - public WifiNetworkSettingsSet setWpaEnterprise(int[] authAlgorithms, int eapMethod, int phase2Method, String username) { - this.authAlgorithms = authAlgorithms; - this.eapMethod = eapMethod; - this.phase2Method = phase2Method; - this.username = username; - return this; - } - - public WifiNetworkSettingsSet setPassword(String password) { - this.password = password; - return this; - } - - public WifiNetworkSettingsSet setCACertificate(String caname, String certificate) { - this.caName = caname; - this.caCertStr = certificate; - return this; - } - - public WifiNetworkSettingsSet setUserSuffix(String suffix) { - this.usernameSuffix = suffix; - return this; - } - - public void setAuthData(String username, String password) { - this.username = username; - this.password = password; - } - - public void setAuthData(String password) { - this.password = password; - } - - public X509Certificate generateCertificate() throws CertificateException, IOException { - byte[] decoded = Base64.decode(caCertStr.replaceAll("-----BEGIN CERTIFICATE-----", "").replaceAll("-----END CERTIFICATE-----", ""), Base64.DEFAULT); - return (X509Certificate) CertificateFactory.getInstance("X.509").generateCertificate(new ByteArrayInputStream(decoded)); - } - } - private View view; private TabLayout tablayout; - private WifiNetworkSettingsSet[] wifiPresets; + private WifiNetworkSettings[] wifiPresets; private WifiManager wifiManager; + private WifiConfigurationManager wifiConfigManager; private ConnectivityManager connectivityManager; private int selectedNetworkIndex = 0; private boolean networkScanned = false; private BroadcastReceiver wifiStateReceiver = new BroadcastReceiver() { @Override - public void onReceive(Context arg0, Intent arg1) { - NetworkInfo networkInfo = (NetworkInfo) arg1.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO); - if (networkInfo.getType() == ConnectivityManager.TYPE_WIFI) { + public void onReceive(Context arg0, Intent intent) { + String action = intent.getAction(); + if(action.equals(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION)){ + WifiInfo myWifiInfo = wifiManager.getConnectionInfo(); + + WifiNetworkSettings settings = null; + for(int i = 0; i < wifiPresets.length; i++) { + if(wifiPresets[i].getSSID().equalsIgnoreCase(myWifiInfo.getSSID())) { + settings = wifiPresets[i]; + break; + } + } + + SupplicantState supl_state=((SupplicantState)intent.getParcelableExtra(WifiManager.EXTRA_NEW_STATE)); + switch(supl_state){ + case ASSOCIATING: + settings.isAssociating = true; + settings.isAssociated = false; + settings.isAuthenticated = false; + settings.isDisconnected = false; + break; + case ASSOCIATED: + settings.isAssociated = true; + break; + case COMPLETED: + settings.isAuthenticated = true; + break; + case DISCONNECTED: + settings.isAssociated = false; + settings.isAssociating = false; + settings.isDisconnected = true; + break; + default: + break; + } + updateWifiState(); + } + + NetworkInfo networkInfo = (NetworkInfo) intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO); + if (networkInfo != null && networkInfo.getType() == ConnectivityManager.TYPE_WIFI) { updateWifiState(); } } @@ -155,120 +126,18 @@ public class WifiSettings extends CampusAppFragment { } }; - private void loadDhbwWifiDefinitions() { - int numOfNetworks = 3; - int i = 0; - wifiPresets = new WifiNetworkSettingsSet[numOfNetworks]; - - wifiPresets[i++] = (new WifiNetworkSettingsSet("dhbw-secure", "dhbw-secure", WifiNetworkAuthenticationTypes.WIFI_AUTHTYPE_WPA_ENTERPRISE)). - setWpaEnterprise( - new int[] {WifiConfiguration.KeyMgmt.WPA_EAP, WifiConfiguration.KeyMgmt.IEEE8021X}, - WifiEnterpriseConfig.Eap.PEAP, - WifiEnterpriseConfig.Phase2.MSCHAPV2, - null - ).setCACertificate("Deutsche Telekom Root CA 2", "-----BEGIN CERTIFICATE-----\n" + - "MIIDnzCCAoegAwIBAgIBJjANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJERTEc\n" + - "MBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxlU2Vj\n" + - "IFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290IENB\n" + - "IDIwHhcNOTkwNzA5MTIxMTAwWhcNMTkwNzA5MjM1OTAwWjBxMQswCQYDVQQGEwJE\n" + - "RTEcMBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxl\n" + - "U2VjIFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290\n" + - "IENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrC6M14IspFLEU\n" + - "ha88EOQ5bzVdSq7d6mGNlUn0b2SjGmBmpKlAIoTZ1KXleJMOaAGtuU1cOs7TuKhC\n" + - "QN/Po7qCWWqSG6wcmtoIKyUn+WkjR/Hg6yx6m/UTAtB+NHzCnjwAWav12gz1Mjwr\n" + - "rFDa1sPeg5TKqAyZMg4ISFZbavva4VhYAUlfckE8FQYBjl2tqriTtM2e66foai1S\n" + - "NNs671x1Udrb8zH57nGYMsRUFUQM+ZtV7a3fGAigo4aKSe5TBY8ZTNXeWHmb0moc\n" + - "QqvF1afPaA+W5OFhmHZhyJF81j4A4pFQh+GdCuatl9Idxjp9y7zaAzTVjlsB9WoH\n" + - "txa2bkp/AgMBAAGjQjBAMB0GA1UdDgQWBBQxw3kbuvVT1xfgiXotF2wKsyudMzAP\n" + - "BgNVHRMECDAGAQH/AgEFMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOC\n" + - "AQEAlGRZrTlk5ynrE/5aw4sTV8gEJPB0d8Bg42f76Ymmg7+Wgnxu1MM9756Abrsp\n" + - "tJh6sTtU6zkXR34ajgv8HzFZMQSyzhfzLMdiNlXiItiJVbSYSKpk+tYcNthEeFpa\n" + - "IzpXl/V6ME+un2pMSyuOoAPjPuCp1NJ70rOo4nI8rZ7/gFnkm0W09juwzTkZmDLl\n" + - "6iFhkOQxIY40sfcvNUqFENrnijchvllj4PKFiDFT1FQUhXB59C4Gdyd1Lx+4ivn+\n" + - "xbrYNuSD7Odlt79jWvNGr4GUN9RBjNYj1h7P9WgbRGOiWrqnNVmh5XAFmw4jV5mU\n" + - "Cm26OWMohpLzGITY+9HPBVZkVw==\n" + - "-----END CERTIFICATE-----"); - - wifiPresets[i++] = (new WifiNetworkSettingsSet("dhbw-wlan", "dhbw-wlan", WifiNetworkAuthenticationTypes.WIFI_AUTHTYPE_NONE)); - - wifiPresets[i++] = (new WifiNetworkSettingsSet("eduroam", "eduroam", WifiNetworkAuthenticationTypes.WIFI_AUTHTYPE_WPA_ENTERPRISE)). - setWpaEnterprise( - new int[] {WifiConfiguration.KeyMgmt.WPA_EAP, WifiConfiguration.KeyMgmt.IEEE8021X}, - WifiEnterpriseConfig.Eap.PEAP, - WifiEnterpriseConfig.Phase2.MSCHAPV2, - null - ).setCACertificate("Deutsche Telekom Root CA 2", "-----BEGIN CERTIFICATE-----\n" + - "MIIDnzCCAoegAwIBAgIBJjANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJERTEc\n" + - "MBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxlU2Vj\n" + - "IFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290IENB\n" + - "IDIwHhcNOTkwNzA5MTIxMTAwWhcNMTkwNzA5MjM1OTAwWjBxMQswCQYDVQQGEwJE\n" + - "RTEcMBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxl\n" + - "U2VjIFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290\n" + - "IENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrC6M14IspFLEU\n" + - "ha88EOQ5bzVdSq7d6mGNlUn0b2SjGmBmpKlAIoTZ1KXleJMOaAGtuU1cOs7TuKhC\n" + - "QN/Po7qCWWqSG6wcmtoIKyUn+WkjR/Hg6yx6m/UTAtB+NHzCnjwAWav12gz1Mjwr\n" + - "rFDa1sPeg5TKqAyZMg4ISFZbavva4VhYAUlfckE8FQYBjl2tqriTtM2e66foai1S\n" + - "NNs671x1Udrb8zH57nGYMsRUFUQM+ZtV7a3fGAigo4aKSe5TBY8ZTNXeWHmb0moc\n" + - "QqvF1afPaA+W5OFhmHZhyJF81j4A4pFQh+GdCuatl9Idxjp9y7zaAzTVjlsB9WoH\n" + - "txa2bkp/AgMBAAGjQjBAMB0GA1UdDgQWBBQxw3kbuvVT1xfgiXotF2wKsyudMzAP\n" + - "BgNVHRMECDAGAQH/AgEFMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOC\n" + - "AQEAlGRZrTlk5ynrE/5aw4sTV8gEJPB0d8Bg42f76Ymmg7+Wgnxu1MM9756Abrsp\n" + - "tJh6sTtU6zkXR34ajgv8HzFZMQSyzhfzLMdiNlXiItiJVbSYSKpk+tYcNthEeFpa\n" + - "IzpXl/V6ME+un2pMSyuOoAPjPuCp1NJ70rOo4nI8rZ7/gFnkm0W09juwzTkZmDLl\n" + - "6iFhkOQxIY40sfcvNUqFENrnijchvllj4PKFiDFT1FQUhXB59C4Gdyd1Lx+4ivn+\n" + - "xbrYNuSD7Odlt79jWvNGr4GUN9RBjNYj1h7P9WgbRGOiWrqnNVmh5XAFmw4jV5mU\n" + - "Cm26OWMohpLzGITY+9HPBVZkVw==\n" + - "-----END CERTIFICATE-----"). - setUserSuffix("@dhbw-loerrach.de"); - } - - - @TargetApi(18) - private void connectToWiFi(WifiNetworkSettingsSet settings) { - WifiConfiguration wifiConfig = new WifiConfiguration(); - wifiConfig.SSID = settings.ssid; - for(int i = 0; i < settings.authAlgorithms.length; i++) - wifiConfig.allowedKeyManagement.set(settings.authAlgorithms[i]); - - switch(settings.authType) { - case WIFI_AUTHTYPE_NONE: - break; - case WIFI_AUTHTYPE_WEP: - // not supported by our app - break; - case WIFI_AUTHTYPE_WPA: - - break; - case WIFI_AUTHTYPE_WPA_ENTERPRISE: - WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig(); - String username = settings.username; - if(settings.usernameSuffix != null) - username += settings.usernameSuffix; - enterpriseConfig.setIdentity(username); - enterpriseConfig.setPassword(settings.password); - enterpriseConfig.setEapMethod(settings.eapMethod); - enterpriseConfig.setPhase2Method(settings.phase2Method); - try { - enterpriseConfig.setCaCertificate(settings.generateCertificate()); - } catch (CertificateException e) { - } catch (IOException e) { - } - wifiConfig.enterpriseConfig = enterpriseConfig; - break; - } - int networkId = wifiManager.addNetwork(wifiConfig); - wifiManager.enableNetwork(networkId, true); - } - - @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - loadDhbwWifiDefinitions(); + + wifiPresets = WifiNetworkSettings.LoadNetworkSettingPresets(AppContext); + wifiManager = (WifiManager)AppContext.getMainActivity().getSystemService(AppContext.getMainActivity().WIFI_SERVICE); connectivityManager = (ConnectivityManager) AppContext.getMainActivity().getSystemService(Activity.CONNECTIVITY_SERVICE); + + wifiConfigManager = new WifiConfigurationManager(wifiManager); } private void updateSelectedNetworkInformation() { @@ -277,7 +146,7 @@ public class WifiSettings extends CampusAppFragment { if(selectedNetworkIndex >= wifiPresets.length) return; - WifiNetworkSettingsSet settings = wifiPresets[selectedNetworkIndex]; + WifiNetworkSettings settings = wifiPresets[selectedNetworkIndex]; TextView ssidInfoView = (TextView) view.findViewById(R.id.ssidInfo); TextView securityInfoView = (TextView) view.findViewById(R.id.securityInfo); TextView eapInfoView = (TextView) view.findViewById(R.id.eapInfo); @@ -292,7 +161,7 @@ public class WifiSettings extends CampusAppFragment { RelativeLayout insecureNetWarning = (RelativeLayout) view.findViewById(R.id.insecureNetWarning); - ssidInfoView.setText(settings.ssid); + ssidInfoView.setText(settings.getSSID()); eapInfoRow.setVisibility(View.GONE); phase2InfoRow.setVisibility(View.GONE); @@ -300,7 +169,7 @@ public class WifiSettings extends CampusAppFragment { pskInfoRow.setVisibility(View.GONE); insecureNetWarning.setVisibility(View.GONE); - switch(settings.authType) { + switch(settings.getAuthType()) { case WIFI_AUTHTYPE_NONE: securityInfoView.setText(R.string.wifiopts_security_open); @@ -325,9 +194,9 @@ public class WifiSettings extends CampusAppFragment { break; } - if(settings.authType == WifiNetworkAuthenticationTypes.WIFI_AUTHTYPE_WPA_ENTERPRISE) { + if(settings.getAuthType() == WifiNetworkSettings.WifiNetworkAuthenticationTypes.WIFI_AUTHTYPE_WPA_ENTERPRISE) { int eapStringId = 0; - switch(settings.eapMethod) { + switch(settings.getEapMethod()) { case WifiEnterpriseConfig.Eap.PEAP: eapStringId = R.string.wifiopts_eap_peap; break; @@ -347,7 +216,7 @@ public class WifiSettings extends CampusAppFragment { eapInfoView.setText(""); int phase2StringId = 0; - switch(settings.phase2Method) { + switch(settings.getPhase2Method()) { case WifiEnterpriseConfig.Phase2.MSCHAPV2: phase2StringId = R.string.wifiopts_phase2_mschapv2; break; @@ -360,7 +229,7 @@ public class WifiSettings extends CampusAppFragment { else phase2InfoView.setText(""); - cacertInfoView.setText(settings.caName); + cacertInfoView.setText(settings.getCAName()); } else { } @@ -370,15 +239,62 @@ public class WifiSettings extends CampusAppFragment { private void updateWifiState() { NetworkInfo myNetworkInfo = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI); WifiInfo myWifiInfo = wifiManager.getConnectionInfo(); + String ssid = (myWifiInfo != null ? myWifiInfo.getSSID() : null); + if(ssid == null) + return; + + WifiNetworkSettings settings = null; + for(int i = 0; i < wifiPresets.length; i++) { + if(wifiPresets[i].getSSID().equalsIgnoreCase(ssid)) { + settings = wifiPresets[i]; + break; + } + } + if(settings == null) + return; + + if(settings.isTryConnecting) { + boolean success = false; + if(settings.isDisconnected) { + settings.failedAuthentication = true; + settings.isTryConnecting = false; + } else if(settings.isAssociated) { + settings.isTryConnecting = false; + success = true; + } + if(!settings.isTryConnecting) { + // success + // TODO: continue + } + } + Resources resources = AppContext.getMainActivity().getResources(); String status; if(myNetworkInfo.isConnectedOrConnecting()) { int statusId = (myNetworkInfo.isConnected() ? R.string.wifisettings_status_connected : R.string.wifisettings_status_connecting); - String ssid = myWifiInfo.getSSID(); + ssid = ssid.replace("\"", ""); status = String.format(resources.getString(statusId), ssid); + + + + + //if(networkConnecting && wifiPresets[connectingNetworkIndex].getSSID().equalsIgnoreCase(ssid)) { + + /* + + wifiConfigManager.finalizeConnection(wifiPresets[connectingNetworkIndex], myNetworkInfo, myWifiInfo, new WifiConfigurationManager.WifiConfigurationComplete() { + @Override + public void onWifiConfigurationComplete(WifiNetworkSettings settings, String response) { + networkConnecting = false; + connectingNetworkIndex = 0; + updateWifiConnectButton(); + } + }); + */ + //} } else { status = resources.getString(R.string.wifisettings_status_disconnected); } @@ -394,7 +310,7 @@ public class WifiSettings extends CampusAppFragment { for(int i = 0; i < wifiPresets.length; i++) { boolean networkFound = false; for(ScanResult result : scanResultList) { - if(result.SSID != null && result.SSID.equalsIgnoreCase(wifiPresets[i].ssid)) { + if(result.SSID != null && result.SSID.equalsIgnoreCase(wifiPresets[i].getSSID())) { networkFound = true; break; } @@ -407,7 +323,7 @@ public class WifiSettings extends CampusAppFragment { private void updateWifiConnectButton() { if(selectedNetworkIndex >= wifiPresets.length) return; - WifiNetworkSettingsSet preset = wifiPresets[selectedNetworkIndex]; + WifiNetworkSettings preset = wifiPresets[selectedNetworkIndex]; boolean buttonEnabled = false; int buttonTextId = 0; @@ -416,8 +332,10 @@ public class WifiSettings extends CampusAppFragment { if(!wifiManager.isWifiEnabled()) { buttonTextId = R.string.wifisettings_connect_button_disabled; - } else if(networkInfo.isConnectedOrConnecting() && wifiInfo.getSSID() != null && wifiInfo.getSSID().equalsIgnoreCase(preset.ssid)) { + } else if(networkInfo.isConnectedOrConnecting() && wifiInfo.getSSID() != null && wifiInfo.getSSID().equalsIgnoreCase(preset.getSSID())) { buttonTextId = R.string.wifisettings_connect_button_ready; + } else if(preset.isTryConnecting) { + buttonTextId = R.string.wifisettings_connect_button_connecting; } else if(!networkScanned) { buttonTextId = R.string.wifisettings_connect_button_scan; } else if(!preset.isInScanResult) { @@ -445,7 +363,7 @@ public class WifiSettings extends CampusAppFragment { tablayout.removeAllTabs(); for(int i = 0; i < wifiPresets.length; i++) { TabLayout.Tab tab = tablayout.newTab(); - tab.setText(wifiPresets[i].name); + tab.setText(wifiPresets[i].getName()); tablayout.addTab(tab); } tablayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() { @@ -468,9 +386,7 @@ public class WifiSettings extends CampusAppFragment { connectBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - Button connectBtn = (Button) v; - connectBtn.setEnabled(false); - WifiNetworkSettingsSet settings = wifiPresets[selectedNetworkIndex]; + WifiNetworkSettings settings = wifiPresets[selectedNetworkIndex]; if(!settings.isInScanResult) { networkScanned = false; wifiManager.startScan(); @@ -478,24 +394,24 @@ public class WifiSettings extends CampusAppFragment { return; } + + EditText usernameEdt = (EditText) view.findViewById(R.id.wifiUsername); EditText passwordEdt = (EditText) view.findViewById(R.id.wifiPassword); String username = usernameEdt.getText().toString(); String password = passwordEdt.getText().toString(); - if (Build.VERSION.SDK_INT < 18) { - // connectToDHWiFi not supported! - // do something else? - } else if (username.length() > 0 && password.length() > 0) { - + if (username.length() > 0 && password.length() > 0) { if(settings != null) { + settings.isTryConnecting = true; settings.setAuthData(username, password); - connectToWiFi(settings); + + wifiConfigManager.connect(settings); } - } - connectBtn.setEnabled(true); + updateWifiConnectButton(); + } } }); @@ -512,7 +428,10 @@ public class WifiSettings extends CampusAppFragment { if(AppContext == null) AppContext = CampusAppContext.getInstance(); - AppContext.getMainActivity().registerReceiver(this.wifiStateReceiver, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)); + IntentFilter filter = new IntentFilter(); + filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); + filter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION); + AppContext.getMainActivity().registerReceiver(this.wifiStateReceiver, new IntentFilter(filter)); AppContext.getMainActivity().registerReceiver(this.wifiScanReceiver, new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)); networkScanned = false; diff --git a/app/src/main/java/de/dhbwloe/campusapp/vorlesungen/CalendarManager.java b/app/src/main/java/de/dhbwloe/campusapp/vorlesungen/CalendarManager.java index fae9148..e7b03cb 100644 --- a/app/src/main/java/de/dhbwloe/campusapp/vorlesungen/CalendarManager.java +++ b/app/src/main/java/de/dhbwloe/campusapp/vorlesungen/CalendarManager.java @@ -83,7 +83,7 @@ public class CalendarManager extends IscRequestHelper { timeFrom = now - (86400 * 5); timeTo = now + (86400 * 7 * 4); } else { - timeFrom = now - (86400 * 365 * 3); + timeFrom = now - (86400 * 365 * 4); timeTo = now + (86400 * 365 * 4); } CourseEvent[] events = AppContext.getDatabaseManager().getCourseCalendarEvents(sCourseName, timeFrom, timeTo); @@ -213,7 +213,7 @@ public class CalendarManager extends IscRequestHelper { } } - dbEvent.update(AppContext.getDatabaseManager()); + dbEvent.update(AppContext.getDatabaseManager(), event); Log.i("CMSync", "Update Event: "+dbEvent.getUniqueId()); } diff --git a/app/src/main/java/de/dhbwloe/campusapp/vorlesungen/CourseEvent.java b/app/src/main/java/de/dhbwloe/campusapp/vorlesungen/CourseEvent.java index 71a49ca..0e5a1cb 100644 --- a/app/src/main/java/de/dhbwloe/campusapp/vorlesungen/CourseEvent.java +++ b/app/src/main/java/de/dhbwloe/campusapp/vorlesungen/CourseEvent.java @@ -1,24 +1,38 @@ package de.dhbwloe.campusapp.vorlesungen; +import net.fortuna.ical4j.model.Component; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + import de.dhbwloe.campusapp.database.DatabaseManager; /** * Created by pk910 on 20.01.2016. */ -public class CourseEvent { +public class CourseEvent implements Comparable { + public enum CourseType { + COURSETYPE_NORMAL, + COURSETYPE_SPECIAL, + COURSETYPE_KLAUSUR, + } + private int iEventId; private String sCourseName; private String sUniqueId; private int iSequenceId; private long iEventFrom, iEventTo; - private String sEventTitle, sEventLocation, sEventStatus; + private String sEventTitle, sEventTitleShort, sEventTitleAuthor, sEventLocation, sEventStatus; private String sRecurRule, sExcludeDates; private CourseGroup oCourseGroup; + private CourseType oCourseType = null; private boolean bMustUpdate = false; private boolean bIsNew = false; - public CourseEvent(int id, String coursename, String uniqueid, int sequenceid, long eventfrom, long eventto, String title, String location, String status, String rrule, String exdates, CourseGroup group) { + private boolean bIsKlausurPraesi = false; + + public CourseEvent(int id, String coursename, String uniqueid, int sequenceid, long eventfrom, long eventto, String title, String location, String status, String rrule, String exdates, CourseGroup group, int eventtype) { iEventId = id; sCourseName = coursename; sUniqueId = uniqueid; @@ -32,8 +46,14 @@ public class CourseEvent { sExcludeDates = exdates; oCourseGroup = group; - if(group != null) + if(group != null) { group.addCourseEvent(this); + try { + oCourseType = CourseType.values()[eventtype]; + } catch(Exception e) {} + } + + ParseEventTitle(); } public CourseEvent(String coursename, String uniqueid, int sequenceid, boolean isNew) { @@ -44,6 +64,37 @@ public class CourseEvent { bMustUpdate = isNew; } + public void ParseEventTitle() { + Pattern pattern = Pattern.compile("^((Klausur|Tutorium|Pr(ä|ae)sentation)[ :]+)?(.*?)( - ([a-zA-Z., -]+))?$"); + Matcher m = pattern.matcher(sEventTitle); + CourseType oldtype = oCourseType; + boolean typeisset = (oCourseType != null); + if (m.matches()) { + String eventType = m.group(2); + if (!typeisset && eventType != null && !eventType.isEmpty()) { + if(eventType.equalsIgnoreCase("Klausur")) + oCourseType = CourseType.COURSETYPE_KLAUSUR; + else if(eventType.equalsIgnoreCase("Präsentation") || eventType.equalsIgnoreCase("Praesentation")) { + oCourseType = CourseType.COURSETYPE_KLAUSUR; + bIsKlausurPraesi = true; + } else if(eventType.equalsIgnoreCase("Tutorium")) + oCourseType = CourseType.COURSETYPE_SPECIAL; + + typeisset = true; + } + sEventTitleShort = m.group(4); + sEventTitleAuthor = m.group(6); + } else { + sEventTitleShort = sEventTitle; + sEventTitleAuthor = ""; + } + if(!typeisset) + oCourseType = CourseType.COURSETYPE_NORMAL; + + if(oldtype != oCourseType) + bMustUpdate = true; + } + public void setEventId(int id) { iEventId = id; } @@ -57,11 +108,11 @@ public class CourseEvent { bMustUpdate = false; } - public void update(DatabaseManager dbm) { + public void update(DatabaseManager dbm, Component event) { if(!bMustUpdate) return; - dbm.updateCourseCalendar(this); + dbm.updateCourseCalendar(this, event); resetUpdateFlag(); } @@ -69,6 +120,22 @@ public class CourseEvent { return oCourseGroup; } + public void setCourseType(CourseType type) { + oCourseType = type; + } + + public CourseType getCourseType() { + return oCourseType; + } + + public int getCourseTypeId() { + for(int i = 0; i < CourseType.values().length; i++) { + if(oCourseType == CourseType.values()[i]) + return i; + } + return 0; + } + public void setCourseGroup(CourseGroup group) { group.addCourseEvent(this); oCourseGroup = group; @@ -108,11 +175,18 @@ public class CourseEvent { return sEventTitle; } public String getGroupTitle() { - return sEventTitle; // maybe cut prof name? + return sEventTitleShort + (sEventTitleAuthor == null ? "" : " - " + sEventTitleAuthor); + } + public String getEventShortTitle() { + return sEventTitleShort; + } + public String getEventDozent() { + return sEventTitleAuthor; } public void setEventTitle(String sEventTitle) { this.sEventTitle = sEventTitle; + ParseEventTitle(); bMustUpdate = true; } @@ -164,4 +238,13 @@ public class CourseEvent { this.sExcludeDates = exrules; bMustUpdate = true; } + + public boolean getIsKlausurPraesi() { + return bIsKlausurPraesi; + } + + @Override + public int compareTo(CourseEvent another) { + return (int)(another.getEventFrom() - iEventFrom); + } } diff --git a/app/src/main/java/de/dhbwloe/campusapp/vorlesungen/CourseGroup.java b/app/src/main/java/de/dhbwloe/campusapp/vorlesungen/CourseGroup.java index 06475c1..181f61b 100644 --- a/app/src/main/java/de/dhbwloe/campusapp/vorlesungen/CourseGroup.java +++ b/app/src/main/java/de/dhbwloe/campusapp/vorlesungen/CourseGroup.java @@ -1,8 +1,13 @@ package de.dhbwloe.campusapp.vorlesungen; +import android.os.Bundle; import android.provider.ContactsContract; +import java.lang.reflect.Array; import java.util.ArrayList; +import java.util.Arrays; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import de.dhbwloe.campusapp.database.DatabaseManager; @@ -17,6 +22,8 @@ public class CourseGroup { private boolean bIsNew = false; private ArrayList events = new ArrayList(); + private Bundle extraData = new Bundle(); + public static CourseGroup GetCourseGroupById(DatabaseManager dbm, int id) { for(CourseGroup group : CourseGroups) { if(group.iCourseGroupId == id) @@ -61,6 +68,14 @@ public class CourseGroup { return (CourseEvent[])this.events.toArray(); } + public CourseEvent[] getOrderedCourseEvents() { + CourseEvent[] events = getCourseEvents(); + + Arrays.sort(events); + + return events; + } + public int getGroupId() { return iCourseGroupId; } @@ -72,4 +87,12 @@ public class CourseGroup { return ret; } + public Bundle getExtraData() { + return extraData; + } + + public String getGroupName() { + return sCourseGroupName; + } + } diff --git a/app/src/main/java/de/dhbwloe/campusapp/wifi/SecureLoginManager.java b/app/src/main/java/de/dhbwloe/campusapp/wifi/SecureLoginManager.java new file mode 100644 index 0000000..d98c626 --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/wifi/SecureLoginManager.java @@ -0,0 +1,220 @@ +package de.dhbwloe.campusapp.wifi; + +import android.app.DownloadManager; +import android.net.NetworkInfo; +import android.net.SSLCertificateSocketFactory; +import android.net.SSLSessionCache; +import android.net.wifi.WifiInfo; +import android.os.AsyncTask; +import android.util.Log; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.UnsupportedEncodingException; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLEncoder; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.KeyManager; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; + +import cz.msebera.android.httpclient.HttpVersion; +import cz.msebera.android.httpclient.NameValuePair; +import cz.msebera.android.httpclient.client.HttpClient; +import cz.msebera.android.httpclient.client.methods.HttpPost; +import cz.msebera.android.httpclient.conn.ClientConnectionManager; +import cz.msebera.android.httpclient.conn.scheme.PlainSocketFactory; +import cz.msebera.android.httpclient.conn.scheme.Scheme; +import cz.msebera.android.httpclient.conn.scheme.SchemeRegistry; +import cz.msebera.android.httpclient.impl.client.DefaultHttpClient; +import cz.msebera.android.httpclient.impl.conn.tsccm.ThreadSafeClientConnManager; +import cz.msebera.android.httpclient.message.BasicNameValuePair; +import cz.msebera.android.httpclient.params.BasicHttpParams; +import cz.msebera.android.httpclient.params.HttpParams; +import cz.msebera.android.httpclient.params.HttpProtocolParams; +import cz.msebera.android.httpclient.protocol.HTTP; +import de.dhbwloe.campusapp.CampusAppContext; + +/** + * Created by pk910 on 08.02.2016. + */ +public class SecureLoginManager extends AsyncTask { + private static final String SECURELOGIN_URL = "https://securelogin1.dhbw-loerrach.de/cgi-bin/login"; + private static final int SECURELOGIN_TIMEOUT = 20000; + private static final int SECURELOGIN_HANDSHAKE_TIMEOUT = 10000; + private static String SECURELOGIN_FAILED_REGEX = "Authentication failed"; + private static String SECURELOGIN_SUCCESS_REGEX = "User Authenticated"; + + public interface SecureLoginResult { + void onSecureLoginFailed(String errMsg); + void onSecureLoginSuccess(); + } + + private CampusAppContext AppContext; + private SecureLoginTask task; + private String responseString; + + @Override + protected SecureLoginTask doInBackground(SecureLoginTask... params) { + task = params[0]; + if (AppContext == null) + AppContext = CampusAppContext.getInstance(); + + task.responseReceived = false; + task.responseString = null; + + //build login post request: + //https://securelogin1.dhbw-loerrach.de/cgi-bin/login + // user=***censored*** + // password=***censored*** + // cmd=authenticate + // Login=Log+In + + /* Dear DHBW Tech Guys + * Go and replace these fucking outdated and insecure SSL implementation for your "secure"login page! + * 3DES_EDE_CBC + * + * Ok, after ~3 days android ssl library analysis, i've finnaly made it communicating with an 3DES Endpoint. + * Dear DHBW Tech Guys, gu fuck yourself! If you don't know why, continue (^.^) + */ + + //SSLSocket socket = null; + StringBuilder responseString = new StringBuilder(); + try { + SSLContext sslContext = SSLContext.getInstance("TLS"); + SecureLoginTrustManager loginTrustManager = new SecureLoginTrustManager(); + sslContext.init(null, new TrustManager[]{loginTrustManager}, null); + SSLSocketFactory innerSslSocketPactory = SSLCertificateSocketFactory.getInsecure(SECURELOGIN_HANDSHAKE_TIMEOUT, new SSLSessionCache(AppContext.getMainActivity())); + SSLSocketFactory sslSocketFactory = new SecureLoginSocketFactory(sslContext, innerSslSocketPactory); + + //SSLSocket socket = (SSLSocket) sslSocketFactory.createSocket("securelogin1.dhbw-loerrach.de", 443); + //socket.startHandshake(); + + Log.i("SecureLogin", "HTTPS INITIALIZED"); + + URL url = new URL(SECURELOGIN_URL); + URLConnection conn = url.openConnection(); + if (conn instanceof HttpsURLConnection) { + ((HttpsURLConnection)conn).setSSLSocketFactory(sslSocketFactory); + ((HttpsURLConnection )conn).setRequestMethod("POST"); + ((HttpsURLConnection)conn).setHostnameVerifier(org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); + } else { + ((HttpURLConnection) conn).setRequestMethod("POST"); + } + conn.setConnectTimeout(SECURELOGIN_TIMEOUT); + conn.setReadTimeout(SECURELOGIN_TIMEOUT); + + conn.setDoInput(true); + conn.setDoOutput(true); + + List postData = new ArrayList(); + postData.add(new BasicNameValuePair("cmd", "authenticate")); + postData.add(new BasicNameValuePair("Login", "Log+In")); + postData.add(new BasicNameValuePair("user", task.settings.username)); + postData.add(new BasicNameValuePair("password", task.settings.password)); + + OutputStream os = conn.getOutputStream(); + BufferedWriter writer = new BufferedWriter( + new OutputStreamWriter(os, "UTF-8")); + String postString = encodePostData(postData); + writer.write(postString); + writer.flush(); + writer.close(); + os.close(); + + conn.connect(); + + int responseCode; + if (conn instanceof HttpsURLConnection) + responseCode = ((HttpsURLConnection)conn).getResponseCode(); + else + responseCode = ((HttpURLConnection)conn).getResponseCode(); + + if (responseCode == HttpsURLConnection.HTTP_OK) { + task.responseReceived = true; + String line; + BufferedReader br=new BufferedReader(new InputStreamReader(conn.getInputStream())); + while ((line=br.readLine()) != null) { + responseString.append(line); + } + } else { + task.responseError = "HTTP " + Integer.toString(responseCode); + } + + } catch (Exception e) { + task.responseError = e.getMessage(); + } + + task.responseString = responseString.toString(); + + return task; + } + + @Override + protected void onPreExecute() { + super.onPreExecute(); + + } + + @Override + protected void onPostExecute(SecureLoginTask task) { + super.onPostExecute(task); + + boolean success = false; + String error = null; + + if(task.responseReceived) { + Pattern p1 = Pattern.compile(SECURELOGIN_SUCCESS_REGEX); + Pattern p2 = Pattern.compile(SECURELOGIN_FAILED_REGEX); + + Matcher m1 = p1.matcher(task.responseString); + Matcher m2 = p2.matcher(task.responseString); + + if(m1.find()) + success = true; + else if(m2.find()) + error = "Incorrect username or password code entered. Please try again."; + else + error = "An unknown error occured!"; + } else if(task.responseError != null) { + error = task.responseError; + } + if(success) + task.loginCallback.onSecureLoginSuccess(); + else + task.loginCallback.onSecureLoginFailed(error); + } + + private String encodePostData(List params) throws UnsupportedEncodingException { + StringBuilder result = new StringBuilder(); + boolean first = true; + + for (NameValuePair pair : params) { + if (first) + first = false; + else + result.append("&"); + + result.append(URLEncoder.encode(pair.getName(), "UTF-8")); + result.append("="); + result.append(URLEncoder.encode(pair.getValue(), "UTF-8")); + } + + return result.toString(); + } +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/wifi/SecureLoginSocketFactory.java b/app/src/main/java/de/dhbwloe/campusapp/wifi/SecureLoginSocketFactory.java new file mode 100644 index 0000000..74f06f9 --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/wifi/SecureLoginSocketFactory.java @@ -0,0 +1,95 @@ +package de.dhbwloe.campusapp.wifi; + +import android.util.Log; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; +import java.net.UnknownHostException; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; + +public class SecureLoginSocketFactory extends SSLSocketFactory { + private static final String ENABLED_PROTOCOLS[] = { + "TLSv1", "TLSv1.1", "TLSv1.2" + }; + private static final String ENABLED_CIPHERS[] = { + "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", + "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", + "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", + "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", + "TLS_DHE_RSA_WITH_AES_128_CBC_SHA", + "TLS_DHE_RSA_WITH_AES_256_CBC_SHA", + "TLS_DHE_DSS_WITH_AES_128_CBC_SHA", + "TLS_ECDHE_RSA_WITH_RC4_128_SHA", + "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA", + "TLS_RSA_WITH_AES_128_CBC_SHA", + "TLS_RSA_WITH_AES_256_CBC_SHA", + "SSL_RSA_WITH_3DES_EDE_CBC_SHA", + "SSL_RSA_WITH_RC4_128_SHA", + "SSL_RSA_WITH_RC4_128_MD5", + }; + + private SSLSocketFactory socketFactory; + public SSLContext context; + + public SecureLoginSocketFactory(SSLContext context, SSLSocketFactory innerSslSocketFactory) { + super(); + this.context = context; + this.socketFactory = innerSslSocketFactory; + } + + @Override + public String[] getDefaultCipherSuites() { + return socketFactory.getDefaultCipherSuites(); + } + + @Override + public String[] getSupportedCipherSuites() { + return socketFactory.getSupportedCipherSuites(); + } + + @Override + public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException { + return enableTLSOnSocket(socketFactory.createSocket(s, host, port, autoClose)); + } + + @Override + public Socket createSocket(String host, int port) throws IOException, UnknownHostException { + return enableTLSOnSocket(socketFactory.createSocket(host, port)); + } + + @Override + public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException { + return enableTLSOnSocket(socketFactory.createSocket(host, port, localHost, localPort)); + } + + @Override + public Socket createSocket(InetAddress host, int port) throws IOException { + return enableTLSOnSocket(socketFactory.createSocket(host, port)); + } + + @Override + public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException { + return enableTLSOnSocket(socketFactory.createSocket(address, port, localAddress, localPort)); + } + + private Socket enableTLSOnSocket(Socket socket) { + if(socket != null && (socket instanceof SSLSocket)) { + SSLSocket sslSocket = ((SSLSocket) socket); + + sslSocket.setEnabledCipherSuites(ENABLED_CIPHERS); + sslSocket.setEnabledProtocols(ENABLED_PROTOCOLS); + } + return socket; + } + +} \ No newline at end of file diff --git a/app/src/main/java/de/dhbwloe/campusapp/wifi/SecureLoginTask.java b/app/src/main/java/de/dhbwloe/campusapp/wifi/SecureLoginTask.java new file mode 100644 index 0000000..2e7f7a5 --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/wifi/SecureLoginTask.java @@ -0,0 +1,20 @@ +package de.dhbwloe.campusapp.wifi; + +import android.net.NetworkInfo; +import android.net.wifi.WifiInfo; +import android.net.wifi.WifiManager; + +/** + * Created by pk910 on 08.02.2016. + */ +public class SecureLoginTask { + public WifiNetworkSettings settings; + public NetworkInfo networkInfo; + public WifiInfo wifiInfo; + + public boolean responseReceived; + public String responseString; + public String responseError; + + public SecureLoginManager.SecureLoginResult loginCallback; +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/wifi/SecureLoginTrustManager.java b/app/src/main/java/de/dhbwloe/campusapp/wifi/SecureLoginTrustManager.java new file mode 100644 index 0000000..9c0b545 --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/wifi/SecureLoginTrustManager.java @@ -0,0 +1,31 @@ +package de.dhbwloe.campusapp.wifi; + +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +/** + * Created by pk910 on 12.02.2016. + */ +public class SecureLoginTrustManager implements javax.net.ssl.X509TrustManager { + private static final X509Certificate[] _AcceptedIssuers = new X509Certificate[] {}; + + public void checkClientTrusted(X509Certificate[] arg0, String arg1) + throws CertificateException { + } + + public void checkServerTrusted(X509Certificate[] arg0, String arg1) + throws CertificateException { + } + + public boolean isClientTrusted(X509Certificate[] chain) { + return (true); + } + + public boolean isServerTrusted(X509Certificate[] chain) { + return (true); + } + + public X509Certificate[] getAcceptedIssuers() { + return (_AcceptedIssuers); + } +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/wifi/WifiConfigurationManager.java b/app/src/main/java/de/dhbwloe/campusapp/wifi/WifiConfigurationManager.java new file mode 100644 index 0000000..a58f128 --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/wifi/WifiConfigurationManager.java @@ -0,0 +1,72 @@ +package de.dhbwloe.campusapp.wifi; + +import android.annotation.TargetApi; +import android.net.NetworkInfo; +import android.net.wifi.WifiConfiguration; +import android.net.wifi.WifiEnterpriseConfig; +import android.net.wifi.WifiInfo; +import android.net.wifi.WifiManager; +import android.os.Build; + +import java.io.IOException; +import java.security.cert.CertificateException; + +/** + * Created by pk910 on 08.02.2016. + */ +public class WifiConfigurationManager { + public interface WifiConfigurationComplete { + public void onWifiConfigurationComplete(WifiNetworkSettings settings, String response); + } + + private WifiManager wifiManager; + + public WifiConfigurationManager(WifiManager wifiManager) { + this.wifiManager = wifiManager; + } + + public void connect(WifiNetworkSettings settings) { + if (Build.VERSION.SDK_INT >= 18) { + connectToWiFi(settings); + } + } + + + @TargetApi(18) + private void connectToWiFi(WifiNetworkSettings settings) { + WifiConfiguration wifiConfig = new WifiConfiguration(); + wifiConfig.SSID = settings.ssid; + for(int i = 0; i < settings.authAlgorithms.length; i++) + wifiConfig.allowedKeyManagement.set(settings.authAlgorithms[i]); + + switch(settings.authType) { + case WIFI_AUTHTYPE_NONE: + break; + case WIFI_AUTHTYPE_WEP: + // not supported by our app + break; + case WIFI_AUTHTYPE_WPA: + + break; + case WIFI_AUTHTYPE_WPA_ENTERPRISE: + WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig(); + String username = settings.username; + if(settings.usernameSuffix != null) + username += settings.usernameSuffix; + enterpriseConfig.setIdentity(username); + enterpriseConfig.setPassword(settings.password); + enterpriseConfig.setEapMethod(settings.eapMethod); + enterpriseConfig.setPhase2Method(settings.phase2Method); + try { + enterpriseConfig.setCaCertificate(settings.generateCertificate()); + } catch (CertificateException e) { + } catch (IOException e) { + } + wifiConfig.enterpriseConfig = enterpriseConfig; + break; + } + int networkId = wifiManager.addNetwork(wifiConfig); + wifiManager.enableNetwork(networkId, true); + } + +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/wifi/WifiNetworkSettings.java b/app/src/main/java/de/dhbwloe/campusapp/wifi/WifiNetworkSettings.java new file mode 100644 index 0000000..3c1ba68 --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/wifi/WifiNetworkSettings.java @@ -0,0 +1,264 @@ +package de.dhbwloe.campusapp.wifi; + +import android.net.wifi.WifiConfiguration; +import android.net.wifi.WifiEnterpriseConfig; +import android.support.design.widget.TabLayout; +import android.util.Base64; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.util.ArrayList; + +import de.dhbwloe.campusapp.CampusAppContext; + +/** + * Created by pk910 on 08.02.2016. + */ +public class WifiNetworkSettings { + public static WifiNetworkSettings[] LoadNetworkSettingPresets(CampusAppContext context) { + ArrayList presets = new ArrayList(); + + presets.add((new WifiNetworkSettings("dhbw-secure", "dhbw-secure", WifiNetworkAuthenticationTypes.WIFI_AUTHTYPE_WPA_ENTERPRISE)). + setWpaEnterprise( + new int[]{WifiConfiguration.KeyMgmt.WPA_EAP, WifiConfiguration.KeyMgmt.IEEE8021X}, + WifiEnterpriseConfig.Eap.PEAP, + WifiEnterpriseConfig.Phase2.MSCHAPV2, + null + ).setCACertificate("Deutsche Telekom Root CA 2", "-----BEGIN CERTIFICATE-----\n" + + "MIIDnzCCAoegAwIBAgIBJjANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJERTEc\n" + + "MBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxlU2Vj\n" + + "IFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290IENB\n" + + "IDIwHhcNOTkwNzA5MTIxMTAwWhcNMTkwNzA5MjM1OTAwWjBxMQswCQYDVQQGEwJE\n" + + "RTEcMBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxl\n" + + "U2VjIFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290\n" + + "IENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrC6M14IspFLEU\n" + + "ha88EOQ5bzVdSq7d6mGNlUn0b2SjGmBmpKlAIoTZ1KXleJMOaAGtuU1cOs7TuKhC\n" + + "QN/Po7qCWWqSG6wcmtoIKyUn+WkjR/Hg6yx6m/UTAtB+NHzCnjwAWav12gz1Mjwr\n" + + "rFDa1sPeg5TKqAyZMg4ISFZbavva4VhYAUlfckE8FQYBjl2tqriTtM2e66foai1S\n" + + "NNs671x1Udrb8zH57nGYMsRUFUQM+ZtV7a3fGAigo4aKSe5TBY8ZTNXeWHmb0moc\n" + + "QqvF1afPaA+W5OFhmHZhyJF81j4A4pFQh+GdCuatl9Idxjp9y7zaAzTVjlsB9WoH\n" + + "txa2bkp/AgMBAAGjQjBAMB0GA1UdDgQWBBQxw3kbuvVT1xfgiXotF2wKsyudMzAP\n" + + "BgNVHRMECDAGAQH/AgEFMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOC\n" + + "AQEAlGRZrTlk5ynrE/5aw4sTV8gEJPB0d8Bg42f76Ymmg7+Wgnxu1MM9756Abrsp\n" + + "tJh6sTtU6zkXR34ajgv8HzFZMQSyzhfzLMdiNlXiItiJVbSYSKpk+tYcNthEeFpa\n" + + "IzpXl/V6ME+un2pMSyuOoAPjPuCp1NJ70rOo4nI8rZ7/gFnkm0W09juwzTkZmDLl\n" + + "6iFhkOQxIY40sfcvNUqFENrnijchvllj4PKFiDFT1FQUhXB59C4Gdyd1Lx+4ivn+\n" + + "xbrYNuSD7Odlt79jWvNGr4GUN9RBjNYj1h7P9WgbRGOiWrqnNVmh5XAFmw4jV5mU\n" + + "Cm26OWMohpLzGITY+9HPBVZkVw==\n" + + "-----END CERTIFICATE-----")); + + presets.add((new WifiNetworkSettings("dhbw-wlan", "dhbw-wlan", WifiNetworkAuthenticationTypes.WIFI_AUTHTYPE_NONE)). + setDHSecureLogin(new SecureLoginManager()).setCACertificate("DHBW CA - G01", "-----BEGIN CERTIFICATE-----\n" + + "MIIFSjCCBDKgAwIBAgIHF6/20DYr2jANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQG\n" + + "EwJERTETMBEGA1UEChMKREZOLVZlcmVpbjEQMA4GA1UECxMHREZOLVBLSTEkMCIG\n" + + "A1UEAxMbREZOLVZlcmVpbiBQQ0EgR2xvYmFsIC0gRzAxMB4XDTE0MDYwNTE0MDU1\n" + + "MloXDTE5MDcwOTIzNTkwMFowcjELMAkGA1UEBhMCREUxLDAqBgNVBAoTI0R1YWxl\n" + + "IEhvY2hzY2h1bGUgQmFkZW4tV3VlcnR0ZW1iZXJnMRYwFAYDVQQDEw1ESEJXIENB\n" + + "IC0gRzAxMR0wGwYJKoZIhvcNAQkBFg5wa2lAZGhidy12cy5kZTCCASIwDQYJKoZI\n" + + "hvcNAQEBBQADggEPADCCAQoCggEBALtoqHrbNRjuRdIh20VPATXIitkVgIva+HcA\n" + + "Q7qA87lsm6v28DSasCPgx+TnGoUduGucA7fA7UhtWGqmU5cbJ5Ucsra0XBwqbOEN\n" + + "u7GIBVcK7vUac67Ny+9TEqB/mtKz4nU/G8TlpX5hotMDg4H46SvtIvtSz1RJ2BVs\n" + + "ssssubz3zI/W5Y6qqfRhZGLzrqmZYYlfCc/sn0jOkElgomAzzt4rmKRq8CsX3bYJ\n" + + "ImeUaEHXU5QgTEZLgA9olse54r6N2O+cM4y/eDK9q9RaWdjNKTDsRq/sILlgUIaO\n" + + "XcxzBa3pOhQ7zRb5amGiTIxWb2Szzy+Ka1XO9BseTFCcot8BAO8CAwEAAaOCAfsw\n" + + "ggH3MBIGA1UdEwEB/wQIMAYBAf8CAQEwDgYDVR0PAQH/BAQDAgEGMBEGA1UdIAQK\n" + + "MAgwBgYEVR0gADAdBgNVHQ4EFgQUjOlwcWC4U59ExpdeQh/tKHK9Dn4wHwYDVR0j\n" + + "BBgwFoAUSbfGz+g9H3/qRHsTKffxCnA+3mQwGQYDVR0RBBIwEIEOcGtpQGRoYnct\n" + + "dnMuZGUwgYgGA1UdHwSBgDB+MD2gO6A5hjdodHRwOi8vY2RwMS5wY2EuZGZuLmRl\n" + + "L2dsb2JhbC1yb290LWNhL3B1Yi9jcmwvY2FjcmwuY3JsMD2gO6A5hjdodHRwOi8v\n" + + "Y2RwMi5wY2EuZGZuLmRlL2dsb2JhbC1yb290LWNhL3B1Yi9jcmwvY2FjcmwuY3Js\n" + + "MIHXBggrBgEFBQcBAQSByjCBxzAzBggrBgEFBQcwAYYnaHR0cDovL29jc3AucGNh\n" + + "LmRmbi5kZS9PQ1NQLVNlcnZlci9PQ1NQMEcGCCsGAQUFBzAChjtodHRwOi8vY2Rw\n" + + "MS5wY2EuZGZuLmRlL2dsb2JhbC1yb290LWNhL3B1Yi9jYWNlcnQvY2FjZXJ0LmNy\n" + + "dDBHBggrBgEFBQcwAoY7aHR0cDovL2NkcDIucGNhLmRmbi5kZS9nbG9iYWwtcm9v\n" + + "dC1jYS9wdWIvY2FjZXJ0L2NhY2VydC5jcnQwDQYJKoZIhvcNAQELBQADggEBAMS9\n" + + "5ypdDi/q3AeBOfzj3A3PZaQaLumKxvVeA2xy8j8XtwWpPeG99QwtqeKlJ3+OCuwT\n" + + "eRtkWxnsm0eonLYI2rSL8y94LUoM0kj4GiHbRqL4eqPJKCwoQeTWxkeW/2fmZPtn\n" + + "u9zBDAWFDgMtK9iiIOzp9FlNljHKJH1EdaWJD/Gv/5+ElNZpFDZ/oz/kosyvLeWZ\n" + + "EiA3U8/LDPFPlxe+gVqmr7+tzo88/C6i3NZSaUJ3qkaWSxx+xx/sNXoHX22msHwN\n" + + "SqnBQinCotrRO3WgZVDvhN3/eHZ8Zj7z6re7x/qlIGsKDVGxidWjGQDF2jp8O5Rp\n" + + "uD3s1d70097Es++KwAg=\n" + + "-----END CERTIFICATE-----\n")); + + presets.add((new WifiNetworkSettings("dhbw-802.1x", "dhbw-802.1x", WifiNetworkAuthenticationTypes.WIFI_AUTHTYPE_WPA_ENTERPRISE)). + setWpaEnterprise( + new int[]{WifiConfiguration.KeyMgmt.WPA_EAP, WifiConfiguration.KeyMgmt.IEEE8021X}, + WifiEnterpriseConfig.Eap.PEAP, + WifiEnterpriseConfig.Phase2.GTC, + null + ).setCACertificate("Deutsche Telekom Root CA 2", "-----BEGIN CERTIFICATE-----\n" + + "MIIDnzCCAoegAwIBAgIBJjANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJERTEc\n" + + "MBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxlU2Vj\n" + + "IFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290IENB\n" + + "IDIwHhcNOTkwNzA5MTIxMTAwWhcNMTkwNzA5MjM1OTAwWjBxMQswCQYDVQQGEwJE\n" + + "RTEcMBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxl\n" + + "U2VjIFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290\n" + + "IENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrC6M14IspFLEU\n" + + "ha88EOQ5bzVdSq7d6mGNlUn0b2SjGmBmpKlAIoTZ1KXleJMOaAGtuU1cOs7TuKhC\n" + + "QN/Po7qCWWqSG6wcmtoIKyUn+WkjR/Hg6yx6m/UTAtB+NHzCnjwAWav12gz1Mjwr\n" + + "rFDa1sPeg5TKqAyZMg4ISFZbavva4VhYAUlfckE8FQYBjl2tqriTtM2e66foai1S\n" + + "NNs671x1Udrb8zH57nGYMsRUFUQM+ZtV7a3fGAigo4aKSe5TBY8ZTNXeWHmb0moc\n" + + "QqvF1afPaA+W5OFhmHZhyJF81j4A4pFQh+GdCuatl9Idxjp9y7zaAzTVjlsB9WoH\n" + + "txa2bkp/AgMBAAGjQjBAMB0GA1UdDgQWBBQxw3kbuvVT1xfgiXotF2wKsyudMzAP\n" + + "BgNVHRMECDAGAQH/AgEFMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOC\n" + + "AQEAlGRZrTlk5ynrE/5aw4sTV8gEJPB0d8Bg42f76Ymmg7+Wgnxu1MM9756Abrsp\n" + + "tJh6sTtU6zkXR34ajgv8HzFZMQSyzhfzLMdiNlXiItiJVbSYSKpk+tYcNthEeFpa\n" + + "IzpXl/V6ME+un2pMSyuOoAPjPuCp1NJ70rOo4nI8rZ7/gFnkm0W09juwzTkZmDLl\n" + + "6iFhkOQxIY40sfcvNUqFENrnijchvllj4PKFiDFT1FQUhXB59C4Gdyd1Lx+4ivn+\n" + + "xbrYNuSD7Odlt79jWvNGr4GUN9RBjNYj1h7P9WgbRGOiWrqnNVmh5XAFmw4jV5mU\n" + + "Cm26OWMohpLzGITY+9HPBVZkVw==\n" + + "-----END CERTIFICATE-----")); + + presets.add((new WifiNetworkSettings("eduroam", "eduroam", WifiNetworkAuthenticationTypes.WIFI_AUTHTYPE_WPA_ENTERPRISE)). + setWpaEnterprise( + new int[] {WifiConfiguration.KeyMgmt.WPA_EAP, WifiConfiguration.KeyMgmt.IEEE8021X}, + WifiEnterpriseConfig.Eap.PEAP, + WifiEnterpriseConfig.Phase2.MSCHAPV2, + null + ).setCACertificate("Deutsche Telekom Root CA 2", "-----BEGIN CERTIFICATE-----\n" + + "MIIDnzCCAoegAwIBAgIBJjANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJERTEc\n" + + "MBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxlU2Vj\n" + + "IFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290IENB\n" + + "IDIwHhcNOTkwNzA5MTIxMTAwWhcNMTkwNzA5MjM1OTAwWjBxMQswCQYDVQQGEwJE\n" + + "RTEcMBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxl\n" + + "U2VjIFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290\n" + + "IENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrC6M14IspFLEU\n" + + "ha88EOQ5bzVdSq7d6mGNlUn0b2SjGmBmpKlAIoTZ1KXleJMOaAGtuU1cOs7TuKhC\n" + + "QN/Po7qCWWqSG6wcmtoIKyUn+WkjR/Hg6yx6m/UTAtB+NHzCnjwAWav12gz1Mjwr\n" + + "rFDa1sPeg5TKqAyZMg4ISFZbavva4VhYAUlfckE8FQYBjl2tqriTtM2e66foai1S\n" + + "NNs671x1Udrb8zH57nGYMsRUFUQM+ZtV7a3fGAigo4aKSe5TBY8ZTNXeWHmb0moc\n" + + "QqvF1afPaA+W5OFhmHZhyJF81j4A4pFQh+GdCuatl9Idxjp9y7zaAzTVjlsB9WoH\n" + + "txa2bkp/AgMBAAGjQjBAMB0GA1UdDgQWBBQxw3kbuvVT1xfgiXotF2wKsyudMzAP\n" + + "BgNVHRMECDAGAQH/AgEFMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOC\n" + + "AQEAlGRZrTlk5ynrE/5aw4sTV8gEJPB0d8Bg42f76Ymmg7+Wgnxu1MM9756Abrsp\n" + + "tJh6sTtU6zkXR34ajgv8HzFZMQSyzhfzLMdiNlXiItiJVbSYSKpk+tYcNthEeFpa\n" + + "IzpXl/V6ME+un2pMSyuOoAPjPuCp1NJ70rOo4nI8rZ7/gFnkm0W09juwzTkZmDLl\n" + + "6iFhkOQxIY40sfcvNUqFENrnijchvllj4PKFiDFT1FQUhXB59C4Gdyd1Lx+4ivn+\n" + + "xbrYNuSD7Odlt79jWvNGr4GUN9RBjNYj1h7P9WgbRGOiWrqnNVmh5XAFmw4jV5mU\n" + + "Cm26OWMohpLzGITY+9HPBVZkVw==\n" + + "-----END CERTIFICATE-----"). + setUserSuffix("@dhbw-loerrach.de")); + + WifiNetworkSettings[] presetsArr = new WifiNetworkSettings[presets.size()]; + presetsArr = presets.toArray(presetsArr); + return presetsArr; + } + + public enum WifiNetworkAuthenticationTypes { + WIFI_AUTHTYPE_NONE, + WIFI_AUTHTYPE_WEP, + WIFI_AUTHTYPE_WPA, + WIFI_AUTHTYPE_WPA_ENTERPRISE, + }; + + String name; + String ssid; + WifiNetworkAuthenticationTypes authType; + int[] authAlgorithms = new int[0]; + int eapMethod; + int phase2Method; + + String caCertStr; + String caName; + + SecureLoginManager performDHSecureLogin; + String usernameSuffix; + String username; + String password; + + public TabLayout.Tab tab; + public boolean isInScanResult = false; + public boolean isTryConnecting = false; + public boolean isAssociating = false; + public boolean isAssociated = false; + public boolean isAuthenticated = false; + public boolean isDisconnected = false; + public boolean failedAuthentication = false; + + public WifiNetworkSettings(String name, String ssid, WifiNetworkAuthenticationTypes authType) { + this.name = name; + this.ssid = ssid; + this.authType = authType; + switch(authType) { + case WIFI_AUTHTYPE_NONE: + case WIFI_AUTHTYPE_WEP: + this.authAlgorithms = new int[] { WifiConfiguration.KeyMgmt.NONE }; + break; + case WIFI_AUTHTYPE_WPA: + this.authAlgorithms = new int[] { WifiConfiguration.KeyMgmt.WPA_PSK }; + break; + } + } + + public WifiNetworkSettings setWpaEnterprise(int[] authAlgorithms, int eapMethod, int phase2Method, String username) { + this.authAlgorithms = authAlgorithms; + this.eapMethod = eapMethod; + this.phase2Method = phase2Method; + this.username = username; + return this; + } + + public WifiNetworkSettings setPassword(String password) { + this.password = password; + return this; + } + + public WifiNetworkSettings setCACertificate(String caname, String certificate) { + this.caName = caname; + this.caCertStr = certificate; + return this; + } + + public WifiNetworkSettings setUserSuffix(String suffix) { + this.usernameSuffix = suffix; + return this; + } + + public WifiNetworkSettings setDHSecureLogin(SecureLoginManager secureLogin) { + this.performDHSecureLogin = secureLogin; + return this; + } + + public void setAuthData(String username, String password) { + this.username = username; + this.password = password; + } + + public void setAuthData(String password) { + this.password = password; + } + + public X509Certificate generateCertificate() throws CertificateException, IOException { + byte[] decoded = Base64.decode(caCertStr.replaceAll("-----BEGIN CERTIFICATE-----", "").replaceAll("-----END CERTIFICATE-----", ""), Base64.DEFAULT); + return (X509Certificate) CertificateFactory.getInstance("X.509").generateCertificate(new ByteArrayInputStream(decoded)); + } + + + + public String getName() { + return name; + } + + public String getSSID() { + return ssid; + } + + public WifiNetworkSettings.WifiNetworkAuthenticationTypes getAuthType() { + return authType; + } + + public int getEapMethod() { + return eapMethod; + } + public int getPhase2Method() { + return phase2Method; + } + + public String getCAName() { + return caName; + } + +} diff --git a/app/src/main/res/drawable/dhbw_mensa.jpg b/app/src/main/res/drawable/dhbw_mensa.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4d04fc4f5f0de0fa19ae8239a4a5e92838cd6d6a GIT binary patch literal 35929 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/header_kalender.jpg b/app/src/main/res/drawable/header_kalender.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c7f94b0e49a94c17f9c4ab222cb84a24087ca70f GIT binary patch literal 30399 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
    UIr7f& za-r+QpS{y%F#RQ;&E!qIeG>O;dE>aZ!!*8FS~pk25kHHIsr6>!iFE0ASc?o@XU2lB z!)d8N2ao9&878y0=Z>m6DE&_Ej6S@Uqy~T(3=8{T~f`rQDO5F9yoq1#S4gxx#Nk|`lA?Jk3JZ+zRW#9+G{SC(0H zPVE3Pg%kxb0niLv55_a09U$#qOuf{&?(6mr%)Y~Xs-3n1N$wwJjO0owib&f@pLdF6#(EwjQ?d7| z?;g(6pv1ct11n(O zgt_MzYd1JY^5~CL$ZRfv^Dn{2&v6H(q@f0ymtHZLN~*J%;wj>A%JrgR&gu;Op%AeK zU$1ZsRY75!&U@0EPu(|%RG90ChOYG!LD&3jt5Wn*J!>;xu;3)(T+b_f7MUYcu3XtiI11*pTHaW}!!yNM8 ztDPv9FCBymJy7{DO3y6+sV8@L^u(_#iHgyi@_BKYS2WwIf{eESJXXOQyG1+Ye%7e= z)4Vj!-<>inlol%^sU33B#2Wzv=0aXia~qjnpX~DL!aSRrH!+qgRWsbqZdoIf{JA}E z9wGEBFHbC+juyMoNK5x+&5V6&!J0V}4t3~W_d25ac6H*GNle9=Ag9OT>GD$Z z*z_OX@>bQ2{!!WU@^wsQ z4J5w#+}V_r$93o`@FRk+V0vz7q`;4V*je~5(1&ZxIY)z0`N09L_Nh1^3gCvsU%)+n zO1k6O3%`!ePA63eU)MJ>le%apUPtkxUrCXwGzV9E-H4^ePls9GK)8t$@ri!`p2<(O zU86r!?q0Lw=NMzCI3qx09HTMY&E4mhTGy9m&!Hp2C~5B4k+nKp)nlzjCV>d}8VC{Q zUV$%k$-sA8Fq+o@C3Lpq3_~{$oPV)cy8l-xnIKnUdri|$(~6{U%4=4+C(Xri|X-D+Kjz7nrocOH5T?^uXtEZrk>*b8RoF?b>r*w-yU zGcx7S?mm})#HoB%rC8m^QXfjORhm#X#_+jhM)|{g_QCZ;5?kGj#ERah9j+tM1Ss;V ze4dKRHK0ji!+`cQdw}s?Onh>HGbvdTbBznTk&Nj;l33lj~#Zr`c^C2&ONezD@kWQIEwmm`7h^kEqHDblMIg3iD1Uc|Bi{UZQslXL$9Edq4r zd(0je_P+BK){U=-$HL7jV_v#0;R$SU@yqs4aqd;3Fp~cER+EtjH?M={W9KRibha96 z^VxsRY!9}aAaFVCvW&hu$}SJw?`8KacJ>S_G4%VBr=FE&`x_$|Ui(bUGk$iN?M6d@ z<@2Dp{M!cQchdd)bG(dVc_T7a#-{H^5}D(A8;?q6uHBw$Ci+xUg>TT_>PV-WMPcU* zvc-J#ig=?Zqa=WT0V&$DU7R4E08oBiOl*NRaf>G`;)%%8sh0t&xw2v_ZamF4rnlrsMQ}Y zHQsaCvhP4LlJO< z>~c(y0OErUk#cbm*g)Jg3^JY1z!)LV&|^>pV2bDsQ^nzRt9t+OuB0fdr_L0{f;IiF zCXSYq!?TRLO8_gRt3m?G1309d2;lweDv(9?;nRQXQoK$^QphF&>;uGxB^==F&+x>$ls&p}|Iw6p`~QH;i$+K-8CyGU=( zt@4^UlxDt!{~&I%Sa(21(&vQtv(;(KLP4QY$WZiCReLJM{co`SHRA8QeEqBgBPSC0 z>eaKBj-g$crwoyHAEwvaxpBR77N67GdNe1!B#V(v zG;@7fHSMpHi&NIq$=^}9e@%Hu``#bk8?dE0o?GZ0kBDRgK8KrzOaqCIu-p0x-<%1R zM|b;U$R|#GSSxyUa$@ygT_I21j2{fG>Oq>Bjqov2-yK!Nyj^d#o82vW4luHrS zPCZz0(9+_y^k2X+enU8HSs!oc$Lm`aEqO&WtY0=ZttnTypQ@sL;Do5Hy`g<*=4?Lm zSN9TZrv15qh|A*_uM$v5OXtnA$;GrE^GYg9HE)*^yW9J4nbkX)+ke((JWaRl(h2a? z5<=EqJ{!vEo7<=C=$crABZ;SfS98s9Z!}z)Im9`p3QkzodK7sM8IN`WGE5hqCrm%9 zn+BvD-TAc|hpiUP^qd0S{8n(CL6t2SLB|HOo;Kp1ly_h{!)Xjj2|3Gr=_UuPVZq+c zqsWY$TQ=4-9CM#?5e+3p81b;R5qfQb2)vd=va(tL=GH`U$+`uL)AZ(Uo|=WJH?>$A zTp=V8aOh0x z-u%AbN&CN#F-}Hir;hjDa8Tp2!a`NIbkj)xr9w3!9|pUK5(-Dc`*^XlOcST0IfT1m zK3=G5?*+o;or10LFNvUF{buO`PM^gHA@h%ouWgPdIEXviG-lGx5BRLBt^t>^g7mwl z3ZwdpI2FCC`=*ubg2H)IGpdAl=@wEPcrF$enmzvu{|)kp+blJnVYDccB(iAkjOP{5 ze|qI%6RCNaVr=!1u0TxBcvc&i12R={l>@_NFvx+C3S{62g+6%J7Pt01?f_FamPd~_ zo7bi!0%4G8DOl9R^lxL;%_mI1NP?VkhQCpV{(}GUMQs$P&M;|~&g)fPuw=(;61cF+>BJ*OhR4B9o zV7IoIni+rm18_Ka?2bs}v!sUTfUfSoub-9kgIN6Yati9~2`VSRSLu*2-3PKgTHoYa z-E-P@n{R!2e4Ng4d7ZFbIlVTLc2z6;(1No!Lj(~v0vb^l7(TA4B~?{l)>p0X;$!A{ zWSpIL6*Ir>?&!6lGzZ>pKn~mww@8b;X`Xu%F;SIU_TtIQ++u<$!a0IQ8KOSh^)X2z zGY4LU+J3VN>12br@1?kS=7v{x=D9foZl3V%AuCJHcLhxwo5SL@GBv;H;p_$|_~i<( zx>{5*sGaWRzNtX^xCyIV zk`7tG;xvA%+g{y)(lEh&JrT7N&Oy8Eb6~(A|AaOhbw}b|=_d;3O&C&8(znpcnpgq= zZ#@kg#QuB-=rbu2UW8A<%C2fnLi@iSE_B54I&0gv>JPV?3dgFy^W>ZCztva7&AW}$ zyiy;bKhK2ExmvEK_}b8X+!$LCYx=vyH>!Sbf&ZsG2MvR zhl(WY1bKQ9U}@iWe><|+@wl*LX+$rz6*0&$goEO8VkD6|$-ny(^|>_uYN0iX8oS5a z!CCt+An%ftit4U-j0j`)Ux<1(*KQ>G-CXud%o?L1@zfBf9y8FHORQZ>=IRf|_>hHS z1j|8ExCxGA0WBo4&ci(XK8WKyIso3oK(p$={L7S(J+RlwIAq%yV0E>nBgIvogbl+0 zK}cRuMf{rq{+-tt@&C;M7-;3c2Tce648jOP$RrGX?Fjp*}9}LA}JmFgH@HP0! zf1o4Lk=BP~c3ii4xY)--T^Xci^wh!n!)RQURiz!lrurhTtkd^CKfs|&1b!+IYXfyA zAHpJ6py$KdH`e(j<QzxMIB$D9$dBHpjx;m-))b~v6GGcHQg5zHbFXvd;p{664sKYxD6i#9>jm@V*QMDXaTPqu zTOy|p=eQrcSpI+@1N7_8=xS&3#??jxam4~IgpADtI1-&m0Nzc+(Ctma%FjV>5ybsP zP)X+mFpJ`}OA24x!1w;d0AXWE$;vAnG#&0D2eDOHm0t|ypMd;M+B4V8+t=WoKq?k( z#bS~iKrpv;!{2xvGj$3Wx;X@1chUU@lpQH$w8j0Vt(UMQQkz0Y3&-+Yg@qg$8Z9fd z3A}hmi0HapBF@nE26|n1 z1B@~Oed)dQnV+ND(`uSM-$=-01SCuHIH${$o5~=wYr+clgMM?kYFtvCIQJybHfoou5&dt-_&db;SjK6~(Rh>*|TK0YOeGWZk zoP1bZ<#=6rlp$>fZrW|C6ML%3_`+GTC1AJ?{u4OFLudu%s{i^i#ri7{#G9~lbr&de25vB$~OHN}(_b~iZ+(x}vQ>~o4n^Bj7^ZP4P z{;=X}CTH1XgHYzIxMJ%Uv1Tb^J$^u&UFEder>2{OL9eu4DKc)OGFNpe`eD1ACQh5u@|x!r*0m9FmWe={=2Wg`Iz~ zRRHZMOb)5B6CQU}o|l&urwd~?DBRr!QVmoZ0Gd?JB~pOPhm;G(Q*8r4P-R{lQi*Iq z_oEfCS&O%$ld7=^FsZI?TBTK*vD%=#$95U+aPR~{?e0vw6b2Fkd-GVVCA@M*Sj=@E z`HSGv1}QEKA^}05I|$DI58(>A9$=8mk`$K%THitLu7CZ(2bu~5puOV%uqaqChYnhm z{c;mx=e~3<*_g4vz$HKuRplt0KQ4DEK!2a3Ny{@Z3pQcW(}tgOYga(1LHyHawe&Aw z;Q;rxy2rpOr|=h0f{iUi?f#N$2F0!jyx-2_u`E1?P5HS-{T{u8z=mOv0;U(#0<*|wSIG7&(<}*E4^l`zwmrk zjOv*c4BHL#6!J3BPZ`r$U`@q~q)5&u|4tmcc;lau+WVdg70eu$y@p&0y~reQWLdLF zE~!qsi^r5zYLMw)N^GfFsApsSz?aB#kJuJxWV6&srnpIcWH@y7CF1J6{E2okI+IDi zQ~sgvQ}J6CHB!3ZZ$>&&}16s%Lo+p{>?tMAkd&gC$0e zIM^jxueB(gXx`Fy#ik8Sc+kd~HZ-qkE>6&tni*M5kK*m+jAuL(wwzR8Z0%^;QT-I< z0Q=Ca=sDSW$hB5_NwM@wEHf;(Vcke1U&kiW)Ko=LgocDEB9O+gN5sE)%}QIBA*<{Q zuiaIi)i+K!W}VO$X%s;QLa*I0lI)jTje(dEt%3y3Wg(~UBO9@0N7uZc6Jm*s*B~EB z6!+m2EU7fOZ3Shn^lCJ9-me%z3${p&W=#qrDn8m8w`iw0mL=!0BxI$80DY&Gb$6Yu z$cEQEosO;Uz4S`5{nO;8)x~MumnBW6bH+%tpW3cPKC(m_gO)#IN#f*`3xFNF`p)eD zta4Cl@nNHwINk~wISqXw0#WLkf!EpM1WTJa2jOf0V1A@km$DeOG^0WZ_V8-6u_SL3 zzOs3ceXyQurq+Bmk|}UsjL#@w=3>o;(sjvj=OC>5z{`f}%Ic&=;)N3)8ZMlgc%(Z6 zd#VJ8WBZVnWe^oX6-f>x{;SJ>v4wxt$w;yb-T#%K`#NNp)`da}P#OKZx8r(B_3gMnjK2 zs}WKl#NogOWA+H`=#mQ`Tz+$%NcQ7w#z-bOCuSUI6g#wJL$BiNz5Xj+*b*GU5Da<) zyxu&7nZZ)yJ6K(od@E2vLSF=ptz;i1Z1bu>ulB${ zlD>{?t^vg9X=bJwi_VF7BTxJe3m;~{ij^4r?q%fjU=Z7EyApb0Vr%;# zQD~#&~K+&u+FN0 zVL?oUm6I?h6m-iiGFPf0aVl95>5Ti`VDMu|la9KvwIkWQN@)d|t$L8ruhm-rbz&~5 z{Ix5Lc>D-u@@z@RZ>KXZJ+iICAzb1T;s_G|(k{_^p^3VCM5a%F_a5aZv~Wf%u>BWM zmvT-^?vTo2n~ozL5UqewL0X8;RQ2;_^?XZvGy^ z6DJRY4MLDv0b&4Q=2>Cn?svQDCl6rK(k)u5+i6wPtXF|C9!b$>LAQg!CU-yo>k*{? zPI^M<2!;WxwRm)8dn}rjPCF7^Sm4ahw_QgC>4AncD}01>S~0|F@iJ+}##@!ieRp2& ze5Y-Cu{9fi66{opYG$8?{;4y&3p8O3{N96dEcne>@8C}x?a z)H%p##CNx0TjNue9sgOpx=&ag@#*GP-&rtFWJX(o87ZfFXtjGp!xf;OG<3qLLk?aB zJBf4;T-Nux6KBC7CtxovYh9zwkO5%TJW=Ls0N>GV>g~jKmz(A!! z6OYSzFcgE$`+)p`P|l=y{HUG(fl30zo3HNh*Y*2zh*6H1k=$bs9tT7Qrh@eM(#Nx> zY$bBNJST!@;*FIxcW2m+U3$~$RT8gJ{+M7lbMqyCEh24-ymjRb*BoCO8a zZ&Fq+mJWQs^7z+E-J8jFeH`LQnas|fqG_tNJs4i3^8>vnYDk=wTB*6_mx{Bz)am|* zX<6ZAss8WhmL3(Rwq#)w3-2Ac)HI#B-4ue?eu!~#Dp$+uC%5^Z!i>M=nr46Gy*Afy z+y8!^r#zZg=`Nk(EOKl<%x=0gy!Xw(f=u)E%d~SE;WO~!li|S0atjah55HreUA~xT zKciRH1k|2bx^Kwvn4R$JG*+&*KN5w$j+}&L>%VgMxkg~$^{1i95f8rq1t6|OEzS0= zhX8Ht!R`~*5r{`}PhAWcv8=i#f$Xwb&fXmuL|i!n45U6BC1l`@eNb5i2m%38(kqbX sT)2wAI-+YMtILP{FJKSBw4L;BKx{x9fRcdj^FY!pOaMomUfkU&4#nM_kKg^Cd;d6+ zCppPv-ei)Kc_&<1Q5qeE1O*BT3S9;)p$Y{B?ey`UiG=WR_r`ww{qcZyRh1Tls+u4> z{7ArAi7JRfLDj~hzL>y&q>;aZbzGsKF#7(xpp%&~NTHxSEMz1^)jbVQeGzjEG}3Nc zJG$Nszk@O3;(`J7Qw60<%7cajwJ{;iEL{fb5>ujsFyC0hqB8SnX5!b|uyns0UVU%t z$b7DDm|PpCD-vb!+O7DR=zVnN?Yr^z?Rp-`(^E8VXKR0p3j%8qGFi+llEjEjK_TdQ zQ6uEJ`wEk{tu8Yw{P0@vH3N0`Otvu4v?W-A#jxwJI|va|DMWgm9Z)8i^rDgVFuW4* zqdZiPJIniGvs)Z?&i4L7jAk>aH-Ec4mxkva>F>t}ZO}^Be$Xlm_}EC0t+?7I^FTFd zCXEMjDze*(^^ZoEgaxlY@&Oi$;MH9l@%5vksH>Gvt`RXnOw9kXG5*WW4JLFZ{Kfgw z9ya%K@zaY|59Ug+2y_hkufzkPC(k({mM~6zwYpRfp;#1T0~xBLcgeGty!o|9WPamn zvpWdlA5!ck@^ffWIr>qV`>7!a$^8_{UoI&PK8p}bN-H3C#wV)`CK)ZX^U0v%eXk`o zoN2qoFu2DgLVH9z8=(kT?z1qaDn50*cC%@c{a;-WEay=-a&t}yIgUrTmSon?03xNf zh(EAq8B?<|{8j%rZie5xr()lNbt)4%@FH4cJU?{Q`)xD+-WADabtX=HE;UFo%mX|G zdJTP$j!xJ0kk%sf=gB?K*bsdyCd^~3qnH;X?lc~0-uh4Pe}kmgC#jhjs5jL&%O6&= zWQx9ptQsJzGGkt7x}vK^e3npr|4i|Er30pl)~-O$xDEm*n^n<%=;xw_`~y`ont&g6 zKdbJUqJRw$@QO}0w+rH%n+U@D#&U`8KOeFo-qA}S0(nA*u6udZ&ytnK(2b)P$vHp>g{RFQE-`WjC%7DqX~b2BjJ@ z+t3m;*ly^8Q8@!%h)0~7I)(Fy$cM+eiVGk0J<|)0dt=s=JFn854PN;k9F|=CWBD%p zo&I~*HL1L8qiMco*8hwuE2P-=d#E43qO;?O}?NLs4!P^O9HiJxu%1NN~{s({u}n5mKQ_+Uq+!~f6`;WE7FH@ad5qY zMDEyrA!j;_3L3p&MiU|?Mb>A*t995DTeADTI{#qv&yxZ&_GEbj28vX8*5VDi{pwCc z{}sD~qVPJ0{sxNiHi>hiD5Dy+Vtku_;i$k^1V)gxqL7Fj{0HnL+B)BPrisU(vsGD1 zqdInp`IP_O=Y!WhC1CcEr<2A0Z;s7-@11s`k3L7$xPDt6tQFX4&St>Ku?hrt{8*3m zhZWj*6LFJB@SHi0`vASy^+Saofu}J$2>!me&Z~3HY7VRn`Gi3m!i4{>{UD9o^bg>x zfG>&8y|BO$vnWWPPW<+iNklgzjD>8MDMHEac#GOZZqGAaXRgQXI8v;EC~Hb4`6P&J zaM{IvvP-ok!oD_`g{eqB`Jwz>#f(JV!$H{0JE~MuH2j><7QV=Ecy-oT-RWPbaxz zeKL|{f__N$e8aNMB`S;Oo8>Z{c+F*)%`mM!5f?O)RBMe&!NytTlWr#XLW3pMgRHvy zJZ@B~D_Ny3(b9UXA}u^Pm_n}SqfV073B*Sv_~N&x&dAUnbM8x!HSYZh%L`H4d8~r; zEh@fwSS0L1dEXo;5O_m4A$qrp`d}#XOC7C+22`UOobG|r&D1cGX`Ef_#ie)wFE7{+ z>Q5I(&(P)ADdb~d6Mido$WKjJkyN@}nlIc_tSP5CxfW_V1Vrmu-|RE`l^GR?`RaI) zqV^D~py4|W_Q_keWacKZ(h%+HLHiDJn?E577j8_5++NUx{=60FPH|q92*6kG%lBs7 z#XdRi-dya8Jv&FG^T3QW9TvhB#8a#8#|5zT8ZaZ<`7{ zEOMhf_yOcQ(o4C`E2z$0DDSz%yJfR35KvK#&Eu{icQuE{Y?4jwxOz{-Hp7v ziXbARLH4Qvg(d(g@VBojS6OKSfrsMebJU(e9#)0&&9Hn4w+d)wK%<#d1T-EvQ#He;oc?s zi&<9}D#wJ>bZ3jY@JuepF2t{x|@}2mw)bG(O*t-B+i_$rJkrnq!FwP z9I03XyKaoyrnsglA_DKMJ3=%sJ4SfynnN@5V|&zZ*jy>qsX-InJ@8$(2x;`J30nrtT z^UJTl$Y6B8L^M4SHB_w-At=5Rg!?#0P1p(caY3>f*-cC1**;;E%Be;v?J1NrZ&op;Y?KVYMjKbu)!)LEm|(288qsb*U?2HOB9jbkJGN$p%|QPhnRoZ+hRaNT=N5kY7F zuD$w9Cly`wb8m(N=Xg<8yKrcE*FtWDFuNDmBEg;JFU+6vN)*z59OS)D`#j*l5u9UQ zv`TrkG9RsJVEg3`dta;?sRD=V2kISU@_gTytqp*L4Jk>)IB823pJ^WVWlO%@n{C!M z5NU6WKWR$I=`rgH2*mmH*WTU>p2#iHUY!-L@Qcb~XE;h36gHL=Icp%I>KD}i{JZS~ zkaI3>J?YP|9mFLe);9IMGE1759hF!4JhqS3df?*beg!0#BoXEJHm+m?>wKRuoDgv!xN~M59Xn-5@Q|(!BFYvU@ zNbGM?_*i)kve{=Wli`F|0DGZv+J3%H)SbPAAUNPCpI%qScqh;%m49e4ogwOYdRnXB z2U^AUDjW;7>PinHGK#hb3b72Rgu`~AVh|6>IAiLcmxT{ta+M2Iuf%Ue+MJSnO2c?w zE;FX!d{LwdMw5%`A(%*>_T7@Sew$P>PTlxd4zFIM_~m&(1@7%Do2ru!h^C+KSDYE# zSfkhg3bcQJtZH-EjGjoqC%=0;vXjUiXd{BUO!^x(y)YdQ1C@8vXV1=%pOAe-Z;5rx z%hW+xyhFpFX;q2_#WEbT85VD>CAm|kM`yb7(JNiC; zer+2RJPBuYFj1*2@-o+C_dvA|BBSQmyn_hTxP-|t{|u^3OEq|mUOtD)VEhV&KI}Q#Lpq9IJ5E2oE>nz?4g_^ zsXR`A4Xy!M;UVA@%Of#oOWaSN(})U`@JL4?Y}9Ya1K5O69dc!e=$v`wo{mV9-A2va zy-Jn@@U~kWBx+l_AvCur?B++-MYK@`j#>{zx?oi;w}aalUHG@}gN-l5?h!}}BOeW{ zc<-DJb#7-`Tk6JY)?G9;_3#c6tnCX2G8#SO&1Ng0cKuzMo(^N(Rmk0rx-q^U75On1 zj+4xi9l72^XDv2o+wvrQioC zY_da1H8-3ELb9><*X4_j%kf+GM$FiDqhCv0SC+J12eejs=aBttdo*Z&6fPYsGnT1i znV?~1xz~;k+{fSaMu-^kA1 zH(aO4Z2Ar6(JV_+&Y&e;Lxa6iCuA){_TX%;LXZTmA157twue~di(z0y=QI=|mr9VG zd;Zk}+K34(gVml78(*l?P6?KvN|kjmH9>0v!8FKf5gx~^O}Wo=T+`m~4ypL)YTBVS zCibv0uKd4r>VOWzVtxD>K&*+}bN*jnLl2+oZM>{sj9C!LwT55SfzG@>8wHZ>{wChU@ zwIt)ean)RSI>t4vMd>W|wn}OdF&z47BFgK4f`rxDENj*NQ5OhE#)=bR|T??ggIkCX)ocrEfpGK{xSKGXM(otaNjRP8su7G+ zxy6@W8bU9#>r5Zcl>*8X3&qez7S}0LSA<&DhBNDKwlR^f;ECVu@pgLv-He`RAwHJd zk&9BEU#<)SA7ST4xL3p^bVb}cPCKkz+!TL>foVc&AL>W0+A zV!1{wpxd($!>T{39+Qa#ihNfshiwIU>f?lm$-38u)Jabr$4#Xg_RF4?^QOYj?LW+H zJ@Ff_)B}btFpkAlnfzZFd+JiC(~3#TyC;aROtJ>SDSZl|6NJE(zmWwZ0R(%NT&}=> zeQ_mH;3Rtx-_1{}Uh^gB1j`kras4|d`b_KyknLYnBb_c3H216%xzZg{W?ErgsR%!i z)=L-!XU;{NjcM+TnGOXDAH}cNJXo#hSrYA;d1z|w_gNw{sFmyIFs%WDaaW>}m=ZX} zPvMe@mgthBKtA&L%$&jD`#I6HEcEiq! zAFH}g8_O�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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/platzhalter.png b/app/src/main/res/drawable/platzhalter.png new file mode 100644 index 0000000000000000000000000000000000000000..d501e0cd7a5fef8d22b5ce7ed9f6036e43630ed0 GIT binary patch literal 5648 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/layout-land/fragment_dashboard.xml b/app/src/main/res/layout-land/fragment_dashboard.xml new file mode 100644 index 0000000..994876f --- /dev/null +++ b/app/src/main/res/layout-land/fragment_dashboard.xml @@ -0,0 +1,184 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_dashboard.xml b/app/src/main/res/layout/fragment_dashboard.xml index d6f19a5..4f0d324 100644 --- a/app/src/main/res/layout/fragment_dashboard.xml +++ b/app/src/main/res/layout/fragment_dashboard.xml @@ -1,9 +1,177 @@ - + tools:context=".fragments.Dashboard" + android:orientation="vertical"> - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_dashboard_mensa.xml b/app/src/main/res/layout/fragment_dashboard_mensa.xml new file mode 100644 index 0000000..f576b61 --- /dev/null +++ b/app/src/main/res/layout/fragment_dashboard_mensa.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_dashboard_mensa_menu.xml b/app/src/main/res/layout/fragment_dashboard_mensa_menu.xml new file mode 100644 index 0000000..d1e388a --- /dev/null +++ b/app/src/main/res/layout/fragment_dashboard_mensa_menu.xml @@ -0,0 +1,41 @@ + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_mensa_wochenplan.xml b/app/src/main/res/layout/fragment_dashboard_news_dhbw.xml similarity index 74% rename from app/src/main/res/layout/fragment_mensa_wochenplan.xml rename to app/src/main/res/layout/fragment_dashboard_news_dhbw.xml index 5e4cdd7..38df6d2 100644 --- a/app/src/main/res/layout/fragment_mensa_wochenplan.xml +++ b/app/src/main/res/layout/fragment_dashboard_news_dhbw.xml @@ -2,12 +2,12 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - tools:context="de.dhbwloe.campusapp.fragments.MensaWochenplan"> + tools:context="de.dhbwloe.campusapp.fragments.DashboardNewsDhbw"> + android:text="@string/hello_blank_fragment" /> diff --git a/app/src/main/res/layout/fragment_dashboard_news_stuv.xml b/app/src/main/res/layout/fragment_dashboard_news_stuv.xml new file mode 100644 index 0000000..a7900dc --- /dev/null +++ b/app/src/main/res/layout/fragment_dashboard_news_stuv.xml @@ -0,0 +1,13 @@ + + + + + + diff --git a/app/src/main/res/layout/fragment_dashboard_timetable.xml b/app/src/main/res/layout/fragment_dashboard_timetable.xml new file mode 100644 index 0000000..eed0b1c --- /dev/null +++ b/app/src/main/res/layout/fragment_dashboard_timetable.xml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_mensa.xml b/app/src/main/res/layout/fragment_mensa.xml index db3775d..310a1a6 100644 --- a/app/src/main/res/layout/fragment_mensa.xml +++ b/app/src/main/res/layout/fragment_mensa.xml @@ -1,7 +1,16 @@ - + android:layout_height="match_parent" + android:orientation="vertical"> + + - + diff --git a/app/src/main/res/layout/fragment_mensa_weekend.xml b/app/src/main/res/layout/fragment_mensa_weekend.xml new file mode 100644 index 0000000..261db52 --- /dev/null +++ b/app/src/main/res/layout/fragment_mensa_weekend.xml @@ -0,0 +1,14 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_vorlesungsplan.xml b/app/src/main/res/layout/fragment_vorlesungsplan.xml index 69d2dce..a87634b 100644 --- a/app/src/main/res/layout/fragment_vorlesungsplan.xml +++ b/app/src/main/res/layout/fragment_vorlesungsplan.xml @@ -1,13 +1,36 @@ - - - + + + + + + + + app:layout_behavior="@string/appbar_scrolling_view_behavior" /> - + diff --git a/app/src/main/res/layout/fragment_vorlesungsplan_exams.xml b/app/src/main/res/layout/fragment_vorlesungsplan_exams.xml new file mode 100644 index 0000000..b7fe84e --- /dev/null +++ b/app/src/main/res/layout/fragment_vorlesungsplan_exams.xml @@ -0,0 +1,13 @@ + + + + + + diff --git a/app/src/main/res/layout/fragment_vorlesungsplan_groups.xml b/app/src/main/res/layout/fragment_vorlesungsplan_groups.xml new file mode 100644 index 0000000..68df9fe --- /dev/null +++ b/app/src/main/res/layout/fragment_vorlesungsplan_groups.xml @@ -0,0 +1,21 @@ + + + + + + + + diff --git a/app/src/main/res/layout/fragment_vorlesungsplan_groups_course.xml b/app/src/main/res/layout/fragment_vorlesungsplan_groups_course.xml new file mode 100644 index 0000000..723f68e --- /dev/null +++ b/app/src/main/res/layout/fragment_vorlesungsplan_groups_course.xml @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_vorlesungsplan_upcoming.xml b/app/src/main/res/layout/fragment_vorlesungsplan_upcoming.xml new file mode 100644 index 0000000..e9bd178 --- /dev/null +++ b/app/src/main/res/layout/fragment_vorlesungsplan_upcoming.xml @@ -0,0 +1,15 @@ + + + + + + diff --git a/app/src/main/res/layout/fragment_vorlesungsplan_upcoming_course.xml b/app/src/main/res/layout/fragment_vorlesungsplan_upcoming_course.xml new file mode 100644 index 0000000..8515792 --- /dev/null +++ b/app/src/main/res/layout/fragment_vorlesungsplan_upcoming_course.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_vorlesungsplan_upcoming_day.xml b/app/src/main/res/layout/fragment_vorlesungsplan_upcoming_day.xml new file mode 100644 index 0000000..848160e --- /dev/null +++ b/app/src/main/res/layout/fragment_vorlesungsplan_upcoming_day.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 3ab3e9c..8f539c4 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -3,4 +3,9 @@ #3F51B5 #303F9F #FF4081 + + #8D0011 + #E3001B + #7E878E + #5F6D78 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index de4d73f..1557a8b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -7,6 +7,35 @@ Settings + Montag + Dienstag + Mittwoch + Donnerstag + Freitag + Samstag + Sonntag + + Mo + Di + Mi + Do + Fr + Sa + So + + + Vorlesungsplan + Heute + Morgen + + Klausur: + + Hoch die Hände, Wochenende! + + Nächste Kurse + Kursübersicht + Klausurübersicht + Die DHBW Lörrach bietet den Studenten verschiedene Möglichkeiten Geräte mit dem W-LAN zu verbinden. Mithilfe dieser App kannst du dein Gerät dazu konfigurieren sich unter berücksichtigung sicherheitsrelevanter Überprüfungen mit dem dhbw-secure Netzwerk zu verbinden. SSID: Security: @@ -34,6 +63,7 @@ Scanning... WLAN Adapter deaktiviert Verbunden! + Verbinde... Settings @@ -106,4 +136,12 @@ Dein Guthaben: PSK: Achtung: Dieses Netzwerk ist potentiell unsicher! + Mensaplan + Vergangene Vorlesungen anzeigen + Nächste Vorlesung: + Letzte Vorlesung: + - keine - + - keine - + Klausur: + Präsentation: -- 2.20.1