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
val file = File(fileDir, accountDataFilePath)
val file = File(global.fileDir, accountDataFilePath)
return if (!file.exists()) {
// it does not exist
@@ -36,15 +36,15 @@ class Account(val username: String, val password: String, private val error: Int
} else {
// it exists, so it shall be parsed
val acc: Account = parseAccountDataFile(file)
username = acc.username
password = acc.password
global.username = acc.username
global.password = acc.password
acc.error == 0
}
}
fun save(acc: Account): Boolean {
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"))
true
@@ -55,8 +55,8 @@ class Account(val username: String, val password: String, private val error: Int
suspend fun isValid(acc: Account): Boolean {
return withContext(Dispatchers.IO) {
username = acc.username
password = acc.password
global.username = acc.username
global.password = acc.password
val token = Network.getToken()
!JSONObject(token).has("error")
@@ -45,10 +45,8 @@ const val userDataFilePath: String = "userdata.json"
const val marksFilePath: String = "marks.json"
const val settingsDataFilePath: String = "settings.txt"
const val lectureNotesFilePath: String = "lecture_notes.csv"
lateinit var fileDir: File
var width: Int = 0
var height: Int = 0
var internetAvail: Boolean = false
var loaded: Boolean = false
@@ -137,7 +135,6 @@ val dayMap = mutableMapOf<Int, Day>()
val dayList = mutableListOf<Day>()
var days = listOf<Day>()
var marksJSON: String = ""
var marks: MutableMap<String, MutableList<Mark>> = mutableMapOf()
var username: String = ""
@@ -145,10 +142,6 @@ var password: String = ""
val client = OkHttpClient()
var resetNav: Boolean = false
var isAPISetUp: Boolean = false
enum class LectureNoteType {
Normal, // obycejna poznamka
Writing, // pisemny test
@@ -203,10 +196,10 @@ const val tokenUrl = "$apiBaseUrl/connect/token"
var token: String = ""
const val userDataUrl = "$apiBaseUrl/v1/user"
var userData: String = ""
var studentId: String = ""
//var studentId: String = ""
const val timeTableUrl = "$apiBaseUrl/v1/timeTable"
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 {
lateinit var name: String
@@ -284,44 +277,68 @@ class Mark {
}
}
object FilePaths {
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 {
enum class ScreenElement {
Timetable,
Marks,
Menu
Menu,
About,
Settings
}
object Global {
class Global {
lateinit var token: String
lateinit var userData: JSONObject
lateinit var timetable: List<Day>
lateinit var timetableJSON: JSONObject
lateinit var marks: MutableMap<String, MutableList<Mark>>
var noSignInBackHandle: Boolean = false
val filePaths: FilePaths = FilePaths
val apiEndPoints: APIEndpoints = APIEndpoints
lateinit var marksJSON: JSONObject
lateinit var studentClass: String
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 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
import android.content.Context
import android.widget.Toast
import androidx.compose.ui.graphics.Color
import android.util.Log
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import org.json.JSONObject
private var week: List<Day> = mutableListOf()
private var toastShowing: Boolean = false
private var toastShown: Boolean = false
private var toast2Shown: Boolean = false
fun launchStart(ctx: Context) {
if (Account.haveSaved()) {
if (Util.internetAvail(ctx)) {
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
coscope.launch {
var internetAvail = Util.internetAvail(ctx)
CoroutineScope(Dispatchers.IO).launch {
while (true) {
if (Util.internetAvail(ctx) && !internetAvail) {
internetAvail = Util.internetAvail(ctx)
coscope.launch {
fetchNewData(ctx, coscope)
CoroutineScope(Dispatchers.IO).launch {
remoteLoad(ctx)
}
} else if (!internetAvail) {
internetAvail = Util.internetAvail(ctx)
toolbarText.value = "Bez připojení k internetu."
showStatus("Bez připojení k internetu.")
} else {
internetAvail = Util.internetAvail(ctx)
}
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)
makeStatusBarBlack(window)
global.fileDir = applicationContext.filesDir
launchStart(applicationContext)
setContent {
if (!loaded) {
launchPhase0(LocalContext.current, rememberCoroutineScope())
}
when (signedInAndReadyToLaunch.value) {
true -> launchPhase0(LocalContext.current, rememberCoroutineScope())
else -> {}
}
SolonTheme {
when (screenToShow.value) {
when (global.currentScreen) {
Screen.Main -> MainScreen()
Screen.Splash -> SplashScreen()
Screen.SignIn -> SignInScreen()
@@ -46,14 +40,14 @@ class MainActivity : ComponentActivity() {
}
@Composable
fun MainScreen(modifier: Modifier = Modifier) {
fun MainScreen() {
Column(
modifier = Modifier
.fillMaxSize()
.background(Color.Black)
) {
Box(
modifier = barModifier,
modifier = global.modifiers.bar,
contentAlignment = Alignment.Center
) {
ToolbarScreen()
@@ -64,17 +58,17 @@ class MainActivity : ComponentActivity() {
.fillMaxWidth()
.weight(1f)
) {
when (isShowing) {
IsShowing.Timetable -> TimetableScreen()
IsShowing.Marks -> MarksScreen()
IsShowing.Menu -> MenuScreen()
IsShowing.About -> AboutScreen()
IsShowing.Settings -> SettingsScreen()
when (global.currentScreenElement) {
ScreenElement.Timetable -> TimetableScreen()
ScreenElement.Marks -> MarksScreen()
ScreenElement.Menu -> MenuScreen()
ScreenElement.About -> AboutScreen()
ScreenElement.Settings -> SettingsScreen()
}
}
Box(
modifier = barModifier,
modifier = global.modifiers.bar,
contentAlignment = Alignment.Center
) {
NavbarScreen()
@@ -34,9 +34,8 @@ private var menuColor: MutableState<Color> = mutableStateOf(Color.White)
var refreshButtonContentColor: MutableState<Color> = mutableStateOf(Color.White)
@Composable
fun NavbarScreen(modifier: Modifier = Modifier) {
fun NavbarScreen() {
val ctx = LocalContext.current
val coscope = rememberCoroutineScope()
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center,
@@ -47,7 +46,7 @@ fun NavbarScreen(modifier: Modifier = Modifier) {
spacerModifier = 15.dp
Button(
onClick = {
fetchNewData(ctx, coscope)
remoteLoad(ctx)
refreshButtonContentColor.value = PastelYellow
},
colors = ButtonDefaults.buttonColors(
@@ -11,243 +11,135 @@ import java.io.IOException
class Network {
companion object {
suspend fun getToken(): String {
return withContext(Dispatchers.IO) {
val request = Request.Builder()
.url(tokenUrl)
.header("Content-Type", "application/x-www-form-urlencoded")
.post(
FormBody.Builder()
.add("grant_type", "password")
.add("username", username)
.add("password", password)
.add("client_id", "test_client")
.add("scope", "openid offline_access profile sol_api")
.build()
)
.build()
fun getToken(): String {
val request = Request.Builder()
.url(global.apiEndPoints.tokenUrl)
.header("Content-Type", "application/x-www-form-urlencoded")
.post(
FormBody.Builder()
.add("grant_type", "password")
.add("username", global.username)
.add("password", global.password)
.add("client_id", "test_client")
.add("scope", "openid offline_access profile sol_api")
.build()
)
.build()
try {
val resp = client.newCall(request).execute()
val respString = resp.body?.string().toString()
if (JSONObject(respString).has("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()
return try {
val resp = client.newCall(request).execute()
val respString = resp.body?.string().toString()
if (JSONObject(respString).has("access_token")) {
JSONObject(respString).getString("access_token")
} else {
// if the user is a parent, the class will be of their first child
JSONObject(
Util.fileToString(userDataFilePath)
).getJSONArray("children").getJSONObject(0).getString("className")
""""error": "network""""
}
studentId = getStudentId()
//syid = getSYID()
syid = ""
marksUrl = "$apiBaseUrl/v1/students/$studentId/marks/list"//?SemesterId=${syid}"
isAPISetUp = true
//val respJson = respString?.let { JSONObject(it) }
//respJson.toString()
} catch (e: JSONException) {
"""{"error": "json"}"""
} catch (e: Exception) {
//e.printStackTrace()
"""{"error": "network"}"""
}
}
/*private fun getSYID(): String {
val f = Util.fileToString(userDataFilePath)
lateinit var syidRoot: String
lateinit var toAdd: String
if (!Parsing.userIsParent()) {
syidRoot = JSONObject(f).getString("schoolYearId")
toAdd = JSONObject(f).getString("studyYear")
fun setupAPI() {
global.token = "NETWORK_UNREACHABLE"
val token = getToken()
if (""""error": """ !in token) {
global.token = token
}
if (global.token == "NETWORK_UNREACHABLE" || """"error": """ in global.token) {
global.userData = JSONObject()
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 {
syidRoot = JSONObject(f)
.getJSONArray("children")
.getJSONObject(0)
.getString("schoolYearId")
toAdd = JSONObject(f)
.getJSONArray("children")
.getJSONObject(0)
.getString("studyYear")
// if the user is a parent, the class will be of their first child
JSONObject(
Util.fileToString(global.filePaths.userData)
).getJSONArray("children").getJSONObject(0).getString("className")
}
global.studentId = getStudentId()
val syidStart = syidRoot[0]
var syidNum = syidRoot.slice(1..<syidRoot.length).toInt()
global.apiEndPoints.marksUrl = "$apiBaseUrl/v1/students/${global.studentId}/marks/list"
for (ch in toAdd) {
syidNum += 1
}
global.isAPISetUp = true
}
return syidStart.toString() + syidNum.toString()
}*/
private fun getStudentId(): String {
fun getStudentId(): String {
return if (!Parsing.userIsParent()) {
JSONObject(userData).getString("personID")
global.userData.getString("personID")
} else {
JSONObject(userData).getJSONArray("children")
global.userData.getJSONArray("children")
.getJSONObject(0)
.getString("id")
}
}
private suspend fun getUserDataJSON(): String {
return withContext(Dispatchers.IO) {
val request = Request.Builder()
.url(userDataUrl)
.header("Content-Type", "application/x-www-form-urlencoded")
.header("Authorization", "Bearer $token") // using Bearer auth token
.get()
.build()
fun getUserData(): JSONObject {
val request = Request.Builder()
.url(global.apiEndPoints.userDataUrl)
.header("Content-Type", "application/x-www-form-urlencoded")
.header("Authorization", "Bearer ${global.token}") // using Bearer auth token
.get()
.build()
try {
val resp = client.newCall(request).execute()
val respString = resp.body?.string().toString()
respString
} catch (e: IOException) {
"""{"error": "network"}"""
}
return try {
val resp = client.newCall(request).execute()
val respString = resp.body?.string().toString()
JSONObject(respString)
} catch (e: JSONException) {
JSONObject("""{"error": "json"}""")
} catch (e: IOException) {
JSONObject("""{"error": "network"}""")
}
}
suspend fun getUserData(): JSONObject {
return withContext(Dispatchers.IO) {
val request = Request.Builder()
.url(userDataUrl)
.header("Content-Type", "application/x-www-form-urlencoded")
.header("Authorization", "Bearer $token") // using Bearer auth token
.get()
.build()
fun getTimeTable(): JSONObject {
val userIsParent = Parsing.userIsParent()
val url = if (!userIsParent) global.apiEndPoints.timeTableUrl
else "${global.apiEndPoints.timeTableUrl}?studentId=${global.studentId}"
try {
val resp = client.newCall(request).execute()
val respString = resp.body?.string().toString()
JSONObject(respString)
} catch (e: JSONException) {
JSONObject("""{"error": "json"}""")
} catch (e: IOException) {
JSONObject("""{"error": "network"}""")
}
val request = Request.Builder()
.url(url)
.header("Content-Type", "application/x-www-form-urlencoded")
.header("Authorization", "Bearer ${global.token}")
.get()
.build()
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 {
return withContext(Dispatchers.IO) {
val userIsParent = Parsing.userIsParent()
var url = timeTableUrl
if (userIsParent) {
url += "?studentId=$studentId"
}
fun getMarks(): JSONObject {
val request = Request.Builder()
.url(global.apiEndPoints.marksUrl)
.header("Content-Type", "application/x-www-form-urlencoded")
.header("Authorization", "Bearer ${global.token}")
.get()
.build()
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()
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"}""")
}
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"}""")
}
}
}
@@ -1,5 +1,6 @@
package com.odweta.solon
import android.util.Log
import androidx.compose.runtime.mutableStateOf
import com.odweta.solon.Util.Companion.getLectureId
import org.json.JSONObject
@@ -52,6 +53,7 @@ class Parsing {
}
fun parseTimetable(jsonString: JSONObject): List<Day> {
Log.d("debug", "gotten json: $jsonString")
val jsonObject = jsonString
/*
@@ -338,9 +340,9 @@ class Parsing {
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>()
for (i in 0..<subjectsJSONArray.length()) {
@@ -150,6 +150,9 @@ fun SettingsTableRow(setting: Setting) {
fun String.toSettingsData(): SettingsData {
val settings = this.split("\n")
if (settings.size <= 1) {
return SettingsData()
}
val auxSettingsData = SettingsData()
val useSvobodaMode = settings[0].split("#")
val useAmoledMode = settings[1].split("#")
@@ -86,16 +86,14 @@ fun signInButtonCallback(ctx: Context, coscope: CoroutineScope, buttonText: Stri
}
coscope.launch {
signedInAndReadyToLaunch.value = false
val valid = Account.isValid(acc)
checked = true
if (valid) {
Account.save(acc)
signedInAndReadyToLaunch.value = true
screenToShow.value = Screen.Splash
fetchNewData(ctx, coscope)
global.currentScreen = Screen.Splash
launchStart(ctx)
} else {
Toast.makeText(
ctx,
@@ -111,7 +109,7 @@ fun SignInScreen() {
// handle the back gesture (back swipe form side or back button on bottom android navbar)
if (!global.noSignInBackHandle) {
BackHandler {
screenToShow.value = Screen.Main
global.currentScreen = Screen.Main
}
}
@@ -147,7 +145,7 @@ fun SignInScreen() {
modifier = Modifier
.border(
width = 5.dp, color = PastelYellow, shape = RoundedCornerShape(
roundedCornerDimen
global.dimens.roundedCorner
)
)
.width(230.dp),
@@ -174,7 +172,7 @@ fun SignInScreen() {
width = 5.dp,
color = PastelYellow,
shape = RoundedCornerShape(
roundedCornerDimen
global.dimens.roundedCorner
)
),
colors = TextFieldDefaults.colors(
@@ -211,7 +209,7 @@ fun SignInScreen() {
modifier = Modifier
.border(
width = 5.dp,
shape = RoundedCornerShape(roundedCornerDimen),
shape = RoundedCornerShape(global.dimens.roundedCorner),
color = PastelYellow
)
.width(230.dp)
@@ -36,7 +36,7 @@ class Util {
fun saveStringToFile(filePath: String, s: String): Boolean {
// 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 (!f.exists()) {
@@ -58,7 +58,7 @@ class Util {
}
fun fileToString(filePath: String): String {
val f = File(fileDir, filePath)
val f = File(global.fileDir, filePath)
return if (f.exists()) {
if (f.canRead()) {
@@ -76,12 +76,12 @@ class Util {
}
fun fileExists(filePath: String): Boolean {
val f = File(fileDir, filePath)
val f = File(global.fileDir, filePath)
return f.exists()
}
fun fileIsEmpty(filePath: String): Boolean {
val f = File(fileDir, filePath)
val f = File(global.fileDir, filePath)
val content = f.readText()
return content == ""
}
@@ -101,7 +101,7 @@ class Util {
}
fun deleteFile(path: String): Boolean {
val f = File(fileDir, path)
val f = File(global.fileDir, path)
if (f.exists()) {
try {