Compare commits
67 Commits
95 changed files with 3492 additions and 1046 deletions
After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 39 KiB |
@ -0,0 +1,145 @@
|
||||
/** |
||||
* ProjectLaogai |
||||
* |
||||
* Copyright 2019-2020 <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 |
||||
|
||||
import android.content.Intent |
||||
import android.graphics.Color |
||||
import android.os.Bundle |
||||
import android.view.View |
||||
import android.widget.EditText |
||||
import android.widget.LinearLayout |
||||
import android.widget.TextView |
||||
import androidx.appcompat.app.AppCompatActivity |
||||
import androidx.core.text.HtmlCompat |
||||
import androidx.viewpager.widget.ViewPager |
||||
import com.afollestad.materialdialogs.callbacks.onDismiss |
||||
import kotlinx.android.synthetic.main.activity_onboarding.* |
||||
import org.mosad.seil0.projectlaogai.controller.preferences.EncryptedPreferences |
||||
import org.mosad.seil0.projectlaogai.fragments.SettingsFragment |
||||
import org.mosad.seil0.projectlaogai.onboarding.ViewPagerAdapter |
||||
|
||||
class OnboardingActivity : AppCompatActivity() { |
||||
|
||||
companion object { |
||||
val layouts = intArrayOf(R.layout.fragment_on_welcome, R.layout.fragment_on_course, R.layout.fragment_on_login) |
||||
} |
||||
private lateinit var viewPager: ViewPager |
||||
private lateinit var viewPagerAdapter: ViewPagerAdapter |
||||
private lateinit var linLayoutDots: LinearLayout |
||||
private lateinit var dots: Array<TextView> |
||||
|
||||
private lateinit var editTextEmail: EditText |
||||
private lateinit var editTextPassword: EditText |
||||
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) { |
||||
super.onCreate(savedInstanceState) |
||||
setContentView(R.layout.activity_onboarding) |
||||
|
||||
viewPager = findViewById(R.id.viewPager) |
||||
linLayoutDots = findViewById(R.id.linLayout_Dots) |
||||
|
||||
addBottomDots(0) |
||||
|
||||
viewPagerAdapter = ViewPagerAdapter(this) |
||||
viewPager.adapter = viewPagerAdapter |
||||
viewPager.addOnPageChangeListener(viewPagerPageChangeListener) |
||||
|
||||
// we don't use the skip button, instead we use the start button to skip the last fragment |
||||
btn_Skip.visibility = View.GONE |
||||
} |
||||
|
||||
fun btnNextClick(@Suppress("UNUSED_PARAMETER")v: View) { |
||||
if (viewPager.currentItem < layouts.size - 1) { |
||||
viewPager.currentItem++ |
||||
} else { |
||||
launchHomeScreen() |
||||
} |
||||
} |
||||
|
||||
fun btnSkipClick(@Suppress("UNUSED_PARAMETER")v: View) { |
||||
launchHomeScreen() |
||||
} |
||||
|
||||
fun btnSelectCourseClick(@Suppress("UNUSED_PARAMETER")v: View) { |
||||
SettingsFragment().selectCourse(this).show { |
||||
onDismiss { |
||||
btnNextClick(v) // show the next fragment |
||||
} |
||||
} |
||||
} |
||||
|
||||
fun btnLoginClick(@Suppress("UNUSED_PARAMETER")v: View) { |
||||
editTextEmail = findViewById(R.id.editText_email) |
||||
editTextPassword = findViewById(R.id.editText_password) |
||||
|
||||
// get login credentials from gui |
||||
val email = editTextEmail.text.toString() |
||||
val password = editTextPassword.text.toString() |
||||
|
||||
// save the credentials |
||||
EncryptedPreferences.saveCredentials(email, password, this) |
||||
|
||||
launchHomeScreen() |
||||
} |
||||
|
||||
private fun addBottomDots(currentPage: Int) { |
||||
dots = Array(layouts.size) { TextView(this) } |
||||
linLayoutDots.removeAllViews() |
||||
|
||||
dots.forEach { |
||||
it.text = HtmlCompat.fromHtml("•", HtmlCompat.FROM_HTML_MODE_LEGACY) |
||||
it.textSize = 35f |
||||
it.setTextColor(Color.parseColor("#cccccc")) |
||||
linLayoutDots.addView(it) |
||||
} |
||||
|
||||
if (dots.isNotEmpty()) |
||||
dots[currentPage].setTextColor(Color.parseColor("#000000")) |
||||
} |
||||
|
||||
private fun launchHomeScreen() { |
||||
startActivity(Intent(this, MainActivity::class.java)) |
||||
finish() |
||||
} |
||||
|
||||
private var viewPagerPageChangeListener: ViewPager.OnPageChangeListener = object : ViewPager.OnPageChangeListener { |
||||
|
||||
override fun onPageSelected(position: Int) { |
||||
addBottomDots(position) |
||||
// changing the next button text to skip for the login fragment |
||||
if (position == layouts.size - 1) { |
||||
btn_Next.text = getString(R.string.skip) |
||||
btn_Next.visibility = View.VISIBLE |
||||
btn_Skip.visibility = View.GONE |
||||
} else { |
||||
btn_Next.visibility = View.GONE |
||||
btn_Skip.visibility = View.GONE |
||||
} |
||||
} |
||||
|
||||
override fun onPageScrolled(arg0: Int, arg1: Float, arg2: Int) {} |
||||
override fun onPageScrollStateChanged(arg0: Int) {} |
||||
} |
||||
|
||||
} |
@ -1,164 +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.controller |
||||
|
||||
import android.content.Context |
||||
import com.google.gson.Gson |
||||
import com.google.gson.GsonBuilder |
||||
import com.google.gson.JsonParser |
||||
import com.google.gson.reflect.TypeToken |
||||
import org.mosad.seil0.projectlaogai.controller.PreferencesController.Companion.cCourse |
||||
import org.mosad.seil0.projectlaogai.hsoparser.* |
||||
import java.io.BufferedReader |
||||
import java.io.File |
||||
import java.io.FileReader |
||||
import java.util.* |
||||
import kotlin.collections.ArrayList |
||||
|
||||
class CacheController(cont: Context) { |
||||
|
||||
private val context = cont |
||||
|
||||
init { |
||||
val cal = Calendar.getInstance() |
||||
val currentDay = Calendar.getInstance().get(Calendar.DAY_OF_WEEK) |
||||
val currentTime = System.currentTimeMillis() / 1000 |
||||
|
||||
// check if we need to update the mensa data before displaying it |
||||
readMensa(context) |
||||
cal.time = Date(mensaMenu.meta.updateTime * 1000) |
||||
|
||||
// if a) it's monday and the last cache update was on sunday or b) the cache is older than 24hr, update blocking |
||||
if ((currentDay == Calendar.MONDAY && cal.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY) || (currentTime - mensaMenu.meta.updateTime) > 86400) { |
||||
println("update mensa blocking") |
||||
TCoRAPIController.getMensa(context).get() |
||||
} |
||||
|
||||
// check if we need to update the timetables before displaying them |
||||
readTimetable(cCourse.courseName, 0, context) |
||||
cal.time = Date(timetables[0].meta.updateTime * 1000) |
||||
|
||||
// if a) it`s monday and the last cache update was not on a sunday or b) the cache is older than 24hr, update blocking |
||||
if((currentDay == Calendar.MONDAY && cal.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY) || (currentTime - timetables[0].meta.updateTime) > 86400) { |
||||
println("updating timetable after sunday!") |
||||
val jobA = TCoRAPIController.getTimetable(cCourse.courseName, 0, context) |
||||
val jobB = TCoRAPIController.getTimetable(cCourse.courseName, 1, context) |
||||
|
||||
jobA.get() |
||||
jobB.get() |
||||
} |
||||
|
||||
// check if an update is necessary, not blocking |
||||
if(currentTime - PreferencesController.coursesCacheTime > 86400) |
||||
TCoRAPIController.getCoursesList(context) |
||||
|
||||
if(currentTime - PreferencesController.mensaCacheTime > 10800) |
||||
TCoRAPIController.getMensa(context) |
||||
|
||||
if(currentTime - PreferencesController.timetableCacheTime > 10800) { |
||||
TCoRAPIController.getTimetable(cCourse.courseName, 0, context) |
||||
TCoRAPIController.getTimetable(cCourse.courseName, 1, context) |
||||
} |
||||
|
||||
readStartCache(cCourse.courseName) |
||||
} |
||||
|
||||
companion object { |
||||
var coursesList = ArrayList<Course>() |
||||
var timetables = ArrayList<TimetableCourseWeek>() |
||||
var mensaMenu = MensaMenu() |
||||
|
||||
/** |
||||
* read the courses list from the cached file |
||||
* add them to the coursesList object |
||||
*/ |
||||
private fun readCoursesList(context: Context) { |
||||
val file = File(context.filesDir, "courses.json") |
||||
|
||||
// make sure the file exists |
||||
if (!file.exists()) |
||||
TCoRAPIController.getCoursesList(context).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) |
||||
} |
||||
|
||||
/** |
||||
* get the MensaMenu object from the cached json, |
||||
* if cache is empty create the cache file |
||||
*/ |
||||
fun readMensa(context: Context) { |
||||
val file = File(context.filesDir, "mensa.json") |
||||
|
||||
// make sure the file exists |
||||
if (!file.exists()) { |
||||
TCoRAPIController.getMensa(context).get() |
||||
} |
||||
|
||||
val fileReader = FileReader(file) |
||||
val bufferedReader = BufferedReader(fileReader) |
||||
val mensaObject = JsonParser().parse(bufferedReader.readLine()).asJsonObject |
||||
|
||||
mensaMenu = GsonBuilder().create().fromJson(mensaObject, MensaMenu().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, context: Context) { |
||||
val file = File(context.filesDir, "timetable-$courseName-$week.json") |
||||
|
||||
// make sure the file exists |
||||
if (!file.exists()) |
||||
TCoRAPIController.getTimetable(courseName, week, context).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[week] = (Gson().fromJson(timetableObject, TimetableCourseWeek().javaClass)) |
||||
} else { |
||||
timetables.add(Gson().fromJson(timetableObject, TimetableCourseWeek().javaClass)) |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 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(context) |
||||
readMensa(context) |
||||
readTimetable(courseName, 0, context) |
||||
readTimetable(courseName, 1, context) |
||||
} |
||||
|
||||
} |
@ -1,134 +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.controller |
||||
|
||||
import android.content.Context |
||||
import android.graphics.Color |
||||
import org.jetbrains.anko.defaultSharedPreferences |
||||
import org.mosad.seil0.projectlaogai.R |
||||
import org.mosad.seil0.projectlaogai.hsoparser.Course |
||||
|
||||
/** |
||||
* The PreferencesController class |
||||
* contains all preferences and global variables that exist in this app |
||||
*/ |
||||
class PreferencesController { |
||||
|
||||
companion object { |
||||
var coursesCacheTime: Long = 0 |
||||
var mensaCacheTime: Long = 0 |
||||
var timetableCacheTime: Long = 0 |
||||
var cColorPrimary: Int = Color.BLACK |
||||
var cColorAccent: Int = Color.parseColor("#3F51B5") |
||||
var cCourse = Course("https://www.hs-offenburg.de/index.php?id=6627&class=class&iddV=DA64F6FE-9DDB-429E-A677-05D0D40CB636&week=0", "AI3") |
||||
var cShowBuffet = true |
||||
var oGiants = false |
||||
|
||||
// the save function |
||||
fun save(context: Context) { |
||||
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()) { |
||||
putString(context.getString(R.string.save_key_course), cCourse.courseName) |
||||
putString(context.getString(R.string.save_key_courseTTLink), cCourse.courseLink) |
||||
apply() |
||||
} |
||||
|
||||
// save the primary color |
||||
with (sharedPref.edit()) { |
||||
putInt(context.getString(R.string.save_key_colorPrimary), |
||||
cColorPrimary |
||||
) |
||||
apply() |
||||
} |
||||
|
||||
// save the accent color |
||||
with (sharedPref.edit()) { |
||||
putInt(context.getString(R.string.save_key_colorAccent), |
||||
cColorAccent |
||||
) |
||||
apply() |
||||
} |
||||
|
||||
// save showBuffet |
||||
with (sharedPref.edit()) { |
||||
putBoolean(context.getString(R.string.save_key_showBuffet), |
||||
cShowBuffet |
||||
) |
||||
apply() |
||||
} |
||||
|
||||
} |
||||
|
||||
// the load function |
||||
fun load(context: Context) { |
||||
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( |
||||
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")!! |
||||
) |
||||
|
||||
// load saved colors |
||||
cColorPrimary = sharedPref.getInt(context.getString( |
||||
R.string.save_key_colorPrimary |
||||
), Color.BLACK) |
||||
cColorAccent = sharedPref.getInt(context.getString( |
||||
R.string.save_key_colorAccent |
||||
), Color.parseColor("#3F51B5")) |
||||
|
||||
// load showBuffet |
||||
cShowBuffet = sharedPref.getBoolean(context.getString( |
||||
R.string.save_key_showBuffet |
||||
), true) |
||||
} |
||||
} |
||||
|
||||
} |
@ -0,0 +1,236 @@
|
||||
/** |
||||
* ProjectLaogai |
||||
* |
||||
* Copyright 2019-2020 <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 android.util.Log |
||||
import kotlinx.coroutines.Dispatchers |
||||
import kotlinx.coroutines.runBlocking |
||||
import kotlinx.coroutines.withContext |
||||
import org.jsoup.HttpStatusException |
||||
import org.jsoup.Jsoup |
||||
import org.jsoup.nodes.Element |
||||
import org.mosad.seil0.projectlaogai.R |
||||
import org.mosad.seil0.projectlaogai.controller.preferences.EncryptedPreferences |
||||
import org.mosad.seil0.projectlaogai.util.GradeSubject |
||||
import java.security.KeyStore |
||||
import java.security.cert.CertificateFactory |
||||
import java.security.cert.X509Certificate |
||||
import java.util.* |
||||
import javax.net.ssl.SSLContext |
||||
import javax.net.ssl.SSLSocketFactory |
||||
import javax.net.ssl.TrustManagerFactory |
||||
import kotlin.collections.ArrayList |
||||
import kotlin.collections.HashMap |
||||
|
||||
/** |
||||
* Parse the qispos site the get all needed data for the grades fragment |
||||
*/ |
||||
class QISPOSParser(val context: Context) { |
||||
|
||||
private val className = this.javaClass.name |
||||
private val baseURL = "https://notenverwaltung.hs-offenburg.de" |
||||
private val loginPath = "/qispos/rds?state=user&type=1&category=auth.login&startpage=portal.vm&breadCrumbSource=portal" |
||||
|
||||
/** |
||||
* check if qispos is available |
||||
* @return a http status code, 999 if no HttpStatusException supplied |
||||
*/ |
||||
fun checkQISPOSStatus(): Int { |
||||
return runBlocking { |
||||
withContext(Dispatchers.IO) { |
||||
val socketFactory = createSSLSocketFactory() |
||||
|
||||
try { |
||||
val res = Jsoup.connect(baseURL + loginPath) |
||||
.sslSocketFactory(socketFactory) |
||||
.followRedirects(true) |
||||
.referrer("https://notenverwaltung.hs-offenburg.de/qispos/rds?state=user&type=0") |
||||
.execute() |
||||
|
||||
res.statusCode() |
||||
} catch (exHttp: HttpStatusException) { |
||||
exHttp.statusCode |
||||
} catch (ex: Exception) { |
||||
Log.e(className, "Error while checking status", ex) |
||||
999 |
||||
} |
||||
|
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* parse the html from readGrades() |
||||
* @return a SortedMap, each entry is a semester, each semester has a ArrayList with subjects |
||||
*/ |
||||
fun parseGrades(): SortedMap<String, ArrayList<GradeSubject>> { |
||||
val gradesMap = HashMap<String, ArrayList<GradeSubject>>() |
||||
val gradesListHtml = readGrades() |
||||
|
||||
gradesListHtml?.select("table > tbody > tr")?.forEach { |
||||
val row = it.select("td.tabelle1_alignleft,td.tabelle1_aligncenter,td.tabelle1_alignright") |
||||
|
||||
// only real subjects will be selected |
||||
if(row.size >= 6 && row[0].text().length >=7) { |
||||
val subject = GradeSubject( |
||||
id = row[0].text(), |
||||
name = row[1].text(), |
||||
semester = row[2].text(), |
||||
grade = if (row[3].text().isNotEmpty()) row[3].text() else row[4].text(), |
||||
credits = row[5].text() |
||||
) |
||||
|
||||
if (gradesMap.containsKey(subject.semester)) { |
||||
gradesMap[subject.semester]!!.add(subject) |
||||
} else { |
||||
gradesMap[subject.semester] = arrayListOf(subject) |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
// return the sorted map |
||||
return gradesMap.toSortedMap(compareBy<String>{ |
||||
val oText = it.substringAfter(" ") |
||||
|
||||
if (oText.contains("/")) { |
||||
oText.substringBefore("/").toInt() + 0.5 |
||||
} else { |
||||
oText.toDouble() |
||||
} |
||||
}.thenBy { it }) |
||||
} |
||||
|
||||
/** |
||||
* read the grades html from qispos |
||||
* @return the grades list as html element or null |
||||
*/ |
||||
private fun readGrades(): Element?{ |
||||
|
||||
val credentials = EncryptedPreferences.readCredentials(context) |
||||
val username = credentials.first.substringBefore("@") |
||||
val password = credentials.second |
||||
|
||||
return runBlocking { |
||||
withContext(Dispatchers.IO) { |
||||
try { |
||||
val socketFactory = createSSLSocketFactory() |
||||
|
||||
// login, asdf = username, fdsa = password, wtf |
||||
val list = mapOf( |
||||
Pair("asdf", username), |
||||
Pair("fdsa", password), |
||||
Pair("submit", "Anmelden") |
||||
) |
||||
|
||||
// login and get session cookies |
||||
val res = Jsoup.connect(baseURL + loginPath) |
||||
.sslSocketFactory(socketFactory) |
||||
.followRedirects(true) |
||||
.referrer("https://notenverwaltung.hs-offenburg.de/qispos/rds?state=user&type=0") |
||||
.data(list) |
||||
.postDataCharset("UTF-8") |
||||
.execute() |
||||
Log.i(className, "login status is: ${res.statusMessage()}: ${res.statusCode()}") |
||||
|
||||
val loginCookies = res.cookies() |
||||
|
||||
// grades root document and url |
||||
val rootHtml =Jsoup.parse(res.body()) |
||||
val gradesRootLink = rootHtml.select("li.menueListStyle > a.auflistung").last().attr("abs:href") |
||||
|
||||
// parse grades url |
||||
val gradesHtml = Jsoup.connect(gradesRootLink) |
||||
.sslSocketFactory(socketFactory) |
||||
.followRedirects(true) |
||||
.cookies(loginCookies) |
||||
.referrer("https://notenverwaltung.hs-offenburg.de/qispos/rds?state=user&type=0") |
||||
.get() |
||||
|
||||
val gradesNextLink = gradesHtml.select("li.treelist > a.regular").attr("abs:href") |
||||
|
||||
val gradesNextHtml = Jsoup.connect(gradesNextLink) |
||||
.sslSocketFactory(socketFactory) |
||||
.followRedirects(true) |
||||
.cookies(loginCookies) |
||||
.referrer("https://notenverwaltung.hs-offenburg.de/qispos/rds?state=user&type=0") |
||||
.get() |
||||
|
||||
val gradesListLink = gradesNextHtml.selectFirst("li.treelist > ul > li").selectFirst("a").attr("abs:href") |
||||
|
||||
// get the grades list |
||||
val gradesListHtml = Jsoup.connect(gradesListLink) |
||||
.sslSocketFactory(socketFactory) |
||||
.followRedirects(true) |
||||
.cookies(loginCookies) |
||||
.referrer("https://notenverwaltung.hs-offenburg.de/qispos/rds?state=user&type=0") |
||||
.get() |
||||
|
||||
gradesListHtml.body() |
||||
} catch (ex: Exception) { |
||||
Log.e(className, "error while loading qispos", ex) |
||||
null |
||||
} |
||||
} |
||||
} |
||||