/** * ProjectLaogai * * Copyright 2019-2020 * * 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.fragments import android.graphics.Rect import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.TextView import androidx.core.content.res.ResourcesCompat import androidx.fragment.app.Fragment 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.databinding.FragmentGradesBinding 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 * contains all needed parts to display and the grades screen */ class GradesFragment : Fragment() { private lateinit var parser: QISPOSParser private lateinit var binding: FragmentGradesBinding override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { binding = FragmentGradesBinding.inflate(inflater, container, false) return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding.refreshLayoutGrades.setProgressBackgroundColorSchemeColor(Preferences.themeSecondary) initActions() parser = QISPOSParser(context!!)// init the parser if (checkCredentials()) { GlobalScope.launch(Dispatchers.Default) { // if the cache is older than 24hr, update blocking val currentTime = System.currentTimeMillis() / 1000 withContext(Dispatchers.Main) { if ((currentTime - Preferences.gradesCacheTime) > 86400 && checkQisposStatus()) { binding.refreshLayoutGrades.isRefreshing = true CacheController.updateGrades(context!!).join() } } addGrades() } } } /** * initialize the actions */ private fun initActions() = GlobalScope.launch(Dispatchers.Default) { binding.refreshLayoutGrades.setOnRefreshListener { binding.linLayoutGrades.removeAllViews() // clear layout // TODO add loading textView GlobalScope.launch(Dispatchers.Default) { CacheController.updateGrades(context!!).join() addGrades() } } } /** * check if the credentials are set, if not show a login dialog */ private fun checkCredentials(): Boolean { val credentials = EncryptedPreferences.readCredentials(context!!) var credentialsPresent = false // if there is no password set, show the login dialog if (credentials.first.isEmpty() || credentials.second.isEmpty()) { LoginDialog(this.context!!) .positiveButton { EncryptedPreferences.saveCredentials(email, password, context) addGrades() } .negativeButton { binding.txtViewLoading.text = resources.getString(R.string.credentials_missing) } .show { email = EncryptedPreferences.email password = "" } } else { credentialsPresent = true } return credentialsPresent } /** * check if qispos is available, if not show an error */ private fun checkQisposStatus(): Boolean { val statusCode = parser.checkQISPOSStatus() // show error if the status code is not 200 if (statusCode != 200) { val infoText = resources.getString(when(statusCode) { 503 -> R.string.qispos_unavailable else -> R.string.qispos_generic_error }) val img = ResourcesCompat.getDrawable(resources, R.drawable.ic_error_outline_black_24dp, null)?.apply { bounds = Rect(0, 0, 75, 75) } binding.txtViewLoading.apply { text = infoText setCompoundDrawables(null, null, null, img) } } return statusCode == 200 } /** * add the grades to the layout, async * TODO this is slow as hell */ private fun addGrades() = GlobalScope.launch(Dispatchers.Default) { val addGradesTime = measureTimeMillis { val grades = CacheController(context!!).readGrades() withContext(Dispatchers.Main) { binding.linLayoutGrades.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() val semesterCard = DayCardView(context!!) semesterCard.setDayHeading(semester.key) // for each subject add a new linLayout semester.value.forEachIndexed { index, subject -> if (usedSubjects.contains(subject.name)) { return@forEachIndexed } // get the first sub subjects val subSubject = semester.value.firstOrNull { it.name.contains(subject.name) && it.name != subject.name } // if sub subject is not null, add it to used subjects subSubject?.let { usedSubjects.add(it.name) } val subjectLayout = GradeLinearLayout(context).set { subjectName = subject.name grade = subject.grade subSubjectName = subSubject?.name.toString() subGrade = subSubject?.grade.toString() } // disable sub-subject if not set if (subSubject == null) subjectLayout.disableSubSubject() // disable divider if last element if (index == semester.value.lastIndex || semester.value.indexOf(subSubject) == semester.value.lastIndex) subjectLayout.disableDivider() semesterCard.getLinLayoutDay().addView(subjectLayout) } // without context we can't access the view withContext(Dispatchers.Main) { binding.linLayoutGrades.addView(semesterCard) } } val txtViewLegal = TextView(context).apply { text = resources.getString(R.string.without_guarantee) textAlignment = View.TEXT_ALIGNMENT_CENTER } // stop refreshing and show legal warning withContext(Dispatchers.Main) { binding.linLayoutGrades.addView(txtViewLegal) binding.refreshLayoutGrades.isRefreshing = false } } println("added grades in $addGradesTime ms") } }