From 1516a00841fb020adf39cf7a45c7b4b9e95ddd10 Mon Sep 17 00:00:00 2001 From: Odweta Date: Mon, 30 Dec 2024 17:31:27 +0100 Subject: [PATCH] fixed app crashes and some other improvements --- mobile/.idea/caches/deviceStreaming.xml | 340 ++++++++++++++++++ mobile/app/build.gradle.kts | 2 +- .../release/baselineProfiles/0/app-release.dm | Bin 7884 -> 0 bytes .../release/baselineProfiles/1/app-release.dm | Bin 7796 -> 0 bytes mobile/app/release/output-metadata.json | 10 +- .../src/main/java/com/odweta/solon/Account.kt | 49 ++- .../src/main/java/com/odweta/solon/Global.kt | 10 +- .../src/main/java/com/odweta/solon/Launch.kt | 1 + .../src/main/java/com/odweta/solon/Marks.kt | 136 +++++-- .../src/main/java/com/odweta/solon/Menu.kt | 5 +- .../src/main/java/com/odweta/solon/Network.kt | 15 +- .../src/main/java/com/odweta/solon/Parsing.kt | 16 +- .../src/main/java/com/odweta/solon/SignIn.kt | 15 +- .../main/java/com/odweta/solon/Timetable.kt | 2 + 14 files changed, 521 insertions(+), 80 deletions(-) create mode 100644 mobile/.idea/caches/deviceStreaming.xml delete mode 100644 mobile/app/release/baselineProfiles/0/app-release.dm delete mode 100644 mobile/app/release/baselineProfiles/1/app-release.dm diff --git a/mobile/.idea/caches/deviceStreaming.xml b/mobile/.idea/caches/deviceStreaming.xml new file mode 100644 index 0000000..406736c --- /dev/null +++ b/mobile/.idea/caches/deviceStreaming.xml @@ -0,0 +1,340 @@ + + + + + + \ No newline at end of file diff --git a/mobile/app/build.gradle.kts b/mobile/app/build.gradle.kts index 8614826..d76082d 100644 --- a/mobile/app/build.gradle.kts +++ b/mobile/app/build.gradle.kts @@ -32,7 +32,7 @@ android { applicationId = "com.odweta.solon" minSdk = 28 targetSdk = 34 - versionCode = 208 + versionCode = 210 versionName = "2.3.0" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" diff --git a/mobile/app/release/baselineProfiles/0/app-release.dm b/mobile/app/release/baselineProfiles/0/app-release.dm deleted file mode 100644 index 68e52a985e5c86b76c42d82adb923d24c7f88b4f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7884 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 I_?Ps*0Np$GO#lD@ diff --git a/mobile/app/release/baselineProfiles/1/app-release.dm b/mobile/app/release/baselineProfiles/1/app-release.dm deleted file mode 100644 index 11e8fa4ac3d705958aa4c65d4cbb6d52b9b67cd3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7796 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 e`Y-wLCz*e@bk = file.readLines() - - if (lines.size >= 2) { - if (lines[0] != "" && lines[1] != "") { - return Account(lines[0], lines[1], 0) - } - } - - return Account("", "", 1) + private fun parseAccountDataFile(): Account { + val lines: List = File(global.fileDir, global.filePaths.accountData).readLines() + return Account(lines[0], lines[1], 0) } fun haveSaved(): Boolean { @@ -27,15 +21,17 @@ class Account(val username: String, val password: String, private val error: Int */ // check if file exists - val file = File(global.fileDir, global.filePaths.accountData) - return if (!file.exists()) { + return if ( + !Util.fileExists(global.filePaths.accountData) || + Util.fileIsEmpty(global.filePaths.accountData) + ) { // it does not exist - file.createNewFile() + File(global.fileDir, global.filePaths.accountData).createNewFile() false } else { // it exists, so it shall be parsed - val acc: Account = parseAccountDataFile(file) + val acc: Account = parseAccountDataFile() global.username = acc.username global.password = acc.password acc.error == 0 @@ -43,23 +39,20 @@ class Account(val username: String, val password: String, private val error: Int } fun save(acc: Account): Boolean { - return try { - val file = File(global.fileDir, global.filePaths.accountData) - file.writeText("${acc.username}\n${acc.password}\n", Charset.forName("UTF-8")) - - true - } catch (e: Exception) { - false - } + val payload = "${acc.username}\n${acc.password}" + return Util.saveStringToFile(global.filePaths.accountData, payload) } - suspend fun isValid(acc: Account): Boolean { - return withContext(Dispatchers.IO) { - global.username = acc.username - global.password = acc.password + fun isValid(acc: Account): Boolean { + global.username = acc.username + global.password = acc.password - val token = networkRequestFactory.perform(NetworkRequestType.Token).getString("token") - !JSONObject(token).has("error") + val tokenJSON = networkRequestFactory.perform(NetworkRequestType.Token) + return if (tokenJSON.has("token")) { + val token = tokenJSON.getString("token") + !token.contains("\"error\"") + } else { + false } } } diff --git a/mobile/app/src/main/java/com/odweta/solon/Global.kt b/mobile/app/src/main/java/com/odweta/solon/Global.kt index e4e8dc3..295f395 100644 --- a/mobile/app/src/main/java/com/odweta/solon/Global.kt +++ b/mobile/app/src/main/java/com/odweta/solon/Global.kt @@ -76,7 +76,15 @@ fun String.toLectureNoteType(): LectureNoteType { } } -class LectureNote(id: Int, note: MutableState, type: MutableState, number: Int, lectureName: String) { +data class LectureNote( + var id: Int, + var note: MutableState = mutableStateOf(""), + var type: MutableState = mutableStateOf(LectureNoteType.Normal), + var number: Int, + var lectureName: String +) + +class LectureNoteOLD(id: Int, note: MutableState, type: MutableState, number: Int, lectureName: String) { var id: Int = 0 var note: MutableState = mutableStateOf("") diff --git a/mobile/app/src/main/java/com/odweta/solon/Launch.kt b/mobile/app/src/main/java/com/odweta/solon/Launch.kt index f0671d5..850f6b4 100644 --- a/mobile/app/src/main/java/com/odweta/solon/Launch.kt +++ b/mobile/app/src/main/java/com/odweta/solon/Launch.kt @@ -81,6 +81,7 @@ fun launchContinue( global.settings = settingsData // load the lecture notes + Util.loadLectureNotes() Util.csvToLectureNotes() // listen for internet availability in case of disconnection diff --git a/mobile/app/src/main/java/com/odweta/solon/Marks.kt b/mobile/app/src/main/java/com/odweta/solon/Marks.kt index 27fb11c..4975ed9 100644 --- a/mobile/app/src/main/java/com/odweta/solon/Marks.kt +++ b/mobile/app/src/main/java/com/odweta/solon/Marks.kt @@ -14,12 +14,16 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.AutoAwesome +import androidx.compose.material.icons.filled.WorkspacePremium +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonColors import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.Icon import androidx.compose.material3.Text @@ -57,6 +61,7 @@ import java.time.LocalDate import java.time.format.DateTimeFormatter var showNewMarksDialog by mutableStateOf(false) +var showFinalMarksDialog by mutableStateOf(false) enum class MarksDepthLevel { All, @@ -71,8 +76,14 @@ fun MarksScreen() { MarksDepthLevel.Subject -> MarksSubject() MarksDepthLevel.Detail -> MarksDetail() } + NewMarksDialog() + FinalMarksDialog() +} + +@Composable +fun NewMarksDialog() { if (showNewMarksDialog) { - val scrollStateNewMarks = rememberScrollState() + val ss = rememberScrollState() Dialog( onDismissRequest = { showNewMarksDialog = false }, @@ -82,7 +93,9 @@ fun MarksScreen() { ) ) { Column( - Modifier.verticalScroll(scrollStateNewMarks).padding(vertical = 10.dp), + Modifier + .verticalScroll(ss) + .padding(vertical = 10.dp), horizontalAlignment = Alignment.CenterHorizontally ) { Text( @@ -91,14 +104,17 @@ fun MarksScreen() { fontWeight = FontWeight.Bold, fontSize = 30.sp, textAlign = TextAlign.Center, - modifier = Modifier.background( - color = Color.Black, - shape = RoundedCornerShape(global.dimens.roundedCorner) - ).border( - shape = RoundedCornerShape(global.dimens.roundedCorner), - color = PastelYellow, - width = global.dimens.borderWidth - ).padding(10.dp) + modifier = Modifier + .background( + color = Color.Black, + shape = RoundedCornerShape(global.dimens.roundedCorner) + ) + .border( + shape = RoundedCornerShape(global.dimens.roundedCorner), + color = PastelYellow, + width = global.dimens.borderWidth + ) + .padding(10.dp) ) Spacer(Modifier.height(10.dp)) for ((k, v) in global.newMarks) { @@ -133,7 +149,8 @@ fun MarksScreen() { Text( i.theme, color = Color.White, - modifier = Modifier.padding(end = 5.dp, top = 5.dp) + modifier = Modifier + .padding(end = 5.dp, top = 5.dp) .weight(1f) ) Column( @@ -146,7 +163,8 @@ fun MarksScreen() { .background( color = getAvgColor(i.grade, true), shape = RoundedCornerShape(global.dimens.roundedCorner) - ).padding(5.dp), + ) + .padding(5.dp), style = TextStyle( shadow = Shadow( color = Color.Black, @@ -173,6 +191,47 @@ fun MarksScreen() { } } +@Composable +fun FinalMarksDialog() { + if (showFinalMarksDialog) { + val ss = rememberScrollState() + + Dialog( + onDismissRequest = { showFinalMarksDialog = false }, + properties = DialogProperties( + dismissOnBackPress = true, + dismissOnClickOutside = true + ) + ) { + Column( + Modifier + .verticalScroll(ss) + .padding(vertical = 10.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text( + "Uzavřené známky", + color = Color.White, + fontWeight = FontWeight.Bold, + fontSize = 30.sp, + textAlign = TextAlign.Center, + modifier = Modifier + .background( + color = Color.Black, + shape = RoundedCornerShape(global.dimens.roundedCorner) + ) + .border( + shape = RoundedCornerShape(global.dimens.roundedCorner), + color = PastelYellow, + width = global.dimens.borderWidth + ) + .padding(10.dp) + ) + } + } + } +} + private fun getAvgColor(avg: String, isBorder: Boolean = false): Color { val aux = avg.replace(",", ".").toFloat() return if (aux in 1f..<1.5f) { @@ -360,21 +419,46 @@ fun MarksAll() { } Box(Modifier.fillMaxSize()) { - FloatingActionButton( - onClick = { showNewMarksDialog = true }, - modifier = Modifier - .align(Alignment.BottomEnd) - .padding(10.dp) - .background(Color.Black, shape = RoundedCornerShape(global.dimens.roundedCorner)), - containerColor = Color.Black, - contentColor = PastelYellow + Row( + modifier = Modifier.align(Alignment.BottomCenter) ) { - Icon( - modifier = Modifier.padding(start = 4.dp, top = 4.dp), - imageVector = Icons.Default.AutoAwesome, - contentDescription = "New marks in subject", - tint = PastelYellow // You can customize the icon color - ) + FloatingActionButton( + onClick = { showNewMarksDialog = true }, + modifier = Modifier +// .size(40.dp) + .padding(10.dp) + .background( + Color.Black, + shape = RoundedCornerShape(global.dimens.roundedCorner) + ), + contentColor = PastelYellow, + containerColor = Color.Black + ) { + Icon( + imageVector = Icons.Default.AutoAwesome, + contentDescription = "New marks in subject", + tint = PastelYellow // You can customize the icon color + ) + } + + FloatingActionButton( + onClick = { showFinalMarksDialog = true }, + modifier = Modifier +// .size(40.dp) + .padding(10.dp) + .background( + Color.Black, + shape = RoundedCornerShape(global.dimens.roundedCorner) + ), + contentColor = PastelYellow, + containerColor = Color.Black + ) { + Icon( + imageVector = Icons.Default.WorkspacePremium, + contentDescription = "Final marks", + tint = PastelYellow // You can customize the icon color + ) + } } } } diff --git a/mobile/app/src/main/java/com/odweta/solon/Menu.kt b/mobile/app/src/main/java/com/odweta/solon/Menu.kt index 1b52f85..26a7dc6 100644 --- a/mobile/app/src/main/java/com/odweta/solon/Menu.kt +++ b/mobile/app/src/main/java/com/odweta/solon/Menu.kt @@ -40,7 +40,10 @@ fun MenuScreen() { Spacer(modifier = Modifier.height(80.dp)) - MenuButton({ global.currentScreen = Screen.SignIn }, "Změnit přihlášení") + MenuButton({ + global.noSignInBackHandle = false + global.currentScreen = Screen.SignIn + }, "Změnit přihlášení") Spacer(modifier = Modifier.height(80.dp)) diff --git a/mobile/app/src/main/java/com/odweta/solon/Network.kt b/mobile/app/src/main/java/com/odweta/solon/Network.kt index 5472c91..889a9e2 100644 --- a/mobile/app/src/main/java/com/odweta/solon/Network.kt +++ b/mobile/app/src/main/java/com/odweta/solon/Network.kt @@ -11,15 +11,21 @@ import org.json.JSONObject fun setupAPI() { global.token = "NETWORK_UNREACHABLE" - val token = networkRequestFactory.perform(NetworkRequestType.Token).getString("token") - if (""""error": """ !in token) { - global.token = token + + val tokenJSON = networkRequestFactory.perform(NetworkRequestType.Token) + if (tokenJSON.has("token")) { + val token = tokenJSON.getString("token") + if ("\"error\": " !in token) { + global.token = token + } } - if (global.token == "NETWORK_UNREACHABLE" || """"error": """ in global.token) { + + if (global.token == "NETWORK_UNREACHABLE" || "\"error\":" in global.token) { global.userData = JSONObject() global.studentId = "" return } + global.userData = networkRequestFactory.perform(NetworkRequestType.UserData) Util.saveStringToFile(global.filePaths.userData, global.userData.toString()) global.studentClass = Parsing.getStudentClass() @@ -90,7 +96,6 @@ object NetworkRequestFactory: NetworkRequestInterface { } private fun getAPI(url: String): JSONObject { - Log.d("debug TOKEN", global.token) val response = get(url, headersOf( "Content-Type", "application/x-www-form-urlencoded", "Authorization", "Bearer ${global.token}" diff --git a/mobile/app/src/main/java/com/odweta/solon/Parsing.kt b/mobile/app/src/main/java/com/odweta/solon/Parsing.kt index 9af49e8..f1d5f6b 100644 --- a/mobile/app/src/main/java/com/odweta/solon/Parsing.kt +++ b/mobile/app/src/main/java/com/odweta/solon/Parsing.kt @@ -153,12 +153,15 @@ class Parsing { subject.name = subj.getJSONObject("subject").getString("abbrev") subject.place = subj.getJSONArray("rooms").getJSONObject(0).getString("abbrev") - subject.teacher = if (subj.getJSONArray("teachers").length() > 0) { - subj.getJSONArray("teachers").getJSONObject(0) - .getString("fullName") // TODO: keep in mind that fullName might not be a valid field (name and surname are!) - } else { - "" - } + subject.teacher = + if (subj.getJSONArray("teachers").length() > 0) { + subj.getJSONArray("teachers") + .getJSONObject(0).getString("name") + " " + + subj.getJSONArray("teachers") + .getJSONObject(0).getString("name") + } else { + "" + } subject.substitute = when (type) { "ROZVRH" -> "0" "SUPLOVANI" -> "1" @@ -322,7 +325,6 @@ class Parsing { timetable.add(day) } - Log.d("debug", "timetable: $timetable") return timetable } diff --git a/mobile/app/src/main/java/com/odweta/solon/SignIn.kt b/mobile/app/src/main/java/com/odweta/solon/SignIn.kt index 48e56c3..f29c248 100644 --- a/mobile/app/src/main/java/com/odweta/solon/SignIn.kt +++ b/mobile/app/src/main/java/com/odweta/solon/SignIn.kt @@ -1,6 +1,7 @@ package com.odweta.solon import android.content.Context +import android.util.Log import android.widget.Toast import androidx.activity.compose.BackHandler import androidx.compose.foundation.Image @@ -40,6 +41,7 @@ import androidx.compose.foundation.layout.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext private var buttonText by mutableStateOf("Přihlásit se") @@ -96,11 +98,13 @@ fun signInButtonCallback(ctx: Context, usernameText: String, passwordText: Strin global.currentScreen = Screen.Splash launchStart(ctx) } else { - Toast.makeText( - ctx, - "Neplatné přihlašovací údaje nebo nejste připojeni k internetu, zkuste to znovu.", - Toast.LENGTH_LONG - ).show() + withContext(Dispatchers.Main) { + Toast.makeText( + ctx, + "Neplatné přihlašovací údaje nebo nejste připojeni k internetu, zkuste to znovu.", + Toast.LENGTH_LONG + ).show() + } } } } @@ -115,7 +119,6 @@ fun SignInScreen() { var usernameText by remember { mutableStateOf("") } var passwordText by remember { mutableStateOf("") } val ctx = LocalContext.current - val coscope = rememberCoroutineScope() var isPasswordVisible by remember { mutableStateOf(false) } // State for visibility toggle val visualTransformation = if (isPasswordVisible) VisualTransformation.None else PasswordVisualTransformation() diff --git a/mobile/app/src/main/java/com/odweta/solon/Timetable.kt b/mobile/app/src/main/java/com/odweta/solon/Timetable.kt index 47c4b8c..0357015 100644 --- a/mobile/app/src/main/java/com/odweta/solon/Timetable.kt +++ b/mobile/app/src/main/java/com/odweta/solon/Timetable.kt @@ -356,6 +356,8 @@ private fun DayScreen(day: Day) { ) { for ((subjectIndex, subject) in day.subjects.withIndex()) { val lectureId = Util.getLectureId(day.date, subjectIndex) + global.lectureNotes[lectureId]?.number = subject.number.toInt() + global.lectureNotes[lectureId]?.lectureName = subject.name val amoledModifier = if (settings.useAmoledMode.checked.value) { Modifier