save grades to a encrypted cache file, use cache if not older than 24hr
continuous-integration/drone/push Build is passing Details

This commit is contained in:
Jannik 2020-08-28 21:38:44 +02:00
parent 313ff4741a
commit d94f38de93
Signed by: Seil0
GPG Key ID: E8459F3723C52C24
7 changed files with 243 additions and 146 deletions

View File

@ -51,7 +51,7 @@ dependencies {
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'
implementation 'androidx.constraintlayout:constraintlayout:2.0.1'
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'
@ -66,7 +66,7 @@ dependencies {
implementation 'org.jsoup:jsoup:1.13.1'
testImplementation 'junit:junit:4.13'
androidTestImplementation 'androidx.test:core:1.2.0'
androidTestImplementation 'androidx.test:core:1.3.0'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
}

View File

@ -24,21 +24,26 @@ package org.mosad.seil0.projectlaogai.controller.cache
import android.content.Context
import android.util.Log
import androidx.security.crypto.EncryptedFile
import androidx.security.crypto.MasterKey
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.QISPOSParser
import org.mosad.seil0.projectlaogai.controller.preferences.Preferences
import org.mosad.seil0.projectlaogai.controller.TCoRAPIController
import org.mosad.seil0.projectlaogai.controller.preferences.Preferences.cCourse
import org.mosad.seil0.projectlaogai.controller.preferences.Preferences.coursesCacheTime
import org.mosad.seil0.projectlaogai.controller.preferences.Preferences.gradesCacheTime
import org.mosad.seil0.projectlaogai.controller.preferences.Preferences.mensaCacheTime
import org.mosad.seil0.projectlaogai.controller.preferences.Preferences.timetableCacheTime
import org.mosad.seil0.projectlaogai.util.*
import java.io.*
import java.util.*
import kotlin.collections.ArrayList
import kotlin.collections.HashMap
/**
* The cacheController reads and updates the cache files.
@ -201,6 +206,25 @@ class CacheController(cont: Context) {
}
}
/**
* update the encrypted grades file
*/
fun updateGrades(context: Context): Job {
val file = File(context.filesDir, "grades_encrypted")
val parser = QISPOSParser(context)
return GlobalScope.launch(Dispatchers.IO) {
if (parser.checkQISPOSStatus() == 200) {
if (file.exists()) { file.delete() }
// save cache file and update time
saveEncrypted(context, file, Gson().toJson(parser.parseGrades()))
gradesCacheTime = System.currentTimeMillis() / 1000
Preferences.save(context)
}
}
}
/**
* save changes in lessonMap and subjectMap,
* called on addSubject or removeSubject
@ -223,6 +247,22 @@ class CacheController(cont: Context) {
}
}
private fun saveEncrypted(context: Context, file: File, text: String) {
val masterKey = MasterKey.Builder(context)
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
.build()
val encryptedFile = EncryptedFile.Builder(
context,
file,
masterKey,
EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB
).build()
encryptedFile.openFileOutput().bufferedWriter().use {
it.write(text)
}
}
}
/**
@ -372,4 +412,40 @@ class CacheController(cont: Context) {
}
/**
* read the encrypted grades file, don't keep them
* in CacheController for security reasons
* @return the grades as HashMap if the file exists, else a empty HashMap
*/
fun readGrades(): HashMap<String, ArrayList<GradeSubject>> {
val file = File(context.filesDir, "grades_encrypted")
// if the file does not exit, try creating it
if (!file.exists()) {
runBlocking { updateGrades(context) }
}
if (file.exists()) {
val masterKey = MasterKey.Builder(context)
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
.build()
val encryptedFile = EncryptedFile.Builder(
context,
file,
masterKey,
EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB
).build()
return encryptedFile.openFileInput().bufferedReader().use {
GsonBuilder().create()
.fromJson(
it.readLine(),
object : TypeToken<HashMap<String, ArrayList<GradeSubject>>>() {}.type
)
}
}
return HashMap()
}
}

View File

