added the tcor api

* drastically improved loading times, closes #14
This commit is contained in:
Jannik 2019-03-17 18:12:36 +01:00
parent 040eb26dcf
commit f3b7ff066d
13 changed files with 364 additions and 358 deletions

View File

@ -13,7 +13,7 @@ android {
minSdkVersion 21 minSdkVersion 21
targetSdkVersion 28 targetSdkVersion 28
versionCode 11 versionCode 11
versionName "0.3.90" versionName "0.3.95"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
} }
buildTypes { buildTypes {
@ -33,11 +33,11 @@ dependencies {
implementation 'androidx.legacy:legacy-support-v4:1.0.0' implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'com.google.android.material:material:1.0.0' implementation 'com.google.android.material:material:1.0.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-alpha3' implementation 'androidx.constraintlayout:constraintlayout:2.0.0-alpha3'
implementation 'org.jsoup:jsoup:1.11.3'
implementation 'org.jetbrains.anko:anko-commons:0.10.8' implementation 'org.jetbrains.anko:anko-commons:0.10.8'
implementation 'com.afollestad:aesthetic:1.0.0-beta05' implementation 'com.afollestad:aesthetic:1.0.0-beta05'
implementation 'com.afollestad.material-dialogs:core:2.0.3' implementation 'com.afollestad.material-dialogs:core:2.0.3'
implementation 'com.afollestad.material-dialogs:color:2.0.3' implementation 'com.afollestad.material-dialogs:color:2.0.3'
implementation 'com.google.code.gson:gson:2.8.5'
testImplementation 'junit:junit:4.12' testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.1.1' androidTestImplementation 'androidx.test:runner:1.1.1'

View File

@ -32,29 +32,23 @@ import androidx.core.view.GravityCompat
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentTransaction import androidx.fragment.app.FragmentTransaction
import com.afollestad.aesthetic.Aesthetic import com.afollestad.aesthetic.Aesthetic
import com.afollestad.materialdialogs.MaterialDialog
import com.google.android.material.navigation.NavigationView import com.google.android.material.navigation.NavigationView
import kotlinx.android.synthetic.main.activity_main.* import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.app_bar_main.* import kotlinx.android.synthetic.main.app_bar_main.*
import org.jetbrains.anko.doAsync import org.mosad.seil0.projectlaogai.controller.PreferencesController.Companion.cColorAccent
import org.jetbrains.anko.uiThread import org.mosad.seil0.projectlaogai.controller.PreferencesController.Companion.cColorPrimary
import org.mosad.seil0.projectlaogai.PreferencesController.Companion.cColorAccent import org.mosad.seil0.projectlaogai.controller.PreferencesController.Companion.cCourse
import org.mosad.seil0.projectlaogai.PreferencesController.Companion.cColorPrimary import org.mosad.seil0.projectlaogai.controller.PreferencesController.Companion.coursesCacheTime
import org.mosad.seil0.projectlaogai.PreferencesController.Companion.cCourse import org.mosad.seil0.projectlaogai.controller.PreferencesController.Companion.mensaCacheTime
import org.mosad.seil0.projectlaogai.PreferencesController.Companion.cCourseTTLinkList import org.mosad.seil0.projectlaogai.controller.PreferencesController.Companion.timetableCacheTime
import org.mosad.seil0.projectlaogai.PreferencesController.Companion.cTimeTableCurrentWeek import org.mosad.seil0.projectlaogai.controller.CacheController
import org.mosad.seil0.projectlaogai.PreferencesController.Companion.cTimeTableNextWeek import org.mosad.seil0.projectlaogai.controller.PreferencesController
import org.mosad.seil0.projectlaogai.PreferencesController.Companion.cMenusCurrentWeek import org.mosad.seil0.projectlaogai.controller.TCoRAPIController
import org.mosad.seil0.projectlaogai.PreferencesController.Companion.cMenusNextWeek
import org.mosad.seil0.projectlaogai.fragments.* import org.mosad.seil0.projectlaogai.fragments.*
import org.mosad.seil0.projectlaogai.hsoparser.MensaParser
import org.mosad.seil0.projectlaogai.hsoparser.TimeTableParser
import kotlin.system.measureTimeMillis import kotlin.system.measureTimeMillis
class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener { class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener {
private val mensaParser = MensaParser()
private val timeTableParser = TimeTableParser()
private var activeFragment: Fragment = HomeFragment() // the currently active fragment, home at the start private var activeFragment: Fragment = HomeFragment() // the currently active fragment, home at the start
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@ -75,6 +69,8 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
colorAccent(Color.parseColor("#FF1744")) colorAccent(Color.parseColor("#FF1744"))
apply() apply()
} }
// TODO show a course selection
} else { } else {
Aesthetic.config { Aesthetic.config {
colorPrimary(cColorPrimary) colorPrimary(cColorPrimary)
@ -164,62 +160,31 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
* load the mensa menus of the current week * load the mensa menus of the current week
*/ */
private fun load() { private fun load() {
val menuLinkCurrentWeek = "https://www.swfr.de/de/essen-trinken/speiseplaene/mensa-offenburg/"
// load the settings // load the settings
PreferencesController.load(this) // this must be finished before doing anything else PreferencesController.load(this) // this must be finished before doing anything else
/** val startupTime = measureTimeMillis {
* load mensa, course timetable and courselist from the swfr/hso website
* TODO make an API see https://git.mosad.xyz/Seil0/TheCitadelofRicks
*/
val time = measureTimeMillis {
/* getting the course list should be faster than the timetable, // get the cached files
* we need have time until the user opens the dialog val cache = CacheController(this)
*/ cache.readStartCache(cCourse.courseName)
doAsync {
cCourseTTLinkList = timeTableParser.getCourseTTLinks() // check if an update is necessary
val tcor = TCoRAPIController(this)
val currentTime = System.currentTimeMillis() / 1000
if(currentTime - coursesCacheTime > 86400)
tcor.getCoursesList()
if(currentTime - mensaCacheTime > 10800)
tcor.getMensa()
if(currentTime - timetableCacheTime > 10800) {
tcor.getTimetable(cCourse.courseName, 0)
tcor.getTimetable(cCourse.courseName, 1)
} }
val jobMenusCurrentWeek = doAsync {
cMenusCurrentWeek = mensaParser.getMensaMenu(menuLinkCurrentWeek)
}
val jobMenusNextWeek = doAsync {
cMenusNextWeek = mensaParser.getMensaMenu(mensaParser.getMenuLinkNextWeek(menuLinkCurrentWeek))
}
val jobTTCurrentWeek = doAsync {
try {
cTimeTableCurrentWeek = timeTableParser.getTimeTable(cCourse.courseLink)
timeTableParser.printTimeTableWeek(cTimeTableCurrentWeek)
} catch (e: Exception) {
uiThread {
MaterialDialog(this@MainActivity)
.title(R.string.error)
.message(R.string.no_tt_error)
.show()
}
e.stackTrace
}
}
val jobTTNextWeek = doAsync {
try {
cTimeTableNextWeek = timeTableParser.getTimeTable(cCourse.courseLink.replace("week=0","week=1"))
} catch (e: Exception) {
e.stackTrace
}
}
jobMenusCurrentWeek.get()
jobMenusNextWeek.get()
jobTTCurrentWeek.get()
jobTTNextWeek.get()
} }
println("Completed in $time ms") println("Completed in $startupTime ms")
} }
} }

View File

@ -0,0 +1,122 @@
/**
* ProjectLaogai
*
* Copyright 2019 <seil0@mosad.xyz>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*/
package org.mosad.seil0.projectlaogai.controller
import android.content.Context
import com.google.gson.Gson
import com.google.gson.JsonParser
import org.mosad.seil0.projectlaogai.hsoparser.Course
import org.mosad.seil0.projectlaogai.hsoparser.MensaWeek
import org.mosad.seil0.projectlaogai.hsoparser.TimetableWeek
import java.io.BufferedReader
import java.io.File
import java.io.FileReader
import com.google.gson.reflect.TypeToken
class CacheController(cont: Context) {
private val context = cont
companion object {
var coursesList = ArrayList<Course>()
var mensaCurrentWeek = MensaWeek()
var mensaNextWeek = MensaWeek()
var timetables = ArrayList<TimetableWeek>()
}
/**
* read coursesList, mensa (current and next week), timetable (current and next week)
* @param courseName the course name (e.g AI1)
*/
fun readStartCache(courseName: String) {
readCoursesList()
readMensa()
readTimetable(courseName, 0)
readTimetable(courseName, 1)
}
/**
* read the courses list from the cached file
* add them to the coursesList object
*/
fun readCoursesList() {
val file = File(context.filesDir, "courses.json")
// make sure the file exists
if (!file.exists())
TCoRAPIController(context).getCoursesList().get()
val fileReader = FileReader(file)
val bufferedReader = BufferedReader(fileReader)
val coursesObject = JsonParser().parse(bufferedReader.readLine()).asJsonObject
coursesList = Gson().fromJson(coursesObject.getAsJsonArray("courses"), object : TypeToken<List<Course>>() {}.type)
}
/**
* read current and next weeks mensa menus from the cached file
*/
fun readMensa() {
val file = File(context.filesDir, "mensa.json")
// make sure the file exists
if (!file.exists()) {
TCoRAPIController(context).getMensa().get()
}
val fileReader = FileReader(file)
val bufferedReader = BufferedReader(fileReader)
val mensaObject = JsonParser().parse(bufferedReader.readLine()).asJsonObject
val currentWeek = mensaObject.getAsJsonObject("currentWeek")
val nextWeek = mensaObject.getAsJsonObject("nextWeek")
mensaCurrentWeek = Gson().fromJson(currentWeek, MensaWeek().javaClass)
mensaNextWeek = Gson().fromJson(nextWeek, MensaWeek().javaClass)
}
/**
* read the weeks timetable from the cached file
* @param courseName the course name (e.g AI1)
* @param week the week to read (0 for the current and so on)
*/
fun readTimetable(courseName: String, week: Int) {
val file = File(context.filesDir, "timetable-$courseName-$week.json")
// make sure the file exists
if (!file.exists())
TCoRAPIController(context).getTimetable(courseName, week).get()
val fileReader = FileReader(file)
val bufferedReader = BufferedReader(fileReader)
val timetableObject = JsonParser().parse(bufferedReader.readLine()).asJsonObject
// make sure you add the single weeks in the exact order!
if (timetables.size == week) {
timetables.add(Gson().fromJson(timetableObject.getAsJsonObject("timetable"), TimetableWeek().javaClass))
} else if (timetables.size >= week) {
timetables[week] = Gson().fromJson(timetableObject.getAsJsonObject("timetable"), TimetableWeek().javaClass)
}
}
}

View File

@ -20,14 +20,13 @@
* *
*/ */
package org.mosad.seil0.projectlaogai package org.mosad.seil0.projectlaogai.controller
import android.content.Context import android.content.Context
import android.graphics.Color import android.graphics.Color
import org.jetbrains.anko.defaultSharedPreferences import org.jetbrains.anko.defaultSharedPreferences
import org.mosad.seil0.projectlaogai.R
import org.mosad.seil0.projectlaogai.hsoparser.Course import org.mosad.seil0.projectlaogai.hsoparser.Course
import org.mosad.seil0.projectlaogai.hsoparser.MealWeek
import org.mosad.seil0.projectlaogai.hsoparser.TimeTable
/** /**
* The PreferencesController class * The PreferencesController class
@ -36,11 +35,9 @@ import org.mosad.seil0.projectlaogai.hsoparser.TimeTable
class PreferencesController { class PreferencesController {
companion object { companion object {
var cCourseTTLinkList = ArrayList<Course>() var coursesCacheTime: Long = 0
var cMenusCurrentWeek = MealWeek() var mensaCacheTime: Long = 0
var cMenusNextWeek = MealWeek() var timetableCacheTime: Long = 0
var cTimeTableCurrentWeek = TimeTable()
var cTimeTableNextWeek = TimeTable()
var cColorPrimary: Int = Color.BLACK var cColorPrimary: Int = Color.BLACK
var cColorAccent: Int = Color.BLACK var cColorAccent: Int = Color.BLACK
var cCourse = Course("https://www.hs-offenburg.de/index.php?id=6627&class=class&iddV=DA64F6FE-9DDB-429E-A677-05D0D40CB636&week=0", "AI3") var cCourse = Course("https://www.hs-offenburg.de/index.php?id=6627&class=class&iddV=DA64F6FE-9DDB-429E-A677-05D0D40CB636&week=0", "AI3")
@ -48,9 +45,23 @@ class PreferencesController {
// the save function // the save function
fun save(context: Context) { fun save(context: Context) {
//println(cCourse.courseLink)
// save the course
val sharedPref = context.defaultSharedPreferences val sharedPref = context.defaultSharedPreferences
// save the update times (cache)
with (sharedPref.edit()) {
putLong(context.getString(R.string.save_key_coursesCacheTime),
coursesCacheTime
)
putLong(context.getString(R.string.save_key_mensaCacheTime),
mensaCacheTime
)
putLong(context.getString(R.string.save_key_timetableCacheTime),
timetableCacheTime
)
apply()
}
// save the course
with (sharedPref.edit()) { with (sharedPref.edit()) {
putString(context.getString(R.string.save_key_course), cCourse.courseName) putString(context.getString(R.string.save_key_course), cCourse.courseName)
putString(context.getString(R.string.save_key_courseTTLink), cCourse.courseLink) putString(context.getString(R.string.save_key_courseTTLink), cCourse.courseLink)
@ -59,19 +70,25 @@ class PreferencesController {
// save the primary color // save the primary color
with (sharedPref.edit()) { with (sharedPref.edit()) {
putInt(context.getString(R.string.save_key_colorPrimary), cColorPrimary) putInt(context.getString(R.string.save_key_colorPrimary),
cColorPrimary
)
apply() apply()
} }
// save the accent color // save the accent color
with (sharedPref.edit()) { with (sharedPref.edit()) {
putInt(context.getString(R.string.save_key_colorAccent), cColorAccent) putInt(context.getString(R.string.save_key_colorAccent),
cColorAccent
)
apply() apply()
} }
// save showBuffet // save showBuffet
with (sharedPref.edit()) { with (sharedPref.edit()) {
putBoolean(context.getString(R.string.save_key_showBuffet), cShowBuffet) putBoolean(context.getString(R.string.save_key_showBuffet),
cShowBuffet
)
apply() apply()
} }
@ -79,19 +96,37 @@ class PreferencesController {
// the load function // the load function
fun load(context: Context) { fun load(context: Context) {
// load saved course
val sharedPref = context.defaultSharedPreferences val sharedPref = context.defaultSharedPreferences
// load the update times (cache)
coursesCacheTime = sharedPref.getLong(context.getString(
R.string.save_key_coursesCacheTime
), 0)
mensaCacheTime = sharedPref.getLong(context.getString(
R.string.save_key_mensaCacheTime
), 0)
timetableCacheTime = sharedPref.getLong(context.getString(
R.string.save_key_timetableCacheTime
), 0)
// load saved course
cCourse = Course( cCourse = Course(
sharedPref.getString(context.getString(R.string.save_key_courseTTLink), "https://www.hs-offenburg.de/index.php?id=6627&class=class&iddV=DA64F6FE-9DDB-429E-A677-05D0D40CB636&week=0")!!, sharedPref.getString(context.getString(R.string.save_key_courseTTLink), "https://www.hs-offenburg.de/index.php?id=6627&class=class&iddV=DA64F6FE-9DDB-429E-A677-05D0D40CB636&week=0")!!,
sharedPref.getString(context.getString(R.string.save_key_course), "AI3")!! sharedPref.getString(context.getString(R.string.save_key_course), "AI3")!!
) )
// load saved colors // load saved colors
cColorPrimary = sharedPref.getInt(context.getString(R.string.save_key_colorPrimary), Color.BLACK) cColorPrimary = sharedPref.getInt(context.getString(
cColorAccent = sharedPref.getInt(context.getString(R.string.save_key_colorAccent), Color.parseColor("#FF1744")) R.string.save_key_colorPrimary
), Color.BLACK)
cColorAccent = sharedPref.getInt(context.getString(
R.string.save_key_colorAccent
), Color.parseColor("#FF1744"))
// load showBuffet // load showBuffet
cShowBuffet = sharedPref.getBoolean(context.getString(R.string.save_key_showBuffet), true) cShowBuffet = sharedPref.getBoolean(context.getString(
R.string.save_key_showBuffet
), true)
} }
} }

View File

@ -0,0 +1,96 @@
/**
* ProjectLaogai
*
* Copyright 2019 <seil0@mosad.xyz>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*/
package org.mosad.seil0.projectlaogai.controller
import android.content.Context
import org.jetbrains.anko.doAsync
import org.json.JSONObject
import org.mosad.seil0.projectlaogai.controller.PreferencesController.Companion.coursesCacheTime
import org.mosad.seil0.projectlaogai.controller.PreferencesController.Companion.mensaCacheTime
import org.mosad.seil0.projectlaogai.controller.PreferencesController.Companion.timetableCacheTime
import java.io.*
import java.net.URL
class TCoRAPIController(cont: Context) {
private val context = cont
/**
* get the json object from tcor api and write it as file (cache)
*/
fun getCoursesList() = doAsync {
val url = URL("https://tcor.mosad.xyz/courses")
val file = File(context.filesDir, "courses.json")
// read data from the API
val coursesObject = JSONObject(url.readText()) //JSONObject(inReader.readLine())
// write the json object to a file
val writer = BufferedWriter(FileWriter(file))
writer.write(coursesObject.toString())
writer.close()
// update cache time
coursesCacheTime = System.currentTimeMillis() / 1000
PreferencesController.save(context)
}
/**
* get the json object from tcor api and write it as file (cache)
*/
fun getMensa() = doAsync {
val url = URL("https://tcor.mosad.xyz/mensamenu")
val file = File(context.filesDir, "mensa.json")
// read data from the API
val mensaObject = JSONObject(url.readText()) //JSONObject(inReader.readLine())
// write the json object to a file
val writer = BufferedWriter(FileWriter(file))
writer.write(mensaObject.toString())
writer.close()
// update cache time
mensaCacheTime = System.currentTimeMillis() / 1000
PreferencesController.save(context)
}
/**
* get the json object from tcor api and write it as file (cache)
*/
fun getTimetable(courseName: String, week: Int) = doAsync {
val url = URL("https://tcor.mosad.xyz/timetable?courseName=$courseName&week=$week")
val file = File(context.filesDir, "timetable-$courseName-$week.json")
// read data from the API
val mensaObject = JSONObject(url.readText()) //JSONObject(inReader.readLine())
// write the json object to a file
val writer = BufferedWriter(FileWriter(file))
writer.write(mensaObject.toString())
writer.close()
// update cache time
timetableCacheTime = System.currentTimeMillis() / 1000
PreferencesController.save(context)
}
}

View File

@ -32,10 +32,12 @@ import com.afollestad.materialdialogs.MaterialDialog
import kotlinx.android.synthetic.main.fragment_home.* import kotlinx.android.synthetic.main.fragment_home.*
import org.jetbrains.anko.doAsync import org.jetbrains.anko.doAsync
import org.jetbrains.anko.uiThread import org.jetbrains.anko.uiThread
import org.mosad.seil0.projectlaogai.PreferencesController.Companion.cTimeTableCurrentWeek
import org.mosad.seil0.projectlaogai.PreferencesController.Companion.cMenusCurrentWeek
import org.mosad.seil0.projectlaogai.R import org.mosad.seil0.projectlaogai.R
import org.mosad.seil0.projectlaogai.hsoparser.* import org.mosad.seil0.projectlaogai.controller.CacheController.Companion.mensaCurrentWeek
import org.mosad.seil0.projectlaogai.controller.CacheController.Companion.timetables
import org.mosad.seil0.projectlaogai.hsoparser.DataTypes
import org.mosad.seil0.projectlaogai.hsoparser.Meal
import org.mosad.seil0.projectlaogai.hsoparser.NotRetardedCalendar
import org.mosad.seil0.projectlaogai.uicomponents.LessonCardView import org.mosad.seil0.projectlaogai.uicomponents.LessonCardView
import org.mosad.seil0.projectlaogai.uicomponents.LessonTextView import org.mosad.seil0.projectlaogai.uicomponents.LessonTextView
import java.util.* import java.util.*
@ -68,14 +70,14 @@ class HomeFragment : Fragment() {
*/ */
private fun addCurrentMensaMenu() { private fun addCurrentMensaMenu() {
doAsync { doAsync {
val dayMenus: ArrayList<Meal> val dayMeals: ArrayList<Meal>
val cal = Calendar.getInstance() val cal = Calendar.getInstance()
if (cal.get(Calendar.HOUR_OF_DAY) < 15) { if (cal.get(Calendar.HOUR_OF_DAY) < 15) {
dayMenus = cMenusCurrentWeek.day[NotRetardedCalendar().getDayOfWeekIndex()] dayMeals = mensaCurrentWeek.days[NotRetardedCalendar().getDayOfWeekIndex()].meals
} else { } else {
dayMenus = cMenusCurrentWeek.day[NotRetardedCalendar().getTomorrowWeekIndex()] dayMeals = mensaCurrentWeek.days[NotRetardedCalendar().getTomorrowWeekIndex()].meals
uiThread { uiThread {
txtView_Menu1Heading.text = resources.getString(R.string.meal_1_tomorrow) txtView_Menu1Heading.text = resources.getString(R.string.meal_1_tomorrow)
txtView_Menu2Heading.text = resources.getString(R.string.meal_2_tomorrow) txtView_Menu2Heading.text = resources.getString(R.string.meal_2_tomorrow)
@ -84,19 +86,19 @@ class HomeFragment : Fragment() {
uiThread { uiThread {
if (dayMenus.size >= 2) { if (dayMeals.size >= 2) {
// get the index of the first meal, not a "Schneller Teller" // get the index of the first meal, not a "Schneller Teller"
loop@ for ((i, meal) in dayMenus.withIndex()) { loop@ for ((i, meal) in dayMeals.withIndex()) {
if (meal.heading.contains("Essen")) { if (meal.heading.contains("Essen")) {
for ((j, part) in dayMenus[i].parts.withIndex()) { for ((j, part) in dayMeals[i].parts.withIndex()) {
txtViewMenu1.append(part) txtViewMenu1.append(part)
if (j < (dayMenus[i].parts.size - 2)) if (j < (dayMeals[i].parts.size - 2))
txtViewMenu1.append("\n") txtViewMenu1.append("\n")
} }
for ((j, part) in dayMenus[i + 1].parts.withIndex()) { for ((j, part) in dayMeals[i + 1].parts.withIndex()) {
txtViewMenu2.append(part) txtViewMenu2.append(part)
if (j < (dayMenus[i + 1].parts.size - 2)) if (j < (dayMeals[i + 1].parts.size - 2))
txtViewMenu2.append("\n") txtViewMenu2.append("\n")
} }
@ -124,9 +126,9 @@ class HomeFragment : Fragment() {
private fun addCurrentTimeTable() { private fun addCurrentTimeTable() {
val dayIndex = NotRetardedCalendar().getDayOfWeekIndex() val dayIndex = NotRetardedCalendar().getDayOfWeekIndex()
if (cTimeTableCurrentWeek.days.isNotEmpty() && dayIndex < 6) { if (timetables[0].days.isNotEmpty() && dayIndex < 6) {
val timeTableDay = cTimeTableCurrentWeek.days[dayIndex] val timeTableDay = timetables[0].days[dayIndex]
// for all timeslots of the day // for all timeslots of the day
for ((i, timeslot) in timeTableDay.timeslots.withIndex()) { for ((i, timeslot) in timeTableDay.timeslots.withIndex()) {

View File

@ -23,18 +23,18 @@
package org.mosad.seil0.projectlaogai.fragments package org.mosad.seil0.projectlaogai.fragments
import android.os.Bundle import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.LinearLayout import android.widget.LinearLayout
import androidx.fragment.app.Fragment
import org.jetbrains.anko.doAsync import org.jetbrains.anko.doAsync
import org.jetbrains.anko.uiThread import org.jetbrains.anko.uiThread
import org.mosad.seil0.projectlaogai.PreferencesController.Companion.cMenusCurrentWeek import org.mosad.seil0.projectlaogai.controller.PreferencesController.Companion.cShowBuffet
import org.mosad.seil0.projectlaogai.PreferencesController.Companion.cMenusNextWeek
import org.mosad.seil0.projectlaogai.PreferencesController.Companion.cShowBuffet
import org.mosad.seil0.projectlaogai.R import org.mosad.seil0.projectlaogai.R
import org.mosad.seil0.projectlaogai.hsoparser.MealWeek import org.mosad.seil0.projectlaogai.controller.CacheController.Companion.mensaCurrentWeek
import org.mosad.seil0.projectlaogai.controller.CacheController.Companion.mensaNextWeek
import org.mosad.seil0.projectlaogai.hsoparser.MensaWeek
import org.mosad.seil0.projectlaogai.hsoparser.NotRetardedCalendar import org.mosad.seil0.projectlaogai.hsoparser.NotRetardedCalendar
import org.mosad.seil0.projectlaogai.uicomponents.MensaDayCardView import org.mosad.seil0.projectlaogai.uicomponents.MensaDayCardView
import org.mosad.seil0.projectlaogai.uicomponents.MenuCardView import org.mosad.seil0.projectlaogai.uicomponents.MenuCardView
@ -55,10 +55,10 @@ class MensaFragment : Fragment() {
// add the current week (week starts on sunday) // add the current week (week starts on sunday)
val dayCurrent = if(NotRetardedCalendar().getDayOfWeekIndex() == 6) 0 else NotRetardedCalendar().getDayOfWeekIndex() val dayCurrent = if(NotRetardedCalendar().getDayOfWeekIndex() == 6) 0 else NotRetardedCalendar().getDayOfWeekIndex()
addWeek(cMenusCurrentWeek, dayCurrent) addWeek(mensaCurrentWeek, dayCurrent)
// add the next week // add the next week
addWeek(cMenusNextWeek, 0) addWeek(mensaNextWeek, 0)
return view return view
} }
@ -66,7 +66,7 @@ class MensaFragment : Fragment() {
/** /**
* add all menus from dayStart to Friday for a given week * add all menus from dayStart to Friday for a given week
*/ */
private fun addWeek(menusWeek: MealWeek, dayStart: Int) { private fun addWeek(menusWeek: MensaWeek, dayStart: Int) {
doAsync { doAsync {
@ -76,7 +76,7 @@ class MensaFragment : Fragment() {
val cardViewMensaDay = MensaDayCardView(context!!, null) val cardViewMensaDay = MensaDayCardView(context!!, null)
for (meal in menusWeek.day[dayIndex]) { for (meal in menusWeek.days[dayIndex].meals) {
val menuViewMenu = MenuCardView(context!!, null) val menuViewMenu = MenuCardView(context!!, null)
menuViewMenu.setMenuHeading(meal.heading) menuViewMenu.setMenuHeading(meal.heading)

View File

@ -37,17 +37,16 @@ import com.afollestad.materialdialogs.list.listItems
import kotlinx.android.synthetic.main.fragment_settings.* import kotlinx.android.synthetic.main.fragment_settings.*
import org.jetbrains.anko.doAsync import org.jetbrains.anko.doAsync
import org.jetbrains.anko.uiThread import org.jetbrains.anko.uiThread
import org.mosad.seil0.projectlaogai.PreferencesController import org.mosad.seil0.projectlaogai.controller.PreferencesController
import org.mosad.seil0.projectlaogai.PreferencesController.Companion.cColorPrimary import org.mosad.seil0.projectlaogai.controller.PreferencesController.Companion.cColorAccent
import org.mosad.seil0.projectlaogai.PreferencesController.Companion.cColorAccent import org.mosad.seil0.projectlaogai.controller.PreferencesController.Companion.cColorPrimary
import org.mosad.seil0.projectlaogai.PreferencesController.Companion.cCourse import org.mosad.seil0.projectlaogai.controller.PreferencesController.Companion.cCourse
import org.mosad.seil0.projectlaogai.PreferencesController.Companion.cCourseTTLinkList import org.mosad.seil0.projectlaogai.controller.PreferencesController.Companion.cShowBuffet
import org.mosad.seil0.projectlaogai.PreferencesController.Companion.cShowBuffet
import org.mosad.seil0.projectlaogai.PreferencesController.Companion.cTimeTableCurrentWeek
import org.mosad.seil0.projectlaogai.PreferencesController.Companion.cTimeTableNextWeek
import org.mosad.seil0.projectlaogai.R import org.mosad.seil0.projectlaogai.R
import org.mosad.seil0.projectlaogai.controller.CacheController
import org.mosad.seil0.projectlaogai.controller.CacheController.Companion.coursesList
import org.mosad.seil0.projectlaogai.controller.TCoRAPIController
import org.mosad.seil0.projectlaogai.hsoparser.DataTypes import org.mosad.seil0.projectlaogai.hsoparser.DataTypes
import org.mosad.seil0.projectlaogai.hsoparser.TimeTableParser
import java.util.* import java.util.*
/** /**
@ -97,13 +96,13 @@ class SettingsFragment : Fragment() {
linLayoutCourse.setOnClickListener { linLayoutCourse.setOnClickListener {
// open a new dialog // open a new dialog
val courseList = ArrayList<String>() val courseNameList = ArrayList<String>()
cCourseTTLinkList.forEach { (_, course) -> coursesList.forEach { (_, courseName) ->
courseList.add(course) courseNameList.add(courseName)
} }
MaterialDialog(context!!).listItems(items = courseList) { _, index, text -> MaterialDialog(context!!).listItems(items = courseNameList) { _, index, text ->
txtView_Course.text = text // update txtView txtView_Course.text = text // update txtView
val dialog = MaterialDialog(context!!).cancelable(false) val dialog = MaterialDialog(context!!).cancelable(false)
@ -112,12 +111,15 @@ class SettingsFragment : Fragment() {
dialog.show() dialog.show()
doAsync { doAsync {
cCourse = cCourseTTLinkList[index] // set the course cCourse = coursesList[index] // set the course
PreferencesController.save(context!!) PreferencesController.save(context!!)
// update current & next weeks timetable // update current & next weeks timetable
cTimeTableCurrentWeek = TimeTableParser().getTimeTable(cCourse.courseLink) TCoRAPIController(context!!).getTimetable(cCourse.courseName, 0)
cTimeTableNextWeek = TimeTableParser().getTimeTable(cCourse.courseLink.replace("week=0","week=1")) TCoRAPIController(context!!).getTimetable(cCourse.courseName, 1)
CacheController(context!!).readTimetable(cCourse.courseName, 0)
CacheController(context!!).readTimetable(cCourse.courseName, 1)
uiThread { uiThread {
dialog.dismiss() dialog.dismiss()

View File

@ -30,9 +30,8 @@ import android.widget.LinearLayout
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import org.jetbrains.anko.doAsync import org.jetbrains.anko.doAsync
import org.jetbrains.anko.uiThread import org.jetbrains.anko.uiThread
import org.mosad.seil0.projectlaogai.PreferencesController.Companion.cTimeTableCurrentWeek
import org.mosad.seil0.projectlaogai.PreferencesController.Companion.cTimeTableNextWeek
import org.mosad.seil0.projectlaogai.R import org.mosad.seil0.projectlaogai.R
import org.mosad.seil0.projectlaogai.controller.CacheController.Companion.timetables
import org.mosad.seil0.projectlaogai.hsoparser.DataTypes import org.mosad.seil0.projectlaogai.hsoparser.DataTypes
import org.mosad.seil0.projectlaogai.hsoparser.NotRetardedCalendar import org.mosad.seil0.projectlaogai.hsoparser.NotRetardedCalendar
import org.mosad.seil0.projectlaogai.uicomponents.LessonCardView import org.mosad.seil0.projectlaogai.uicomponents.LessonCardView
@ -55,7 +54,7 @@ class TimeTableFragment : Fragment() {
linLayoutTTFragment = view.findViewById(R.id.linLayout_TTFragment) linLayoutTTFragment = view.findViewById(R.id.linLayout_TTFragment)
if (cTimeTableCurrentWeek.days.isNotEmpty()) { if (timetables[0].days.isNotEmpty()) {
addCurrentWeek() addCurrentWeek()
} else { } else {
// TODO show card with error msg // TODO show card with error msg
@ -82,7 +81,7 @@ class TimeTableFragment : Fragment() {
cardViewTimeTableDay.setDayHeading(formatter.format(calendar.time)) cardViewTimeTableDay.setDayHeading(formatter.format(calendar.time))
// for each timeslot of the day // for each timeslot of the day
for ((i, timeslot) in cTimeTableCurrentWeek.days[day].timeslots.withIndex()) { for ((i, timeslot) in timetables[0].days[day].timeslots.withIndex()) {
val lessonCardView = LessonCardView(context!!, null) val lessonCardView = LessonCardView(context!!, null)
lessonCardView.getTxtViewTime().text = DataTypes().getTime()[i] lessonCardView.getTxtViewTime().text = DataTypes().getTime()[i]
@ -119,7 +118,7 @@ class TimeTableFragment : Fragment() {
} }
// add next weeks days, max number = dayIndex, if timetable was loaded // add next weeks days, max number = dayIndex, if timetable was loaded
if (cTimeTableNextWeek.days.isNotEmpty()) { if (timetables[1].days.isNotEmpty()) {
calendar.add(Calendar.DATE, 1) // before this we are at a sunday (no lecture on sundays!) calendar.add(Calendar.DATE, 1) // before this we are at a sunday (no lecture on sundays!)
for (day in 0..(dayIndex - 1)) { for (day in 0..(dayIndex - 1)) {
@ -128,7 +127,7 @@ class TimeTableFragment : Fragment() {
cardViewTimeTableDay.setDayHeading(formatter.format(calendar.time)) cardViewTimeTableDay.setDayHeading(formatter.format(calendar.time))
// for each timeslot of the day // for each timeslot of the day
for ((i, timeslot) in cTimeTableNextWeek.days[day].timeslots.withIndex()) { for ((i, timeslot) in timetables[1].days[day].timeslots.withIndex()) {
val lessonCardView = LessonCardView(context!!, null) val lessonCardView = LessonCardView(context!!, null)
lessonCardView.getTxtViewTime().text = DataTypes().getTime()[i] lessonCardView.getTxtViewTime().text = DataTypes().getTime()[i]

View File

@ -115,10 +115,16 @@ data class Course(val courseLink: String, val courseName: String)
data class Meal(val day: String, val heading: String, val parts: ArrayList<String>, val additives: String) data class Meal(val day: String, val heading: String, val parts: ArrayList<String>, val additives: String)
data class MealWeek(val day: Array<ArrayList<Meal>> = Array(7) { ArrayList<Meal>() }) data class Meals(val meals: ArrayList<Meal>)
data class MensaWeek(val days: Array<Meals> = Array(7) { Meals(ArrayList()) })
data class MealWeek(val day: Array<ArrayList<Meal>> = Array(7) { ArrayList<Meal>() }) // TODO remove
data class Lesson(val lessonSubject: String, val lessonTeacher: String, val lessonRoom:String, val lessonRemark: String) data class Lesson(val lessonSubject: String, val lessonTeacher: String, val lessonRoom:String, val lessonRemark: String)
data class TimeTableDay( val timeslots: Array<ArrayList<Lesson>> = Array(6) { ArrayList<Lesson>()}) data class TimetableDay( val timeslots: Array<ArrayList<Lesson>> = Array(6) { ArrayList<Lesson>()})
data class TimeTable(val days: Array<TimeTableDay> = Array(6) { TimeTableDay() }) data class TimetableWeek(val days: Array<TimetableDay> = Array(6) { TimetableDay() })
data class TimeTable(val days: Array<TimetableDay> = Array(6) { TimetableDay() }) // TODO remove

View File

@ -1,78 +0,0 @@
/**
* ProjectLaogai
*
* Copyright 2019 <seil0@mosad.xyz>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*/
package org.mosad.seil0.projectlaogai.hsoparser
import org.jsoup.Jsoup
import java.util.*
import kotlin.collections.ArrayList
class MensaParser {
/**
* returns the mensa menu for the a week
*/
fun getMensaMenu(menuLink: String): MealWeek {
val mealList = ArrayList<Meal>()
val mealWeekList = MealWeek()
val menuHTML = Jsoup.connect(menuLink).get()
menuHTML.select("#speiseplan-tabs").select("div.tab-content").select("div.menu-tagesplan").forEachIndexed { dayIndex, day ->
val strDay = day.select("h3").text()
day.select("div.menu-info").forEachIndexed { mealIndex, meal ->
val heading = day.select("h4")[mealIndex].text()
val parts = ArrayList(meal.html().substringBefore("<br>\n").replace("<br>", " ").split("\n"))
val additives = meal.select("span.show-with-allergenes").text()
mealWeekList.day[dayIndex].add(Meal(strDay, heading, parts, additives))
}
for (i in 0 .. (day.select("div.row h4").size - 1)) {
try {
val heading = day.select("div.row h4")[i].text()
val parts = ArrayList<String>(day.select("div.row").select("div.menu-info")[i].html().substringBefore("<span").replace("<br>", " ").split("\n"))
val additives = day.select("div.row").select("div.menu-info")[i].select("span.show-with-allergenes").text()
mealList.add(Meal(strDay, heading, parts, additives))
} catch (e: Exception) {
//println("Oooups! Something went wrong: ${e.printStackTrace()}")
}
}
}
// Mon to Sat (0 - 5)
//println(mealWeekList.day[4])
return mealWeekList
}
/**
* return the link of the menus of the next week
*/
fun getMenuLinkNextWeek(menuLink: String): String {
val menuHTML = Jsoup.connect(menuLink).get()
return "https://www.swfr.de" + menuHTML.select("#speiseplan-tabs").select("a.next-week").attr("href")
}
}

View File

@ -1,146 +0,0 @@
/**
* ProjectLaogai
*
* Copyright 2019 <seil0@mosad.xyz>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*/
package org.mosad.seil0.projectlaogai.hsoparser
import org.jsoup.Jsoup
class TimeTableParser {
private val days = arrayOf("Monday", "Tuesday" ,"Wednesday", "Thursday", "Friday", "Saturday")
private var courseTTLinkList = ArrayList<Course>()
/**
* get the timetable from the given url
* the timetable is organised per row not per column;
* Mon 1, Tue 1, Wed 1, Thur 1, Fri 1, Sat 1, Mon 2 and so on
*/
fun getTimeTable(courseTTURL: String): TimeTable {
var timeTableWeek = TimeTable() // this must be a var!
val scheduleHTML = Jsoup.connect(courseTTURL).get()
//val week = scheduleHTML.select("h1.timetable-caption").text()
//println("$week successful!\n")
val rows= scheduleHTML.select("table.timetable").select("tr[scope=\"row\"]")
var sDay = -1
var sRow = -1
var sLesson = Lesson("", "", "", "")
// get each row with index, reflects 1 timeslot per day
for ((rowIndex, row) in rows.withIndex()) {
var day = 0
// elements are now all lessons, including empty ones
row.select("td.lastcol, td[style]").forEach { element ->
// if there is a lecture with rowspan="2", we need to shift everything by one to the left. This is stupid and ugly there needs to bee an API
if ((sDay > -1 && sRow > -1) && (sDay == day && ((sRow + 1) == rowIndex))) {
// we found a lecture that is longer than 1 lesson
timeTableWeek.days[day].timeslots[rowIndex].add(sLesson) // this just works if there is one lecture per slot
// adjust the following slot
sDay++
sLesson = Lesson(element.select("div.lesson-subject").text(), element.select("div.lesson-teacher").text(), element.select("div.lesson-room").text(), element.select("div.lesson-remark").text())
// adjust the slot directly as we don't get there anymore
if(sDay == 5) {
timeTableWeek.days[day + 1].timeslots[rowIndex].add(sLesson)
}
} else {
timeTableWeek.days[day].timeslots[rowIndex].add(Lesson(element.select("div.lesson-subject").text(), element.select("div.lesson-teacher").text(), element.select("div.lesson-room").text(), element.select("div.lesson-remark").text()))
}
// we found a lecture with rowspan="2", save day, row and lesson for later adjustment
if(element.toString().contains("rowspan=\"2\"")) {
sDay = day
sRow = rowIndex
sLesson = timeTableWeek.days[day].timeslots[rowIndex].get(index = 0)
}
if(element.hasClass("lastcol")) day++
}
}
printTimeTableWeek(timeTableWeek)
return timeTableWeek
}
/**
* parse all courses from the courses site at https://www.hs-offenburg.de/studium/vorlesungsplaene/
*/
fun getCourseTTLinks(): ArrayList<Course> {
val courseHTML = Jsoup.connect("https://www.hs-offenburg.de/studium/vorlesungsplaene/").get()
courseHTML.select("ul.index-group").select("li.Class").select("a[href]").forEachIndexed { _, element ->
courseTTLinkList.add(Course(element.attr("href").replace("http", "https"),element.text()))
}
return courseTTLinkList
}
@Suppress("unused")
fun printTimeTableWeek(timetable: TimeTable) {
for (j in 0..5) print(days[j].padEnd(75 ,' ') + " | ")
println()
for (j in 0..5) print("-".padEnd(76 + (j.toFloat().div(j).toInt()), '-') + "+")
println()
// the timeslot
for (i in 0..5) {
for (j in 0..5) {
val ldiff = if (timetable.days[j].timeslots[i].size == 0) 1 else timetable.days[j].timeslots[i].size
for (lesson in timetable.days[j].timeslots[i]) print(lesson.lessonSubject.padEnd(75/ldiff ,' '))
if (ldiff == 2) print(" ")
print(" | ")
}
println()
for (j in 0..5) {
val ldiff = if (timetable.days[j].timeslots[i].size == 0) 1 else timetable.days[j].timeslots[i].size
for (lesson in timetable.days[j].timeslots[i]) print(lesson.lessonTeacher.padEnd(75/ldiff ,' '))
if (ldiff == 2) print(" ")
print(" | ")
}
println()
for (j in 0..5) {
val ldiff = if (timetable.days[j].timeslots[i].size == 0) 1 else timetable.days[j].timeslots[i].size
for (lesson in timetable.days[j].timeslots[i]) print(lesson.lessonRoom.padEnd(75/ldiff ,' '))
if (ldiff == 2) print(" ")
print(" | ")
}
println()
for (j in 0..5) print("-".padEnd(76 + (j.toFloat().div(j).toInt()), '-') + "+")
println()
}
println(" \n")
}
}

View File

@ -2,7 +2,7 @@
<string name="app_name" translatable="false">Project Laogai</string> <string name="app_name" translatable="false">Project Laogai</string>
<string name="navigation_drawer_open">Open navigation drawer</string> <string name="navigation_drawer_open">Open navigation drawer</string>
<string name="navigation_drawer_close">Close navigation drawer</string> <string name="navigation_drawer_close">Close navigation drawer</string>
<string name="nav_header_title" translatable="false">hso App 0.3.90</string> <string name="nav_header_title" translatable="false">hso App 0.3.95</string>
<string name="nav_header_subtitle" translatable="false">seil0@mosad.xyz</string> <string name="nav_header_subtitle" translatable="false">seil0@mosad.xyz</string>
<string name="nav_header_desc" translatable="false">Project Laogai</string> <string name="nav_header_desc" translatable="false">Project Laogai</string>
@ -40,7 +40,7 @@
<string name="accent_color_desc">The accent color, default is indigo.</string> <string name="accent_color_desc">The accent color, default is indigo.</string>
<string name="show_buffet">always show buffet</string> <string name="show_buffet">always show buffet</string>
<string name="select">select</string> <string name="select">select</string>
<string name="version" translatable="false">version 0.3.90</string> <string name="version" translatable="false">version 0.3.95</string>
<string name="about">about</string> <string name="about">about</string>
<string name="about_txtView" translatable="false">hso App by @Seil0</string> <string name="about_txtView" translatable="false">hso App by @Seil0</string>
<string name="about_text" translatable="false">"This software is made by @Seil0 and is published under the terms and conditions of GPL 3. For further information visit \ngit.mosad.xyz/Seil0/ProjectLaogai \n\n© 2018-2019 seil0@mosad.xyz "</string> <string name="about_text" translatable="false">"This software is made by @Seil0 and is published under the terms and conditions of GPL 3. For further information visit \ngit.mosad.xyz/Seil0/ProjectLaogai \n\n© 2018-2019 seil0@mosad.xyz "</string>
@ -53,5 +53,8 @@
<string name="save_key_colorPrimary" translatable="false">org.mosad.seil0.projectlaogai.colorPrimary</string> <string name="save_key_colorPrimary" translatable="false">org.mosad.seil0.projectlaogai.colorPrimary</string>
<string name="save_key_colorAccent" translatable="false">org.mosad.seil0.projectlaogai.colorAccent</string> <string name="save_key_colorAccent" translatable="false">org.mosad.seil0.projectlaogai.colorAccent</string>
<string name="save_key_showBuffet" translatable="false">org.mosad.seil0.projectlaogai.showBuffet</string> <string name="save_key_showBuffet" translatable="false">org.mosad.seil0.projectlaogai.showBuffet</string>
<string name="save_key_coursesCacheTime" translatable="false">org.mosad.seil0.projectlaogai.coursesCacheTime</string>
<string name="save_key_mensaCacheTime" translatable="false">org.mosad.seil0.projectlaogai.mensaCacheTime</string>
<string name="save_key_timetableCacheTime" translatable="false">org.mosad.seil0.projectlaogai.timetableCacheTime</string>
</resources> </resources>