started working on the grand refactor (the app launches)

This commit is contained in:
Odweta
2024-12-20 20:29:19 +01:00
parent f9d4ff562d
commit 89dd624b00
11 changed files with 284 additions and 686 deletions
@@ -27,7 +27,7 @@ class Account(val username: String, val password: String, private val error: Int
*/ */
// check if file exists // check if file exists
val file = File(fileDir, accountDataFilePath) val file = File(global.fileDir, accountDataFilePath)
return if (!file.exists()) { return if (!file.exists()) {
// it does not exist // it does not exist
@@ -36,15 +36,15 @@ class Account(val username: String, val password: String, private val error: Int
} else { } else {
// it exists, so it shall be parsed // it exists, so it shall be parsed
val acc: Account = parseAccountDataFile(file) val acc: Account = parseAccountDataFile(file)
username = acc.username global.username = acc.username
password = acc.password global.password = acc.password
acc.error == 0 acc.error == 0
} }
} }
fun save(acc: Account): Boolean { fun save(acc: Account): Boolean {
return try { return try {
val file = File(fileDir, accountDataFilePath) val file = File(global.fileDir, accountDataFilePath)
file.writeText("${acc.username}\n${acc.password}\n", Charset.forName("UTF-8")) file.writeText("${acc.username}\n${acc.password}\n", Charset.forName("UTF-8"))
true true
@@ -55,8 +55,8 @@ class Account(val username: String, val password: String, private val error: Int
suspend fun isValid(acc: Account): Boolean { suspend fun isValid(acc: Account): Boolean {
return withContext(Dispatchers.IO) { return withContext(Dispatchers.IO) {
username = acc.username global.username = acc.username
password = acc.password global.password = acc.password
val token = Network.getToken() val token = Network.getToken()
!JSONObject(token).has("error") !JSONObject(token).has("error")
@@ -45,10 +45,8 @@ const val userDataFilePath: String = "userdata.json"
const val marksFilePath: String = "marks.json" const val marksFilePath: String = "marks.json"
const val settingsDataFilePath: String = "settings.txt" const val settingsDataFilePath: String = "settings.txt"
const val lectureNotesFilePath: String = "lecture_notes.csv" const val lectureNotesFilePath: String = "lecture_notes.csv"
lateinit var fileDir: File
var width: Int = 0 var width: Int = 0
var height: Int = 0 var height: Int = 0
var internetAvail: Boolean = false
var loaded: Boolean = false var loaded: Boolean = false
@@ -137,7 +135,6 @@ val dayMap = mutableMapOf<Int, Day>()
val dayList = mutableListOf<Day>() val dayList = mutableListOf<Day>()
var days = listOf<Day>() var days = listOf<Day>()
var marksJSON: String = ""
var marks: MutableMap<String, MutableList<Mark>> = mutableMapOf() var marks: MutableMap<String, MutableList<Mark>> = mutableMapOf()
var username: String = "" var username: String = ""
@@ -145,10 +142,6 @@ var password: String = ""
val client = OkHttpClient() val client = OkHttpClient()
var resetNav: Boolean = false
var isAPISetUp: Boolean = false
enum class LectureNoteType { enum class LectureNoteType {
Normal, // obycejna poznamka Normal, // obycejna poznamka
Writing, // pisemny test Writing, // pisemny test
@@ -203,10 +196,10 @@ const val tokenUrl = "$apiBaseUrl/connect/token"
var token: String = "" var token: String = ""
const val userDataUrl = "$apiBaseUrl/v1/user" const val userDataUrl = "$apiBaseUrl/v1/user"
var userData: String = "" var userData: String = ""
var studentId: String = "" //var studentId: String = ""
const val timeTableUrl = "$apiBaseUrl/v1/timeTable" const val timeTableUrl = "$apiBaseUrl/v1/timeTable"
var syid = "" // school year id var syid = "" // school year id
var marksUrl = "$apiBaseUrl/v1/students/$studentId/marks/list?SemesterId=$syid" var marksUrl = "$apiBaseUrl/v1/students/abc/marks/list?SemesterId=$syid"
class Subject { class Subject {
lateinit var name: String lateinit var name: String
@@ -284,44 +277,68 @@ class Mark {
} }
} }
object FilePaths { enum class ScreenElement {
const val accountData: String = "account_data.txt"
const val timeTable: String = "timetable.json"
const val userData: String = "userdata.json"
const val marks: String = "marks.json"
const val settings: String = "settings.txt"
const val lectureNotes: String = "lecture_notes.csv"
}
object APIEndpoints {
const val apiBaseUrl = "https://aplikace.skolaonline.cz/solapi/api"
const val tokenUrl = "$apiBaseUrl/connect/token"
lateinit var token: String
const val userDataUrl = "$apiBaseUrl/v1/user"
lateinit var userData: String
lateinit var studentId: String
const val timeTableUrl = "$apiBaseUrl/v1/timeTable"
var marksUrl = "$apiBaseUrl/v1/students/$studentId/marks/list?SemesterId=$syid"
}
enum class MainElement {
Timetable, Timetable,
Marks, Marks,
Menu Menu,
About,
Settings
} }
object Global { class Global {
lateinit var token: String lateinit var token: String
lateinit var userData: JSONObject lateinit var userData: JSONObject
lateinit var timetable: List<Day> lateinit var timetable: List<Day>
lateinit var timetableJSON: JSONObject
lateinit var marks: MutableMap<String, MutableList<Mark>> lateinit var marks: MutableMap<String, MutableList<Mark>>
var noSignInBackHandle: Boolean = false lateinit var marksJSON: JSONObject
val filePaths: FilePaths = FilePaths lateinit var studentClass: String
val apiEndPoints: APIEndpoints = APIEndpoints
lateinit var settings: SettingsData lateinit var settings: SettingsData
lateinit var studentId: String
lateinit var fileDir: File
var username: String = ""
var password: String = ""
var isAPISetUp: Boolean = false
var noSignInBackHandle: Boolean = false
var lectureNotes: HashMap<Int, LectureNote> = hashMapOf() var lectureNotes: HashMap<Int, LectureNote> = hashMapOf()
var currentScreen by mutableStateOf(Screen.Main) var currentScreen by mutableStateOf(Screen.Main)
var currentMainElement by mutableStateOf(MainElement.Timetable) var currentScreenElement by mutableStateOf(ScreenElement.Timetable)
inner class FilePaths {
val accountData: String = "account_data.txt"
val timeTable: String = "timetable.json"
val userData: String = "userdata.json"
val marks: String = "marks.json"
val settings: String = "settings.txt"
val lectureNotes: String = "lecture_notes.csv"
}
val filePaths = FilePaths()
inner class APIEndPoints {
val apiBaseUrl = "https://aplikace.skolaonline.cz/solapi/api"
val tokenUrl = "$apiBaseUrl/connect/token"
lateinit var token: String
val userDataUrl = "$apiBaseUrl/v1/user"
lateinit var userData: String
val timeTableUrl = "$apiBaseUrl/v1/timeTable"
lateinit var marksUrl: String
}
val apiEndPoints = APIEndPoints()
inner class Dimens {
val borderWidth = 5.dp
val roundedCorner = 8.dp
}
val dimens = Dimens()
inner class Modifiers {
var bar = Modifier
.fillMaxWidth()
.height(100.dp)
.padding(20.dp)
.border(
width = Dimens().borderWidth,
color = PastelYellow,
shape = RoundedCornerShape(Dimens().roundedCorner)
)
}
val modifiers = Modifiers()
} }
val global = Global val global = Global()
@@ -1,308 +1,112 @@
package com.odweta.solon package com.odweta.solon
import android.content.Context import android.content.Context
import android.widget.Toast import android.util.Log
import androidx.compose.ui.graphics.Color
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import org.json.JSONObject import org.json.JSONObject
private var week: List<Day> = mutableListOf() fun launchStart(ctx: Context) {
private var toastShowing: Boolean = false if (Account.haveSaved()) {
private var toastShown: Boolean = false if (Util.internetAvail(ctx)) {
private var toast2Shown: Boolean = false remoteLoad(ctx)
} else {
localLoad(ctx)
}
} else {
global.noSignInBackHandle = true
screenToShow.value = Screen.SignIn
}
}
private lateinit var coscope_: CoroutineScope fun remoteLoad(ctx: Context) {
var remoteTimetable: JSONObject
var remoteMarks: JSONObject
if (!global.isAPISetUp) {
CoroutineScope(Dispatchers.IO)
.launch {
Network.setupAPI()
remoteTimetable = Network.getTimeTable()
global.timetableJSON = remoteTimetable
remoteMarks = Network.getMarks()
global.marksJSON = remoteMarks
private fun setOnInternetAvailListener(ctx: Context, coscope: CoroutineScope) { launchContinue(
ctx,
remoteTimetable,
remoteMarks
)
}
} else {
launchContinue(
ctx,
global.timetableJSON,
global.marksJSON
)
}
}
fun localLoad(ctx: Context) {
val localTimetable = JSONObject(Util.fileToString(timeTableFilePath))
val localMarks = JSONObject(Util.fileToString(marksFilePath))
launchContinue(
ctx,
localTimetable,
localMarks
)
}
fun launchContinue(
ctx: Context,
timetable: JSONObject,
marks: JSONObject
) {
// parse timetable and marks
val parsedTimetable = Parsing.parseTimetable(timetable)
global.timetable = parsedTimetable
val parsedMarks = Parsing.parseMarks(marks)
global.marks = parsedMarks
// load the settings
val settingsData = Util.fileToString(global.filePaths.settings).toSettingsData()
global.settings = settingsData
// load the lecture notes
val lectureNotes = Util.fileToString(global.filePaths.lectureNotes)
Parsing.parseLectureNotes(lectureNotes)
// listen for internet availability in case of disconnection
if (!internetAwaitListenerRunning)
setOnInternetAvailListener(ctx)
// show the default (main) screen
global.currentScreen = Screen.Main
// inside show the timetable
global.currentScreenElement = ScreenElement.Timetable
}
private fun setOnInternetAvailListener(ctx: Context) {
internetAwaitListenerRunning = true internetAwaitListenerRunning = true
coscope.launch { var internetAvail = Util.internetAvail(ctx)
CoroutineScope(Dispatchers.IO).launch {
while (true) { while (true) {
if (Util.internetAvail(ctx) && !internetAvail) { if (Util.internetAvail(ctx) && !internetAvail) {
internetAvail = Util.internetAvail(ctx) internetAvail = Util.internetAvail(ctx)
coscope.launch { CoroutineScope(Dispatchers.IO).launch {
fetchNewData(ctx, coscope) remoteLoad(ctx)
} }
} else if (!internetAvail) { } else if (!internetAvail) {
internetAvail = Util.internetAvail(ctx) internetAvail = Util.internetAvail(ctx)
toolbarText.value = "Bez připojení k internetu." showStatus("Bez připojení k internetu.")
} else { } else {
internetAvail = Util.internetAvail(ctx) internetAvail = Util.internetAvail(ctx)
} }
delay(1000) // Check for internet availability every second delay(1000) // Check for internet availability every second
} }
} }
}
fun fetchNewData(ctx: Context, coscope: CoroutineScope) {
coscope.launch {
done = false
// variables for the loading animation
val loadingArray: Array<String> = arrayOf(
"Načítání nových dat",
"Načítání nových dat.",
"Načítání nových dat..",
"Načítání nových dat...",
"Načítání nových dat..",
"Načítání nových dat."
)
val delayTime: Long = 300 // delay in ms
// show a loading animation
while (!done && Util.internetAvail(ctx)) {
for (text in loadingArray) {
if (done) break
toolbarText.value = text
delay(delayTime)
}
}
// determine the role of the student
val role = if (Parsing.userIsParent()) {
" Rodič"
} else {
" Student"
}
// display the full name of the user in the toolbar
toolbarText.value =
"${
JSONObject(
Util.fileToString(
userDataFilePath
)
).getString("fullName")}$role"
}
coscope.launch {
launchPhase1(ctx)
}
}
fun launchPhase0(ctx: Context, coscope: CoroutineScope) {
// start a sign in activity
// gather input and put it into global state (if login is needed)
// otherwise show the splash screen and load the app
/* check if there is a saved user account
*
* if yes, use that
* otherwise show the login activity
*/
coscope_ = coscope
fileDir = ctx.filesDir
width = Util.getDisplayWidth(ctx)
height = Util.getDisplayHeight(ctx)
val accSaved: Boolean = Account.haveSaved()
if (!accSaved) {
// show the sign in screen
screenToShow.value = Screen.SignIn
} else {
// show the splash/loading screen
screenToShow.value = Screen.Splash
coscope.launch {
// launch!
launchPhase1(ctx)
}
}
}
private suspend fun launchPhase1(ctx: Context) {
// TODO: CONVERT TO SNACKBARS
val toast: Toast = Toast.makeText(
ctx,
"Nelze načíst data, zkuste se připojit k internetu.",
Toast.LENGTH_LONG
)
val toast2: Toast = Toast.makeText(
ctx,
"Připojeno k internetu.",
Toast.LENGTH_SHORT
)
// check if the file is available offline,
// if yes, load the data and don't connect to the internet
internetAvail = Util.internetAvail(ctx)
timeTableAvailOffline =
Util.fileExists(timeTableFilePath) && !Util.fileIsEmpty(timeTableFilePath)
marksAvailOffline =
Util.fileExists(marksFilePath) && !Util.fileIsEmpty(marksFilePath)
if (toastShown && internetAvail && !toast2Shown) {
toast2Shown = true
toast2.show()
}
if (!timeTableAvailOffline || !marksAvailOffline || internetAvail) {
// set up the API (required for later uses of it)
if (!isAPISetUp) {
Network.setupAPI()
}
if (internetAvail) {
// cancel a toast, if any
if (toastShowing && !toastShown) {
toast.cancel()
}
// get the JSON data
var timeTableJSON = Network.getTimeTableJSON()
if (timeTableJSON == """{"error": "network"}""") {
timeTableJSON = Util.fileToString(timeTableFilePath)
}
// fetch and parse marks
marksJSON = Network.getMarksJSON()
if (marksJSON == """{"error": "network"}""") {
marksJSON = Util.fileToString(marksFilePath)
}
timeTableAvailOffline =
Util.saveStringToFile(timeTableFilePath, timeTableJSON)
marksAvailOffline =
Util.saveStringToFile(marksFilePath, marksJSON)
// parse the data
week = Parsing.parseTimeTableJSON(timeTableJSON)
marks = Parsing.parseMarksJSON(marksJSON)
// set the settings
if (Util.fileExists(settingsDataFilePath) && !Util.fileIsEmpty(settingsDataFilePath)) {
settings = Util.stringToSettingsData(Util.fileToString(settingsDataFilePath))
} // else load the default settings
// continue to launch phase 2
done = true
launchPhase2(ctx)
} else {
if (timeTableAvailOffline) {
// cancel a toast, if any
if (toastShowing && !toastShown) {
toast.cancel()
}
// load the offline data
val timeTableJSON = Util.fileToString(timeTableFilePath)
// parse the data
if (timeTableJSON != "") {
// cancel a toast, if any
if (toastShowing && !toastShown) {
toast.cancel()
}
// parse the data
week = Parsing.parseTimeTableJSON(timeTableJSON)
// continue launching
done = true
launchPhase2(ctx)
} else {
// make a toast or something
toast.show()
// try to reconnect to the internet
launchPhase1(ctx)
}
} else {
// make a toast or something
if (!toastShown) {
toast.show()
toastShowing = true
toastShown = true
}
// try to reconnect to the internet
launchPhase1(ctx)
}
}
} else {
// load offline
// cancel a toast, if any
if (toastShowing && !toastShown) {
toast.cancel()
}
// load the offline data
val timeTableJSON = Util.fileToString(timeTableFilePath)
marksJSON = Util.fileToString(marksFilePath)
if (marksJSON == "") {
marksJSON =
"""{"message": "Zatím žádné známky, zkuste aktualizovat data."}"""
}
// parse the data
if (timeTableJSON != "") {
// cancel a toast, if any
if (toastShowing && !toastShown) {
toast.cancel()
}
// parse the data
week = Parsing.parseTimeTableJSON(timeTableJSON)
marks = Parsing.parseMarksJSON(marksJSON)
// continue launching
done = true
launchPhase2(ctx)
} else {
// make a toast or something
if (!toastShown) {
toast.show()
toastShowing = true
toastShown = true
}
// try to reconnect to the internet
launchPhase1(ctx)
}
}
}
private fun launchPhase2(ctx: Context) {
days = week
for ((i, day) in week.withIndex()) {
dayMap[i] = day
dayList.add(day)
}
Util.loadLectureNotes()
loaded = true
coscope_.launch {
if (!internetAwaitListenerRunning) {
setOnInternetAvailListener(ctx, coscope_)
}
}
val role = if (Parsing.userIsParent()) {
" Rodič"
} else {
" Student"
}
toolbarText.value = "${
JSONObject(
Util.fileToString(
userDataFilePath
)
).getString("fullName")
}$role"
// set the main content view
screenToShow.value = Screen.Main
if (settings.showRefreshButton.checked.value) {
refreshButtonContentColor.value = Color.White
}
} }
@@ -1,111 +0,0 @@
package com.odweta.solon
import android.content.Context
import androidx.compose.runtime.mutableStateOf
import com.odweta.solon.Util.Companion.getLectureId
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import org.json.JSONObject
fun launchStart(ctx: Context) {
if (Account.haveSaved()) {
if (Util.internetAvail(ctx)) {
remoteLoad(ctx)
} else {
localLoad(ctx)
}
} else {
global.noSignInBackHandle = true
screenToShow.value = Screen.SignIn
}
}
fun remoteLoad(ctx: Context) {
if (!isAPISetUp)
CoroutineScope(Dispatchers.IO)
.launch { Network.setupAPI() }
lateinit var token: String
CoroutineScope(Dispatchers.IO)
.launch { token = Network.getToken() }
global.token = token
lateinit var userData: JSONObject
CoroutineScope(Dispatchers.IO)
.launch { userData = Network.getUserData() }
global.userData = userData
lateinit var remoteTimetable: JSONObject
CoroutineScope(Dispatchers.IO)
.launch { remoteTimetable = Network.getTimeTable() }
lateinit var remoteMarks: JSONObject
CoroutineScope(Dispatchers.IO)
.launch { remoteMarks = Network.getMarks() }
launchContinue(
ctx,
remoteTimetable,
remoteMarks
)
}
fun localLoad(ctx: Context) {
val localTimetable = JSONObject(Util.fileToString(timeTableFilePath))
val localMarks = JSONObject(Util.fileToString(marksFilePath))
launchContinue(
ctx,
localTimetable,
localMarks
)
}
fun launchContinue(
ctx: Context,
timetable: JSONObject,
marks: JSONObject
) {
// parse timetable and marks
val parsedTimetable = Parsing.parseTimetable(timetable)
global.timetable = parsedTimetable
val parsedMarks = Parsing.parseMarks(marks)
global.marks = parsedMarks
// load the settings
val settingsData = Util.fileToString(global.filePaths.settings).toSettingsData()
global.settings = settingsData
// load the lecture notes
val lectureNotes = Util.fileToString(global.filePaths.lectureNotes)
Parsing.parseLectureNotes(lectureNotes)
// listen for internet availability in case of disconnection
if (!internetAwaitListenerRunning)
setOnInternetAvailListener(ctx)
// show the default (main) screen
global.currentScreen = Screen.Main
// inside show the timetable
global.currentMainElement = MainElement.Timetable
}
private fun setOnInternetAvailListener(ctx: Context) {
internetAwaitListenerRunning = true
CoroutineScope(Dispatchers.IO).launch {
while (true) {
if (Util.internetAvail(ctx)) {
CoroutineScope(Dispatchers.IO).launch {
remoteLoad(ctx)
}
} else {
showStatus("Bez připojení k internetu.")
}
delay(1000) // Check for internet availability every second
}
}
}
@@ -24,19 +24,13 @@ class MainActivity : ComponentActivity() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
makeStatusBarBlack(window) makeStatusBarBlack(window)
global.fileDir = applicationContext.filesDir
launchStart(applicationContext)
setContent { setContent {
if (!loaded) {
launchPhase0(LocalContext.current, rememberCoroutineScope())
}
when (signedInAndReadyToLaunch.value) {
true -> launchPhase0(LocalContext.current, rememberCoroutineScope())
else -> {}
}
SolonTheme { SolonTheme {
when (screenToShow.value) { when (global.currentScreen) {
Screen.Main -> MainScreen() Screen.Main -> MainScreen()
Screen.Splash -> SplashScreen() Screen.Splash -> SplashScreen()
Screen.SignIn -> SignInScreen() Screen.SignIn -> SignInScreen()
@@ -46,14 +40,14 @@ class MainActivity : ComponentActivity() {
} }
@Composable @Composable
fun MainScreen(modifier: Modifier = Modifier) { fun MainScreen() {
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.background(Color.Black) .background(Color.Black)
) { ) {
Box( Box(
modifier = barModifier, modifier = global.modifiers.bar,
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
) { ) {
ToolbarScreen() ToolbarScreen()
@@ -64,17 +58,17 @@ class MainActivity : ComponentActivity() {
.fillMaxWidth() .fillMaxWidth()
.weight(1f) .weight(1f)
) { ) {
when (isShowing) { when (global.currentScreenElement) {
IsShowing.Timetable -> TimetableScreen() ScreenElement.Timetable -> TimetableScreen()
IsShowing.Marks -> MarksScreen() ScreenElement.Marks -> MarksScreen()
IsShowing.Menu -> MenuScreen() ScreenElement.Menu -> MenuScreen()
IsShowing.About -> AboutScreen() ScreenElement.About -> AboutScreen()
IsShowing.Settings -> SettingsScreen() ScreenElement.Settings -> SettingsScreen()
} }
} }
Box( Box(
modifier = barModifier, modifier = global.modifiers.bar,
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
) { ) {
NavbarScreen() NavbarScreen()
@@ -34,9 +34,8 @@ private var menuColor: MutableState<Color> = mutableStateOf(Color.White)
var refreshButtonContentColor: MutableState<Color> = mutableStateOf(Color.White) var refreshButtonContentColor: MutableState<Color> = mutableStateOf(Color.White)
@Composable @Composable
fun NavbarScreen(modifier: Modifier = Modifier) { fun NavbarScreen() {
val ctx = LocalContext.current val ctx = LocalContext.current
val coscope = rememberCoroutineScope()
Row( Row(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center, horizontalArrangement = Arrangement.Center,
@@ -47,7 +46,7 @@ fun NavbarScreen(modifier: Modifier = Modifier) {
spacerModifier = 15.dp spacerModifier = 15.dp
Button( Button(
onClick = { onClick = {
fetchNewData(ctx, coscope) remoteLoad(ctx)
refreshButtonContentColor.value = PastelYellow refreshButtonContentColor.value = PastelYellow
}, },
colors = ButtonDefaults.buttonColors( colors = ButtonDefaults.buttonColors(
@@ -11,243 +11,135 @@ import java.io.IOException
class Network { class Network {
companion object { companion object {
suspend fun getToken(): String { fun getToken(): String {
return withContext(Dispatchers.IO) { val request = Request.Builder()
val request = Request.Builder() .url(global.apiEndPoints.tokenUrl)
.url(tokenUrl) .header("Content-Type", "application/x-www-form-urlencoded")
.header("Content-Type", "application/x-www-form-urlencoded") .post(
.post( FormBody.Builder()
FormBody.Builder() .add("grant_type", "password")
.add("grant_type", "password") .add("username", global.username)
.add("username", username) .add("password", global.password)
.add("password", password) .add("client_id", "test_client")
.add("client_id", "test_client") .add("scope", "openid offline_access profile sol_api")
.add("scope", "openid offline_access profile sol_api") .build()
.build() )
) .build()
.build()
try { return try {
val resp = client.newCall(request).execute() val resp = client.newCall(request).execute()
val respString = resp.body?.string().toString() val respString = resp.body?.string().toString()
if (JSONObject(respString).has("access_token")) { if (JSONObject(respString).has("access_token")) {
JSONObject(respString).getString("access_token") JSONObject(respString).getString("access_token")
} else {
""""error": "network""""
}
//val respJson = respString?.let { JSONObject(it) }
//respJson.toString()
} catch (e: JSONException) {
"""{"error": "json"}"""
} catch (e: Exception) {
//e.printStackTrace()
"""{"error": "network"}"""
}
}
}
suspend fun setupAPI() {
return withContext(Dispatchers.IO) {
token = "NETWORK_UNREACHABLE"
token = getToken()
if (""""error": """ !in token) {
token = JSONObject(token).get("access_token").toString()
}
if (token == "NETWORK_UNREACHABLE" || """"error": """ in token) {
userData = ""
studentId = ""
internetAvail = false
return@withContext
}
internetAvail = true
userData = getUserDataJSON()
Util.saveStringToFile(userDataFilePath, userData)
studentClass = if (!Parsing.userIsParent()) {
JSONObject(
Util.fileToString(userDataFilePath)
).getJSONObject("class").get("name").toString()
} else { } else {
// if the user is a parent, the class will be of their first child """"error": "network""""
JSONObject(
Util.fileToString(userDataFilePath)
).getJSONArray("children").getJSONObject(0).getString("className")
} }
studentId = getStudentId() //val respJson = respString?.let { JSONObject(it) }
//syid = getSYID() //respJson.toString()
syid = "" } catch (e: JSONException) {
"""{"error": "json"}"""
marksUrl = "$apiBaseUrl/v1/students/$studentId/marks/list"//?SemesterId=${syid}" } catch (e: Exception) {
//e.printStackTrace()
isAPISetUp = true """{"error": "network"}"""
} }
} }
/*private fun getSYID(): String { fun setupAPI() {
val f = Util.fileToString(userDataFilePath) global.token = "NETWORK_UNREACHABLE"
val token = getToken()
lateinit var syidRoot: String if (""""error": """ !in token) {
lateinit var toAdd: String global.token = token
}
if (!Parsing.userIsParent()) { if (global.token == "NETWORK_UNREACHABLE" || """"error": """ in global.token) {
syidRoot = JSONObject(f).getString("schoolYearId") global.userData = JSONObject()
toAdd = JSONObject(f).getString("studyYear") global.studentId = ""
return
}
global.userData = getUserData()
Util.saveStringToFile(global.filePaths.userData, global.userData.toString())
global.studentClass = if (!Parsing.userIsParent()) {
JSONObject(
Util.fileToString(global.filePaths.userData)
).getJSONObject("class").getString("name")
} else { } else {
syidRoot = JSONObject(f) // if the user is a parent, the class will be of their first child
.getJSONArray("children") JSONObject(
.getJSONObject(0) Util.fileToString(global.filePaths.userData)
.getString("schoolYearId") ).getJSONArray("children").getJSONObject(0).getString("className")
toAdd = JSONObject(f)
.getJSONArray("children")
.getJSONObject(0)
.getString("studyYear")
} }
global.studentId = getStudentId()
val syidStart = syidRoot[0] global.apiEndPoints.marksUrl = "$apiBaseUrl/v1/students/${global.studentId}/marks/list"
var syidNum = syidRoot.slice(1..<syidRoot.length).toInt()
for (ch in toAdd) { global.isAPISetUp = true
syidNum += 1 }
}
return syidStart.toString() + syidNum.toString() fun getStudentId(): String {
}*/
private fun getStudentId(): String {
return if (!Parsing.userIsParent()) { return if (!Parsing.userIsParent()) {
JSONObject(userData).getString("personID") global.userData.getString("personID")
} else { } else {
JSONObject(userData).getJSONArray("children") global.userData.getJSONArray("children")
.getJSONObject(0) .getJSONObject(0)
.getString("id") .getString("id")
} }
} }
private suspend fun getUserDataJSON(): String { fun getUserData(): JSONObject {
return withContext(Dispatchers.IO) { val request = Request.Builder()
val request = Request.Builder() .url(global.apiEndPoints.userDataUrl)
.url(userDataUrl) .header("Content-Type", "application/x-www-form-urlencoded")
.header("Content-Type", "application/x-www-form-urlencoded") .header("Authorization", "Bearer ${global.token}") // using Bearer auth token
.header("Authorization", "Bearer $token") // using Bearer auth token .get()
.get() .build()
.build()
try { return try {
val resp = client.newCall(request).execute() val resp = client.newCall(request).execute()
val respString = resp.body?.string().toString() val respString = resp.body?.string().toString()
respString JSONObject(respString)
} catch (e: IOException) { } catch (e: JSONException) {
"""{"error": "network"}""" JSONObject("""{"error": "json"}""")
} } catch (e: IOException) {
JSONObject("""{"error": "network"}""")
} }
} }
suspend fun getUserData(): JSONObject { fun getTimeTable(): JSONObject {
return withContext(Dispatchers.IO) { val userIsParent = Parsing.userIsParent()
val request = Request.Builder() val url = if (!userIsParent) global.apiEndPoints.timeTableUrl
.url(userDataUrl) else "${global.apiEndPoints.timeTableUrl}?studentId=${global.studentId}"
.header("Content-Type", "application/x-www-form-urlencoded")
.header("Authorization", "Bearer $token") // using Bearer auth token
.get()
.build()
try { val request = Request.Builder()
val resp = client.newCall(request).execute() .url(url)
val respString = resp.body?.string().toString() .header("Content-Type", "application/x-www-form-urlencoded")
JSONObject(respString) .header("Authorization", "Bearer ${global.token}")
} catch (e: JSONException) { .get()
JSONObject("""{"error": "json"}""") .build()
} catch (e: IOException) {
JSONObject("""{"error": "network"}""") return try {
} val resp = client.newCall(request).execute()
JSONObject(resp.body?.string().toString())
} catch (e: JSONException) {
JSONObject("""{"error": "json"}""")
} catch (e: IOException) {
JSONObject("""{"error": "network"}""")
} }
} }
suspend fun getTimeTableJSON(): String { fun getMarks(): JSONObject {
return withContext(Dispatchers.IO) { val request = Request.Builder()
val userIsParent = Parsing.userIsParent() .url(global.apiEndPoints.marksUrl)
var url = timeTableUrl .header("Content-Type", "application/x-www-form-urlencoded")
if (userIsParent) { .header("Authorization", "Bearer ${global.token}")
url += "?studentId=$studentId" .get()
} .build()
val request = Request.Builder() return try {
.url(url) val resp = client.newCall(request).execute()
.header("Content-Type", "application/x-www-form-urlencoded") JSONObject(resp.body?.string().toString())
.header("Authorization", "Bearer $token") } catch (e: JSONException) {
.get() JSONObject("""{"error": "json"}""")
.build() } catch (e: IOException) {
JSONObject("""{"error": "network"}""")
try {
val resp = client.newCall(request).execute()
resp.body?.string().toString()
} catch (e: IOException) {
"""{"error": "network"}"""
}
}
}
suspend fun getTimeTable(): JSONObject {
return withContext(Dispatchers.IO) {
val userIsParent = Parsing.userIsParent()
var url = timeTableUrl
if (userIsParent) {
url += "?studentId=$studentId"
}
val request = Request.Builder()
.url(url)
.header("Content-Type", "application/x-www-form-urlencoded")
.header("Authorization", "Bearer $token")
.get()
.build()
try {
val resp = client.newCall(request).execute()
JSONObject(resp.body?.string().toString())
} catch (e: JSONException) {
JSONObject("""{"error": "json"}""")
} catch (e: IOException) {
JSONObject("""{"error": "network"}""")
}
}
}
suspend fun getMarksJSON(): String {
return withContext(Dispatchers.IO) {
val request = Request.Builder()
.url(marksUrl)
.header("Content-Type", "application/x-www-form-urlencoded")
.header("Authorization", "Bearer $token")
.get()
.build()
try {
val resp = client.newCall(request).execute()
resp.body?.string().toString()
} catch (e: IOException) {
"""{"error": "network"}"""
}
}
}
suspend fun getMarks(): JSONObject {
return withContext(Dispatchers.IO) {
val request = Request.Builder()
.url(marksUrl)
.header("Content-Type", "application/x-www-form-urlencoded")
.header("Authorization", "Bearer $token")
.get()
.build()
try {
val resp = client.newCall(request).execute()
JSONObject(resp.body?.string().toString())
} catch (e: JSONException) {
JSONObject("""{"error": "json"}""")
} catch (e: IOException) {
JSONObject("""{"error": "network"}""")
}
} }
} }
} }
@@ -1,5 +1,6 @@
package com.odweta.solon package com.odweta.solon
import android.util.Log
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import com.odweta.solon.Util.Companion.getLectureId import com.odweta.solon.Util.Companion.getLectureId
import org.json.JSONObject import org.json.JSONObject
@@ -52,6 +53,7 @@ class Parsing {
} }
fun parseTimetable(jsonString: JSONObject): List<Day> { fun parseTimetable(jsonString: JSONObject): List<Day> {
Log.d("debug", "gotten json: $jsonString")
val jsonObject = jsonString val jsonObject = jsonString
/* /*
@@ -338,9 +340,9 @@ class Parsing {
val marksList = mutableListOf<JSONObject>() val marksList = mutableListOf<JSONObject>()
val marksJSONArray = JSONObject(jsonString).getJSONArray("marks") val marksJSONArray = jsonString.getJSONArray("marks")
val subjectsJSONArray = JSONObject(jsonString).getJSONArray("subjects") val subjectsJSONArray = jsonString.getJSONArray("subjects")
val subjectMap = mutableMapOf<String, String>() val subjectMap = mutableMapOf<String, String>()
for (i in 0..<subjectsJSONArray.length()) { for (i in 0..<subjectsJSONArray.length()) {
@@ -150,6 +150,9 @@ fun SettingsTableRow(setting: Setting) {
fun String.toSettingsData(): SettingsData { fun String.toSettingsData(): SettingsData {
val settings = this.split("\n") val settings = this.split("\n")
if (settings.size <= 1) {
return SettingsData()
}
val auxSettingsData = SettingsData() val auxSettingsData = SettingsData()
val useSvobodaMode = settings[0].split("#") val useSvobodaMode = settings[0].split("#")
val useAmoledMode = settings[1].split("#") val useAmoledMode = settings[1].split("#")
@@ -86,16 +86,14 @@ fun signInButtonCallback(ctx: Context, coscope: CoroutineScope, buttonText: Stri
} }
coscope.launch { coscope.launch {
signedInAndReadyToLaunch.value = false
val valid = Account.isValid(acc) val valid = Account.isValid(acc)
checked = true checked = true
if (valid) { if (valid) {
Account.save(acc) Account.save(acc)
signedInAndReadyToLaunch.value = true global.currentScreen = Screen.Splash
screenToShow.value = Screen.Splash launchStart(ctx)
fetchNewData(ctx, coscope)
} else { } else {
Toast.makeText( Toast.makeText(
ctx, ctx,
@@ -111,7 +109,7 @@ fun SignInScreen() {
// handle the back gesture (back swipe form side or back button on bottom android navbar) // handle the back gesture (back swipe form side or back button on bottom android navbar)
if (!global.noSignInBackHandle) { if (!global.noSignInBackHandle) {
BackHandler { BackHandler {
screenToShow.value = Screen.Main global.currentScreen = Screen.Main
} }
} }
@@ -147,7 +145,7 @@ fun SignInScreen() {
modifier = Modifier modifier = Modifier
.border( .border(
width = 5.dp, color = PastelYellow, shape = RoundedCornerShape( width = 5.dp, color = PastelYellow, shape = RoundedCornerShape(
roundedCornerDimen global.dimens.roundedCorner
) )
) )
.width(230.dp), .width(230.dp),
@@ -174,7 +172,7 @@ fun SignInScreen() {
width = 5.dp, width = 5.dp,
color = PastelYellow, color = PastelYellow,
shape = RoundedCornerShape( shape = RoundedCornerShape(
roundedCornerDimen global.dimens.roundedCorner
) )
), ),
colors = TextFieldDefaults.colors( colors = TextFieldDefaults.colors(
@@ -211,7 +209,7 @@ fun SignInScreen() {
modifier = Modifier modifier = Modifier
.border( .border(
width = 5.dp, width = 5.dp,
shape = RoundedCornerShape(roundedCornerDimen), shape = RoundedCornerShape(global.dimens.roundedCorner),
color = PastelYellow color = PastelYellow
) )
.width(230.dp) .width(230.dp)
@@ -36,7 +36,7 @@ class Util {
fun saveStringToFile(filePath: String, s: String): Boolean { fun saveStringToFile(filePath: String, s: String): Boolean {
// get a file object with the specified path // get a file object with the specified path
val f = File(fileDir, filePath) val f = File(global.fileDir, filePath)
// if it does not exist, create it // if it does not exist, create it
if (!f.exists()) { if (!f.exists()) {
@@ -58,7 +58,7 @@ class Util {
} }
fun fileToString(filePath: String): String { fun fileToString(filePath: String): String {
val f = File(fileDir, filePath) val f = File(global.fileDir, filePath)
return if (f.exists()) { return if (f.exists()) {
if (f.canRead()) { if (f.canRead()) {
@@ -76,12 +76,12 @@ class Util {
} }
fun fileExists(filePath: String): Boolean { fun fileExists(filePath: String): Boolean {
val f = File(fileDir, filePath) val f = File(global.fileDir, filePath)
return f.exists() return f.exists()
} }
fun fileIsEmpty(filePath: String): Boolean { fun fileIsEmpty(filePath: String): Boolean {
val f = File(fileDir, filePath) val f = File(global.fileDir, filePath)
val content = f.readText() val content = f.readText()
return content == "" return content == ""
} }
@@ -101,7 +101,7 @@ class Util {
} }
fun deleteFile(path: String): Boolean { fun deleteFile(path: String): Boolean {
val f = File(fileDir, path) val f = File(global.fileDir, path)
if (f.exists()) { if (f.exists()) {
try { try {