@ -38,9 +38,8 @@ import org.mosad.seil0.projectlaogai.util.TimetableWeek
* * add second week
* * add configurable week to addSubject() and removeSubject(), updateAdditionalLessons()
*/
class TimetableController {
object TimetableController {
companion object {
val timetable = ArrayList<TimetableWeek>()
val lessonMap = HashMap<String, Lesson>() // the key is courseName-subject-lessonID
val subjectMap = HashMap<String, ArrayList<String>>() // the key is courseName
@ -126,6 +125,4 @@ class TimetableController {
timetable[0].days[id[0].toInt()].timeslots[id[1].toInt()].add(lesson)
}
}
}

View File

@ -36,6 +36,7 @@ object Preferences {
var coursesCacheTime: Long = 0
var mensaCacheTime: Long = 0
var timetableCacheTime: Long = 0
var gradesCacheTime: Long = 0
var cColorPrimary: Int = Color.parseColor("#009688")
var cColorAccent: Int = Color.parseColor("#0096ff")
var cCourse = Course(
@ -67,6 +68,9 @@ object Preferences {
putLong(context.getString(R.string.save_key_timetableCacheTime),
timetableCacheTime
)
putLong(context.getString(R.string.save_key_gradesCacheTime),
gradesCacheTime
)
apply()
}
@ -166,6 +170,9 @@ object Preferences {
timetableCacheTime = sharedPref.getLong(context.getString(
R.string.save_key_timetableCacheTime
), 0)
gradesCacheTime = sharedPref.getLong(context.getString(
R.string.save_key_gradesCacheTime
), 0)
// load saved course
cCourse = Course(

View File

@ -35,11 +35,13 @@ import kotlinx.android.synthetic.main.fragment_grades.*
import kotlinx.coroutines.*
import org.mosad.seil0.projectlaogai.R
import org.mosad.seil0.projectlaogai.controller.QISPOSParser
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.uicomponents.DayCardView
import org.mosad.seil0.projectlaogai.uicomponents.GradeLinearLayout
import org.mosad.seil0.projectlaogai.uicomponents.dialogs.LoginDialog
import kotlin.system.measureTimeMillis
/**
* The grades fragment class
@ -64,6 +66,7 @@ class GradesFragment : Fragment() {
parser = QISPOSParser(context!!)// init the parser
// TODO if loading from cache, don't check Qispos state
if (checkCredentials() && checkQisposStatus()) {
GlobalScope.launch(Dispatchers.Default) {
addGrades()
@ -125,18 +128,30 @@ class GradesFragment : Fragment() {
return statusCode == 200
}
// add the grades to the layout, async
/**
* add the grades to the layout, async
* TODO this is slow as hell
*/
private fun addGrades() = GlobalScope.launch(Dispatchers.Default) {
val addGradesTime = measureTimeMillis {
withContext(Dispatchers.Main) {
refreshLayout_Grades.isRefreshing = true
}
val grades = parser.parseGrades()
// if the cache is older than 24hr, update blocking
val currentTime = System.currentTimeMillis() / 1000
if ((currentTime - Preferences.gradesCacheTime) > 86400) {
CacheController.updateGrades(context!!).join()
}
val grades = CacheController(context!!).readGrades()
withContext(Dispatchers.Main) {
linLayout_Grades.removeAllViews() // clear layout
}
// TODO this loop takes 3/4 of the time
// for each semester add a new card
grades.forEach { semester ->
val usedSubjects = ArrayList<String>()
@ -183,8 +198,6 @@ class GradesFragment : Fragment() {
}
}
val txtViewLegal = TextView(context).apply {
text = resources.getString(R.string.without_guarantee)
textAlignment = View.TEXT_ALIGNMENT_CENTER
@ -195,6 +208,9 @@ class GradesFragment : Fragment() {
linLayout_Grades.addView(txtViewLegal)
refreshLayout_Grades.isRefreshing = false
}
}
println("startup completed in $addGradesTime ms")
}
}

View File

@ -33,7 +33,7 @@ import kotlinx.android.synthetic.main.fragment_timetable.*
import kotlinx.coroutines.*
import org.mosad.seil0.projectlaogai.R
import org.mosad.seil0.projectlaogai.controller.cache.TimetableController
import org.mosad.seil0.projectlaogai.controller.cache.TimetableController.Companion.timetable
import org.mosad.seil0.projectlaogai.controller.cache.TimetableController.timetable
import org.mosad.seil0.projectlaogai.controller.preferences.Preferences
import org.mosad.seil0.projectlaogai.uicomponents.dialogs.AddSubjectDialog
import org.mosad.seil0.projectlaogai.uicomponents.DayCardView

View File

@ -127,6 +127,7 @@
<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>
<string name="save_key_gradesCacheTime" translatable="false">org.mosad.seil0.projectlaogai.gradesCacheTime</string>
<string name="save_key_user_email" translatable="false">org.mosad.seil0.projectlaogai.user_email</string>
<string name="save_key_user_password" translatable="false">org.mosad.seil0.projectlaogai.user_password</string>