Compare commits

...

53 Commits

Author SHA1 Message Date
Jannik 28520bee74
changelog: minor text fixes 2 years ago
Jannik 0c6a486dd9 Merge pull request 'version 0.6.0' (#46) from develop into master 2 years ago
Jannik ec15c79b63
update fastlane screenshot 2 years ago
Jannik 4592d1984f
release version 0.6.0 2 years ago
Jannik aae74cf9db „README.md“ ändern 2 years ago
Jannik e19b0db39d
add missing licenses to license dialog 2 years ago
Jannik 23fd52b0c5
fab: use plus as icon 2 years ago
Jannik 62a62049c3
code clean up 2 years ago
Jannik 849698779b
add icon as svg 2 years ago
Jannik a42a92a3c5
update constraintlayout 2 years ago
Jannik be30e2f968
enable optimisations and minifying for release builds 2 years ago
Jannik c629b2aec2
fix login button 2 years ago
Jannik 99ba87c3f6
init fragment in onViewCreated() 2 years ago
Jannik 953b4825a9
check qispos status and show message if unavailable 2 years ago
Jannik 2807843d25
fix grades divider shown when sub-subject was added 2 years ago
Jannik 638c321798
grades ui polishing & sorting fix 2 years ago
Jannik 42d938b0bc
fix background in fragment_grades 2 years ago
Jannik 9d7504bbaf
add grades ui 2 years ago
Jannik 8ecfe8f120
QISPOSParser [Part 2] 2 years ago
Jannik ff0c4ad1a7
QISPOSParser [Part 1] 2 years ago
Jannik ea0caea91e
save email and password to encrypted preference 2 years ago
Jannik 1f660bdd37
add login dialog 2 years ago
Jannik be43d87b1a
fix more lint warnings 2 years ago
Jannik dd5c7b3fb8
fix some lint warnings 2 years ago
Jannik fb3dab6dc3
save additional subjects and load them on start 2 years ago
Jannik bcfdb83d14
rework CacheController and TCoRAPIController 2 years ago
Jannik 8c55366ec0
update libs 2 years ago
Jannik 6f68fbb73c „README.md“ ändern 2 years ago
Jannik 297dd77e79
use gradle wrapper for drone 2 years ago
Jannik 6a70f26807
update .drone.yml 2 years ago
Jannik f6b00a8d81
additional Subject are now added to the timetable 2 years ago
Jannik 69ce9fef5a
code clean up 2 years ago
Jannik 2d753851c0
more "Additional Lessons" work 2 years ago
Jannik 6c0624c793
make the app more tolerant about wrong API Data 2 years ago
Jannik 7779296345
change default accent color, code clean up 2 years ago
Jannik 3e2ef0521e
change primary color to reflect mosad.xyz design guidelines 2 years ago
Jannik bdfeb46faf
add "Manage Lessons" Dialog to Settings, minor style fixes 2 years ago
Jannik 18ca435764
update TCoRAPIController to API version 1.2.0 2 years ago
Jannik 948f330ebe
TimeTableFragment clean up 2 years ago
Jannik 72e9efb9e7
fix warings in TCoRAPIController 2 years ago
Jannik bea1b47396
complete & move AddLessonDialog to separate class 2 years ago
Jannik 34a68ff75d
add courses to addLesson Dialog, read lessonSubjects from tcor 2 years ago
Jannik 1ba3f1fa87
update gradle tool to 4.0.0 2 years ago
Jannik 48544aef2f
add a complete version of the addLesson Dialog 2 years ago
Jannik bc09e33147
update gradle, constraintlayout & coroutines 2 years ago
Jannik 6ec9b9f5e2
update commons-lang and coroutines 2 years ago
Jannik a19173b499
update kotlin 2 years ago
Jannik 85dc3181fd
fix compatibility with tcor version 1.2.2 and higher 2 years ago
Jannik e46c234b6c
fix OnboardingActivity is not closed on start of MainActivity 2 years ago
Jannik faa07966da
fix crash on first start, if tcor is not reachable 2 years ago
Jannik ccc0f0f2bc
Onboarding [Part 2] 2 years ago
Jannik e15bf95b85
update to gradle 6.2.1 and gradle android plugin 3.6.0 2 years ago
Jannik 01677c04ef
Onboarding [Part 1] 2 years ago
  1. 4
      .drone.yml
  2. 18
      README.md
  3. 34
      app/build.gradle
  4. 9
      app/proguard-rules.pro
  5. 6
      app/src/androidTest/java/org/mosad/seil0/projectlaogai/ExampleInstrumentedTest.kt
  6. 8
      app/src/main/AndroidManifest.xml
  7. BIN
      app/src/main/ic_laogai_icon-playstore.png
  8. BIN
      app/src/main/ic_laogai_icon-web.png
  9. 29
      app/src/main/java/org/mosad/seil0/projectlaogai/MainActivity.kt
  10. 145
      app/src/main/java/org/mosad/seil0/projectlaogai/OnboardingActivity.kt
  11. 177
      app/src/main/java/org/mosad/seil0/projectlaogai/controller/CacheController.kt
  12. 5
      app/src/main/java/org/mosad/seil0/projectlaogai/controller/NFCMensaCard.kt
  13. 183
      app/src/main/java/org/mosad/seil0/projectlaogai/controller/PreferencesController.kt
  14. 236
      app/src/main/java/org/mosad/seil0/projectlaogai/controller/QISPOSParser.kt
  15. 136
      app/src/main/java/org/mosad/seil0/projectlaogai/controller/TCoRAPIController.kt
  16. 375
      app/src/main/java/org/mosad/seil0/projectlaogai/controller/cache/CacheController.kt
  17. 131
      app/src/main/java/org/mosad/seil0/projectlaogai/controller/cache/TimetableController.kt
  18. 110
      app/src/main/java/org/mosad/seil0/projectlaogai/controller/preferences/EncryptedPreferences.kt
  19. 192
      app/src/main/java/org/mosad/seil0/projectlaogai/controller/preferences/Preferences.kt
  20. 200
      app/src/main/java/org/mosad/seil0/projectlaogai/fragments/GradesFragment.kt
  21. 28
      app/src/main/java/org/mosad/seil0/projectlaogai/fragments/HomeFragment.kt
  22. 50
      app/src/main/java/org/mosad/seil0/projectlaogai/fragments/MensaFragment.kt
  23. 7
      app/src/main/java/org/mosad/seil0/projectlaogai/fragments/MoodleFragment.kt
  24. 155
      app/src/main/java/org/mosad/seil0/projectlaogai/fragments/SettingsFragment.kt
  25. 134
      app/src/main/java/org/mosad/seil0/projectlaogai/fragments/TimeTableFragment.kt
  26. 59
      app/src/main/java/org/mosad/seil0/projectlaogai/onboarding/ViewPagerAdapter.kt
  27. 12
      app/src/main/java/org/mosad/seil0/projectlaogai/uicomponents/DayCardView.kt
  28. 58
      app/src/main/java/org/mosad/seil0/projectlaogai/uicomponents/GradeLinearLayout.kt
  29. 5
      app/src/main/java/org/mosad/seil0/projectlaogai/uicomponents/LessonLinearLayout.kt
  30. 5
      app/src/main/java/org/mosad/seil0/projectlaogai/uicomponents/MealLinearLayout.kt
  31. 63
      app/src/main/java/org/mosad/seil0/projectlaogai/uicomponents/TextViewInfo.kt
  32. 164
      app/src/main/java/org/mosad/seil0/projectlaogai/uicomponents/dialogs/AddSubjectDialog.kt
  33. 87
      app/src/main/java/org/mosad/seil0/projectlaogai/uicomponents/dialogs/LoginDialog.kt
  34. 75
      app/src/main/java/org/mosad/seil0/projectlaogai/util/DataTypes.kt
  35. 64
      app/src/main/java/org/mosad/seil0/projectlaogai/util/NotRetardedCalendar.kt
  36. 4
      app/src/main/res/drawable/background_splash.xml
  37. 9
      app/src/main/res/drawable/ic_add_black_24dp.xml
  38. 9
      app/src/main/res/drawable/ic_error_outline_black_24dp.xml
  39. 9
      app/src/main/res/drawable/ic_grading_black_24dp.xml
  40. 9
      app/src/main/res/drawable/ic_menu_send.xml
  41. 9
      app/src/main/res/drawable/ic_menu_share.xml
  42. 13
      app/src/main/res/drawable/ic_settings_black_24dp.xml
  43. BIN
      app/src/main/res/drawable/ic_splash_logo.png
  44. 9
      app/src/main/res/drawable/side_nav_bar.xml
  45. 53
      app/src/main/res/layouts/activities/layout/activity_onboarding.xml
  46. 30
      app/src/main/res/layouts/activities/layout/dialog_login.xml
  47. 70
      app/src/main/res/layouts/activities/layout/linearlayout_grade.xml
  48. 2
      app/src/main/res/layouts/activities/layout/linearlayout_lesson.xml
  49. 30
      app/src/main/res/layouts/activities/layout/nav_header_main.xml
  50. 42
      app/src/main/res/layouts/dialogs/layout/dialog_add_lesson.xml
  51. 2
      app/src/main/res/layouts/dialogs/layout/dialog_mensa_credit.xml
  52. 38
      app/src/main/res/layouts/fragments/layout/fragment_grades.xml
  53. 33
      app/src/main/res/layouts/fragments/layout/fragment_mensa.xml
  54. 72
      app/src/main/res/layouts/fragments/layout/fragment_on_course.xml
  55. 93
      app/src/main/res/layouts/fragments/layout/fragment_on_login.xml
  56. 70
      app/src/main/res/layouts/fragments/layout/fragment_on_welcome.xml
  57. 483
      app/src/main/res/layouts/fragments/layout/fragment_settings.xml
  58. 23
      app/src/main/res/layouts/fragments/layout/fragment_timetable.xml
  59. 4
      app/src/main/res/menu/activity_main_drawer.xml
  60. 4
      app/src/main/res/mipmap-anydpi-v26/ic_laogai_icon.xml
  61. BIN
      app/src/main/res/mipmap-hdpi/ic_laogai_icon.png
  62. BIN
      app/src/main/res/mipmap-hdpi/ic_laogai_icon_foreground.png
  63. BIN
      app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
  64. BIN
      app/src/main/res/mipmap-mdpi/ic_laogai_icon.png
  65. BIN
      app/src/main/res/mipmap-mdpi/ic_laogai_icon_foreground.png
  66. BIN
      app/src/main/res/mipmap-mdpi/ic_laogai_icon_splash.png
  67. BIN
      app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
  68. BIN
      app/src/main/res/mipmap-xhdpi/ic_laogai_icon.png
  69. BIN
      app/src/main/res/mipmap-xhdpi/ic_laogai_icon_foreground.png
  70. BIN
      app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
  71. BIN
      app/src/main/res/mipmap-xxhdpi/ic_laogai_icon.png
  72. BIN
      app/src/main/res/mipmap-xxhdpi/ic_laogai_icon_foreground.png
  73. BIN
      app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
  74. BIN
      app/src/main/res/mipmap-xxxhdpi/ic_laogai_icon.png
  75. BIN
      app/src/main/res/mipmap-xxxhdpi/ic_laogai_icon_foreground.png
  76. BIN
      app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
  77. BIN
      app/src/main/res/raw/notenverwaltung_hs_offenburg_de.crt
  78. 24
      app/src/main/res/raw/notices.xml
  79. 44
      app/src/main/res/values-de-rDE/strings.xml
  80. 18
      app/src/main/res/values/colors.xml
  81. 77
      app/src/main/res/values/strings.xml
  82. 10
      app/src/main/res/values/styles.xml
  83. 4
      build.gradle
  84. 46
      etc/drawable_resources/laogai_icon.svg
  85. 12
      fastlane/metadata/android/de-DE/full_description.txt
  86. 9
      fastlane/metadata/android/en-US/changelogs/6000.txt
  87. 12
      fastlane/metadata/android/en-US/full_description.txt
  88. BIN
      fastlane/metadata/android/en-US/images/phoneScreenshots/ProjectLaogai_Settings.png
  89. BIN
      fastlane/metadata/android/en-US/images/phoneScreenshots/ProjectLaogai_Settings_dark.png
  90. BIN
      gradle/wrapper/gradle-wrapper.jar
  91. 2
      gradle/wrapper/gradle-wrapper.properties
  92. 31
      gradlew
  93. 4
      gradlew.bat

4
.drone.yml

@ -3,7 +3,7 @@ name: default
steps:
- name: assembleRelease
image: nextcloudci/android:android-51
image: nextcloudci/android10:android-56
commands:
- gradle assembleRelease
- ./gradlew assembleRelease

18
README.md

@ -1,17 +1,21 @@
[![Build Status](https://drone.mosad.xyz/api/badges/Seil0/ProjectLaogai/status.svg)](https://drone.mosad.xyz/Seil0/ProjectLaogai)
![fdroid version](https://img.shields.io/f-droid/v/org.mosad.seil0.projectlaogai.svg?style=flat-square)
[![License: GPL v3](https://img.shields.io/badge/License-GPL%20v3-blue.svg?style=flat-square)](https://www.gnu.org/licenses/gpl-3.0)
# ProjectLaogai "hso App"
ProjectLaogai is a app to access the timetable and the mensa menu of Hochschule Offenburg.
# Project Laogai
Project Laogai is an app to access the timetable, grades (qispos) and the canteen menu of Hochschule Offenburg. Laogai uses the TCoR-API fot timetables and the canteen menu, wich makes acessing them super fast. To get the grades from Qispos, Laogai will ask for your login data, wich are stored encrypted on your device.
[<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png" height="75">](https://f-droid.org/packages/org.mosad.seil0.projectlaogai/)
[<img src="https://play.google.com/intl/en_us/badges/static/images/badges/en_badge_web_generic.png" height="75">](https://play.google.com/store/apps/details?id=org.mosad.seil0.projectlaogai)
## Features
* check out the mensa menu of this and next week
* access your timetable
* have your grades displayed directly in the app
* show the timetable of your course
* take a look at the canteen menu for the current and next week
* check the current balance of your mensa card
* open moodle
* probably some funny bugs
* open moodle directly in the app
Please report bugs and issues to support@mosad.xyz
## Screenshots
[<img src="https://www.mosad.xyz/images/Project_Laogai/ProjectLaogai_HomeScreen.png" width=180>](https://www.mosad.xyz/images/Project_Laogai/ProjectLaogai_HomeScreen.png)
@ -20,4 +24,4 @@ ProjectLaogai is a app to access the timetable and the mensa menu of Hochschule
[<img src="https://www.mosad.xyz/images/Project_Laogai/ProjectLaogai_Settings.png" width=180>](https://www.mosad.xyz/images/Project_Laogai/ProjectLaogai_Settings.png)
[<img src="https://www.mosad.xyz/images/Project_Laogai/ProjectLaogai_Mensa_dark.png" width=180>](https://www.mosad.xyz/images/Project_Laogai/ProjectLaogai_Mensa_dark.png)
ProjectLaogai © 2019-2020 [@Seil0](https://git.mosad.xyz/Seil0), a [mosad](http://www.mosad.xyz) Project
ProjectLaogai © 2019-2020 [@Seil0](https://git.mosad.xyz/Seil0), a [mosad.xyz](http://www.mosad.xyz) Project

34
app/build.gradle

@ -10,8 +10,8 @@ android {
applicationId "org.mosad.seil0.projectlaogai"
minSdkVersion 23
targetSdkVersion 29
versionCode 15
versionName "0.5.1"
versionCode 6000 // 0006000
versionName "0.6.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
resValue "string", "build_time", buildTime()
setProperty("archivesBaseName", "projectlaogai-$versionName")
@ -19,8 +19,8 @@ android {
buildTypes {
release {
minifyEnabled false
shrinkResources false
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
@ -47,23 +47,27 @@ android {
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.3'
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.8'
implementation 'androidx.core:core:1.3.1'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta4'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.0.0'
implementation 'com.google.android.material:material:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.0'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
implementation 'androidx.security:security-crypto:1.1.0-alpha02'
implementation 'com.google.android.material:material:1.2.0'
implementation 'com.google.code.gson:gson:2.8.6'
implementation 'com.afollestad:aesthetic:1.0.0-beta05'
implementation 'com.afollestad.material-dialogs:core:3.1.1'
implementation 'com.afollestad.material-dialogs:color:3.1.1'
implementation 'com.afollestad.material-dialogs:core:3.3.0'
implementation 'com.afollestad.material-dialogs:color:3.3.0'
implementation 'com.afollestad.material-dialogs:bottomsheets:3.3.0'
implementation 'de.psdev.licensesdialog:licensesdialog:2.1.0'
implementation 'org.apache.commons:commons-lang3:3.9'
implementation 'org.apache.commons:commons-lang3:3.11'
implementation 'org.jsoup:jsoup:1.13.1'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
testImplementation 'junit:junit:4.13'
androidTestImplementation 'androidx.test:core:1.2.0'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
}
static def buildTime() {

9
app/proguard-rules.pro vendored

@ -15,7 +15,16 @@
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
-dontobfuscate
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
-keep class org.mosad.seil0.projectlaogai.util.** { <fields>; }
#Gson
-keepattributes Signature
-dontwarn sun.misc.**
#misc
-dontwarn java.lang.instrument.ClassFileTransformer

6
app/src/androidTest/java/org/mosad/seil0/projectlaogai/ExampleInstrumentedTest.kt

@ -1,7 +1,7 @@
package org.mosad.seil0.projectlaogai
import androidx.test.InstrumentationRegistry
import androidx.test.runner.AndroidJUnit4
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import org.junit.Test
import org.junit.runner.RunWith
@ -18,7 +18,7 @@ class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getTargetContext()
val appContext = InstrumentationRegistry.getInstrumentation().context
assertEquals("org.mosad.seil0.projectlaogai", appContext.packageName)
}
}

8
app/src/main/AndroidManifest.xml

@ -28,6 +28,14 @@
android:resource="@xml/shortcuts" />
</activity>
<activity
android:name=".OnboardingActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme.Light"
android:screenOrientation="portrait"
android:launchMode="singleTop">
</activity>
<activity
android:name=".MainActivity"
android:label="@string/app_name"

BIN
app/src/main/ic_laogai_icon-playstore.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
app/src/main/ic_laogai_icon-web.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

29
app/src/main/java/org/mosad/seil0/projectlaogai/MainActivity.kt

@ -31,6 +31,7 @@ import android.nfc.NfcManager
import android.nfc.tech.NfcA
import android.os.Bundle
import android.util.Log
import android.util.TypedValue
import android.view.Menu
import android.view.MenuItem
import androidx.appcompat.app.ActionBarDrawerToggle
@ -43,11 +44,12 @@ import com.afollestad.aesthetic.NavigationViewMode
import com.google.android.material.navigation.NavigationView
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.app_bar_main.*
import org.mosad.seil0.projectlaogai.controller.CacheController
import org.mosad.seil0.projectlaogai.controller.NFCMensaCard
import org.mosad.seil0.projectlaogai.controller.PreferencesController
import org.mosad.seil0.projectlaogai.controller.PreferencesController.Companion.cColorAccent
import org.mosad.seil0.projectlaogai.controller.PreferencesController.Companion.cColorPrimary
import org.mosad.seil0.projectlaogai.controller.cache.CacheController
import org.mosad.seil0.projectlaogai.controller.preferences.EncryptedPreferences
import org.mosad.seil0.projectlaogai.controller.preferences.Preferences
import org.mosad.seil0.projectlaogai.controller.preferences.Preferences.cColorAccent
import org.mosad.seil0.projectlaogai.controller.preferences.Preferences.cColorPrimary
import org.mosad.seil0.projectlaogai.fragments.*
import kotlin.system.measureTimeMillis
@ -86,7 +88,7 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
nav_view.setNavigationItemSelectedListener(this)
// based on the inent we get, call readBalance or open a Fragment
// based on the intent we get, call readBalance or open a Fragment
when (intent.action) {
NfcAdapter.ACTION_TECH_DISCOVERED -> NFCMensaCard.readBalance(intent, this)
"org.mosad.seil0.projectlaogai.fragments.MensaFragment" -> activeFragment = MensaFragment()
@ -154,6 +156,7 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
R.id.nav_mensa -> MensaFragment()
R.id.nav_timetable -> TimeTableFragment()
R.id.nav_moodle -> MoodleFragment()
R.id.nav_grades -> GradesFragment()
R.id.nav_settings -> SettingsFragment()
else -> HomeFragment()
}
@ -172,8 +175,9 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
*/
private fun load() {
val startupTime = measureTimeMillis {
PreferencesController.load(this) // load the settings, must be finished before doing anything else
Preferences.load(this) // load the settings, must be finished before doing anything else
CacheController(this) // load the cache
EncryptedPreferences.load(this)
}
Log.i(className, "startup completed in $startupTime ms")
}
@ -181,13 +185,15 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
private fun initAesthetic() {
// If we haven't set any defaults, do that now
if (Aesthetic.isFirstTime) {
// this is executed on the first app start, use this to show tutorial etc.
// set the default theme at the first app start
Aesthetic.config {
activityTheme(R.style.AppTheme_Light)
apply()
}
SettingsFragment().selectCourse(this) // FIXME this is not working
// show the onboarding activity
startActivity(Intent(this, OnboardingActivity::class.java))
finish()
}
Aesthetic.config {
@ -198,6 +204,13 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
apply()
}
// set theme color values
val out = TypedValue()
this.theme.resolveAttribute(R.attr.themePrimary, out, true)
Preferences.themePrimary = out.data
this.theme.resolveAttribute(R.attr.themeSecondary, out, true)
Preferences.themeSecondary = out.data
}
private fun initForegroundDispatch() {

145
app/src/main/java/org/mosad/seil0/projectlaogai/OnboardingActivity.kt

@ -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("&#8226;", 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) {}
}
}

177
app/src/main/java/org/mosad/seil0/projectlaogai/controller/CacheController.kt

@ -1,177 +0,0 @@
/**
* 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 com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.JsonParser
import com.google.gson.reflect.TypeToken
import kotlinx.coroutines.*
import org.mosad.seil0.projectlaogai.controller.PreferencesController.Companion.cCourse
import org.mosad.seil0.projectlaogai.hsoparser.Course
import org.mosad.seil0.projectlaogai.hsoparser.MensaMenu
import org.mosad.seil0.projectlaogai.hsoparser.TimetableCourseWeek
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 className = "CacheController"
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) {
Log.i(className, "update mensa blocking")
GlobalScope.launch(Dispatchers.Default) { TCoRAPIController.getMensa(context).join() }
}
// 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) {
Log.i(className, "updating timetable after sunday!")
GlobalScope.launch(Dispatchers.Default) {
val threads = listOf(
TCoRAPIController.getTimetable(cCourse.courseName, 0, context),
TCoRAPIController.getTimetable(cCourse.courseName, 1, context)
)
threads.joinAll()
}
TCoRAPIController.getTimetable(cCourse.courseName, 0, context)
TCoRAPIController.getTimetable(cCourse.courseName, 1, context)
}
// 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())
runBlocking { TCoRAPIController.getCoursesList(context).join() }
val fileReader = FileReader(file)
val bufferedReader = BufferedReader(fileReader)
val coursesObject = JsonParser.parseString(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())
runBlocking { TCoRAPIController.getMensa(context).join() }
val fileReader = FileReader(file)
val bufferedReader = BufferedReader(fileReader)
val mensaObject = JsonParser.parseString(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())
runBlocking { TCoRAPIController.getTimetable(courseName, week, context).join() }
val fileReader = FileReader(file)
val bufferedReader = BufferedReader(fileReader)
val timetableObject = JsonParser.parseString(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)
*/
private fun readStartCache(courseName: String) {
readCoursesList(context)
readMensa(context)
readTimetable(courseName, 0, context)
readTimetable(courseName, 1, context)
}
}

5
app/src/main/java/org/mosad/seil0/projectlaogai/controller/NFCMensaCard.kt

@ -35,6 +35,7 @@ import com.codebutler.farebot.card.desfire.DesfireFileSettings
import com.codebutler.farebot.card.desfire.DesfireProtocol
import kotlinx.android.synthetic.main.dialog_mensa_credit.*
import org.mosad.seil0.projectlaogai.R
import org.mosad.seil0.projectlaogai.controller.preferences.Preferences
import java.lang.Exception
class NFCMensaCard {
@ -84,13 +85,13 @@ class NFCMensaCard {
val dialog = MaterialDialog(context)
.customView(R.layout.dialog_mensa_credit)
val current = if (!PreferencesController.oGiants) {
val current = if (!Preferences.oGiants) {
String.format("%.2f €", (currentRaw.toFloat() / 1000))
} else {
String.format("%.4f shm", (currentRaw.toFloat() * 0.0000075))
}
val last = if (!PreferencesController.oGiants) {
val last = if (!Preferences.oGiants) {
String.format("%.2f €", (lastRaw.toFloat() / 1000))
} else {
String.format("%.4f shm", (lastRaw.toFloat() * 0.0000075))

183
app/src/main/java/org/mosad/seil0/projectlaogai/controller/PreferencesController.kt

@ -1,183 +0,0 @@
/**
* 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.graphics.Color
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.getSharedPreferences(
context.getString(R.string.preference_file_key),
Context.MODE_PRIVATE
)
// 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 locally
*/
fun saveCourse(context: Context, course: Course) {
val sharedPref = context.getSharedPreferences(
context.getString(R.string.preference_file_key),
Context.MODE_PRIVATE
)
with (sharedPref.edit()) {
putString(context.getString(R.string.save_key_course), course.courseName)
putString(context.getString(R.string.save_key_courseTTLink), course.courseLink)
apply()
}
cCourse = course
}
/**
* save the primary color
*/
fun saveColorPrimary(context: Context, colorPrimary: Int) {
val sharedPref = context.getSharedPreferences(
context.getString(R.string.preference_file_key),
Context.MODE_PRIVATE
)
with (sharedPref.edit()) {
putInt(context.getString(R.string.save_key_colorPrimary),
colorPrimary
)
apply()
}
cColorPrimary = colorPrimary
}
/**
* save the accent color
*/
fun saveColorAccent(context: Context, colorAccent: Int) {
val sharedPref = context.getSharedPreferences(
context.getString(R.string.preference_file_key),
Context.MODE_PRIVATE
)
with (sharedPref.edit()) {
putInt(context.getString(R.string.save_key_colorAccent),
colorAccent
)
apply()
}
cColorAccent = colorAccent
}
/**
* save showBuffet
*/
fun saveShowBuffet(context: Context, showBuffet: Boolean) {
val sharedPref = context.getSharedPreferences(
context.getString(R.string.preference_file_key),
Context.MODE_PRIVATE
)
with (sharedPref.edit()) {
putBoolean(context.getString(R.string.save_key_showBuffet),
showBuffet
)
apply()
}
cShowBuffet = showBuffet
}
// the load function
fun load(context: Context) {
val sharedPref = context.getSharedPreferences(
context.getString(R.string.preference_file_key),
Context.MODE_PRIVATE
)
// 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)
}
}
}

236
app/src/main/java/org/mosad/seil0/projectlaogai/controller/QISPOSParser.kt

@ -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
}
}
}
}
/**
* since the HS has a fucked up tls setup we need to work around that
*/
private fun createSSLSocketFactory(): SSLSocketFactory {
// Load CAs from an InputStream
// (could be from a resource or ByteArrayInputStream or ...)
val cf: CertificateFactory = CertificateFactory.getInstance("X.509")
val caInput = context.resources.openRawResource(R.raw.notenverwaltung_hs_offenburg_de)
val ca = caInput.use {
cf.generateCertificate(it) as X509Certificate
}
// Create a KeyStore containing our trusted CAs
val keyStoreType = KeyStore.getDefaultType()
val keyStore = KeyStore.getInstance(keyStoreType).apply {
load(null, null)
setCertificateEntry("ca", ca)
}
// Create a TrustManager that trusts the CAs inputStream our KeyStore
val tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm()
val tmf = TrustManagerFactory.getInstance(tmfAlgorithm).apply {
init(keyStore)
}
// Create an SSLContext that uses our TrustManager
val sslContext = SSLContext.getInstance("TLS").apply {
init(null, tmf.trustManagers, null)
}
return sslContext.socketFactory
}
}

136
app/src/main/java/org/mosad/seil0/projectlaogai/controller/TCoRAPIController.kt

@ -22,105 +22,95 @@
package org.mosad.seil0.projectlaogai.controller
import android.content.Context
import android.util.Log
import com.google.gson.Gson
import com.google.gson.JsonParser
import com.google.gson.reflect.TypeToken
import kotlinx.coroutines.*
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.BufferedWriter
import java.io.File
import java.io.FileWriter
import org.mosad.seil0.projectlaogai.util.*
import java.net.URL
import kotlin.Exception
/**
* This Controller calls the tcor api,
* all functions return tcor api objects
*/
class TCoRAPIController {
companion object {
private const val className = "TCoRAPIController"
private const val tcorBaseURL = "https://tcor.mosad.xyz"
/**
* get the json object from tcor api and write it as file (cache)
* Get a array of all currently available courses at the tcor API.
* Read the json object from tcor api
*/
fun getCoursesList(context: Context) = GlobalScope.launch {
try {
val url = URL("$tcorBaseURL/courseList")
val file = File(context.filesDir, "courses.json")