Add save function to grades

This commit is contained in:
BuildTools
2024-04-16 22:45:54 +02:00
parent 46f0ae21a6
commit 100dfd9c42
8 changed files with 250 additions and 86 deletions

2
.idea/compiler.xml generated
View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="17" />
<bytecodeTargetLevel target="18" />
</component>
</project>

3
.idea/gradle.xml generated
View File

@@ -5,8 +5,9 @@
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="testRunner" value="GRADLE" />
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
<option name="gradleJvm" value="18" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />

2
.idea/misc.xml generated
View File

@@ -1,6 +1,6 @@
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_18" default="true" project-jdk-name="18" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">

View File

@@ -50,38 +50,21 @@ android {
}
dependencies {
implementation("androidx.core:core-ktx:1.9.0")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.7.0")
implementation("androidx.activity:activity-compose:1.8.2")
implementation(platform("androidx.compose:compose-bom:2023.03.00"))
implementation("androidx.compose.ui:ui")
implementation("androidx.compose.ui:ui-graphics")
implementation("androidx.compose.ui:ui-tooling-preview")
implementation("androidx.compose.material3:material3")
implementation(platform("androidx.compose:compose-bom:2023.03.00"))
implementation(platform("androidx.compose:compose-bom:2023.03.00"))
implementation(platform("androidx.compose:compose-bom:2023.03.00"))
implementation(platform("androidx.compose:compose-bom:2023.03.00"))
implementation(platform("androidx.compose:compose-bom:2023.03.00"))
implementation(platform("androidx.compose:compose-bom:2023.03.00"))
implementation(platform("androidx.compose:compose-bom:2023.03.00"))
implementation(platform("androidx.compose:compose-bom:2023.03.00"))
implementation(platform("androidx.compose:compose-bom:2023.03.00"))
implementation("androidx.datastore:datastore-preferences:1.0.0")
implementation("com.google.code.gson:gson:2.9.0")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
androidTestImplementation(platform("androidx.compose:compose-bom:2023.03.00"))
androidTestImplementation("androidx.compose.ui:ui-test-junit4")
androidTestImplementation(platform("androidx.compose:compose-bom:2023.03.00"))
androidTestImplementation(platform("androidx.compose:compose-bom:2023.03.00"))
androidTestImplementation(platform("androidx.compose:compose-bom:2023.03.00"))
androidTestImplementation(platform("androidx.compose:compose-bom:2023.03.00"))
androidTestImplementation(platform("androidx.compose:compose-bom:2023.03.00"))
androidTestImplementation(platform("androidx.compose:compose-bom:2023.03.00"))
androidTestImplementation(platform("androidx.compose:compose-bom:2023.03.00"))
androidTestImplementation(platform("androidx.compose:compose-bom:2023.03.00"))
androidTestImplementation(platform("androidx.compose:compose-bom:2023.03.00"))
debugImplementation("androidx.compose.ui:ui-tooling")
debugImplementation("androidx.compose.ui:ui-test-manifest")
}

View File

@@ -39,6 +39,8 @@ import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -47,18 +49,47 @@ import androidx.compose.ui.Alignment.Companion.CenterVertically
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import com.schoolapp.cleverclass.GradeStoreManager.getGrades
import com.schoolapp.cleverclass.GradeStoreManager.saveGrades
import com.schoolapp.cleverclass.ui.theme.CleverClassTheme
import com.schoolapp.cleverclass.ui.theme.TextOnColouredButton
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
var SAWeight = 0
var name = "Test"
var typesSelection = listOf("Test", "Ex", "Referat", "Mündlich", "Sonstiges")
val gradesSelection = listOf(1, 2, 3, 4, 5, 6)
val weightsSelection = listOf(0.5f, 1f, 1.5f, 2f)
class FachActivity : ComponentActivity() {
companion object {
const val ARGUMENT_KEY = "argument_key"
const val NAME_KEY = "name_key"
const val WEIGHT_KEY = "weight_key"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// get subject argument from NotenActivity
val name = intent.getStringExtra(ARGUMENT_KEY) ?: "missing subject"
var grades = emptyList<GradeData>()
// get arguments from NotenActivity
name = intent.getStringExtra(NAME_KEY) ?: "missing subject"
SAWeight = intent.getIntExtra(WEIGHT_KEY, 0)
if(SAWeight != 0 && typesSelection[0] != "Schulaufgabe")
typesSelection = listOf("Schulaufgabe") + typesSelection
CoroutineScope(Dispatchers.IO).launch {
getGrades(this@FachActivity, name).collect { savedGrades ->
grades = savedGrades.toMutableList()
println()
}
}
setContent {
CleverClassTheme {
@@ -66,16 +97,29 @@ class FachActivity : ComponentActivity() {
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
GradeContent(activity = this, name = name)
GradeContent(this, grades)
}
}
}
}
/* TODO: save data when activity is destroyed
override fun onDestroy() {
super.onDestroy()
} */
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun GradeContent(activity: ComponentActivity, name : String){
fun GradeContent(activity: ComponentActivity, loadedGrades: List<GradeData>){
var grades by remember {
mutableStateOf(loadedGrades)
}
var average by remember {
mutableStateOf(0.00f)
}
val colors = listOf(
Color(0xFF536DFE),
Color(0xFF448AFF),
@@ -84,8 +128,17 @@ fun GradeContent(activity: ComponentActivity, name : String){
Color(0xFF64FFDA),
Color(0xFF69F0AE)
)
var grades by remember {
mutableStateOf(emptyList<GradeData>())
LaunchedEffect(grades) {
average = calculateAverage(grades)
}
DisposableEffect(activity) {
onDispose {
CoroutineScope(Dispatchers.IO).launch {
saveGrades(activity, grades, name)
}
}
}
Scaffold(
@@ -97,7 +150,12 @@ fun GradeContent(activity: ComponentActivity, name : String){
style = MaterialTheme.typography.headlineSmall
)},
navigationIcon = {
IconButton(onClick = { activity.finish() }) {
IconButton(onClick = {
CoroutineScope(Dispatchers.IO).launch {
saveGrades(activity, grades, name)
}
activity.finish()
}) {
Icon(
imageVector = Icons.Filled.ArrowBack,
contentDescription = null,
@@ -132,7 +190,8 @@ fun GradeContent(activity: ComponentActivity, name : String){
Text(text = "Schnitt:",
style = MaterialTheme.typography.headlineSmall)
Spacer(modifier = Modifier.weight(1f))
Text(text = "Ø0.00",
Text(
text = "Ø" + if(average.isNaN()) "0.0" else formatFloat(average),
style = MaterialTheme.typography.headlineSmall)
Spacer(modifier = Modifier.width(16.dp))
}
@@ -140,7 +199,12 @@ fun GradeContent(activity: ComponentActivity, name : String){
},
floatingActionButton = {
FloatingActionButton(
onClick = { grades = grades + GradeData(grades.size, colors.random()) },
onClick = { grades = grades + GradeData(
if (grades.isEmpty()) 0 else grades[grades.size - 1].id + 1,
colors.random(),
1,
1.0f,
"Typ") },
shape = RoundedCornerShape(40)
) {
Icon(imageVector = Icons.Filled.Add,
@@ -154,20 +218,41 @@ fun GradeContent(activity: ComponentActivity, name : String){
.verticalScroll(rememberScrollState()))
{
grades.forEach{ gradeData ->
GradePrefab(onClose = { grades = grades.filter { it != gradeData } }, color = gradeData.color)
GradePrefab(
onClose = { grades = grades.filter { it.id != gradeData.id } },
type = gradeData.type,
grade = gradeData.grade,
weight = gradeData.weight,
color = gradeData.color,
onGradeChange = { grade ->
grades.find { it.id == gradeData.id }?.grade = grade
average = calculateAverage(grades) },
onWeightChange = { weight ->
grades.find { it.id == gradeData.id }?.weight = weight
average = calculateAverage(grades) },
onTypeChange = { type ->
grades.find { it.id == gradeData.id }?.type = type
average = calculateAverage(grades) })
}
Spacer(modifier = Modifier.height(216.dp))
}
}
}
val types = listOf("Schulaufgabe", "Test", "Ex", "Referat", "Mündlich", "Sonstiges")
val grades = listOf(1, 2, 3, 4, 5, 6)
val weights = listOf(0.5f, 1f, 1.5f, 2f)
@Composable
fun GradePrefab(onClose: () -> Unit, color: Color){
fun GradePrefab(
onClose: () -> Unit,
type: String,
grade: Int,
weight: Float,
color: Color,
onGradeChange: (grade: Int) -> Unit,
onWeightChange: (weight: Float) -> Unit,
onTypeChange: (type: String) -> Unit
){
var editing by remember {
mutableStateOf(true)
mutableStateOf(false)
}
var typeExpanded by remember {
@@ -181,13 +266,13 @@ fun GradePrefab(onClose: () -> Unit, color: Color){
}
var selectedType by remember {
mutableStateOf("Typ")
mutableStateOf(type)
}
var selectedGrade by remember {
mutableStateOf(1)
mutableStateOf(grade)
}
var selectedWeight by remember {
mutableStateOf(1f)
mutableStateOf(weight)
}
// Background
@@ -221,12 +306,13 @@ fun GradePrefab(onClose: () -> Unit, color: Color){
expanded = typeExpanded,
onDismissRequest = { typeExpanded = false }
){
types.forEach { type ->
typesSelection.forEach { type ->
Item(
name = type,
onItemClick = {
selectedType = type
typeExpanded = !typeExpanded
onTypeChange(selectedType)
}
)
}
@@ -299,12 +385,13 @@ fun GradePrefab(onClose: () -> Unit, color: Color){
DropdownMenu(
expanded = gradeExpanded,
onDismissRequest = { gradeExpanded = false }) {
grades.forEach { grade ->
gradesSelection.forEach { grade ->
Item(
name = grade.toString(),
onItemClick = {
selectedGrade = grade
gradeExpanded = !gradeExpanded
onGradeChange(selectedGrade)
}
)
}
@@ -320,41 +407,58 @@ fun GradePrefab(onClose: () -> Unit, color: Color){
}
// Weight
Spacer(modifier = Modifier.width(8.dp))
Text(text = "Gewichtung:", color = TextOnColouredButton, style = MaterialTheme.typography.labelMedium)
Spacer(modifier = Modifier.width(8.dp))
Surface(
shape = RoundedCornerShape(10),
color = color,
border = if(editing) BorderStroke(2.dp, TextOnColouredButton) else null,
modifier = Modifier
.size(width = 80.dp, height = 32.dp)
.let { if (editing) it.clickable { weightExpanded = !weightExpanded } else it }
) {
Row(verticalAlignment = CenterVertically,
modifier = Modifier.padding(4.dp)) {
Text(text = "$selectedWeight" + "x", color = TextOnColouredButton, style = MaterialTheme.typography.labelMedium)
if (editing) {
DropdownMenu(
expanded = weightExpanded,
onDismissRequest = { weightExpanded = false }) {
weights.forEach { weight ->
Item(
name = weight.toString(),
onItemClick = {
selectedWeight = weight
weightExpanded = !weightExpanded
}
)
}
if (selectedType != "Schulaufgabe") {
Spacer(modifier = Modifier.width(8.dp))
Text(
text = "Gewichtung:",
color = TextOnColouredButton,
style = MaterialTheme.typography.labelMedium
)
Spacer(modifier = Modifier.width(8.dp))
Surface(
shape = RoundedCornerShape(10),
color = color,
border = if (editing) BorderStroke(2.dp, TextOnColouredButton) else null,
modifier = Modifier
.size(width = 80.dp, height = 32.dp)
.let {
if (editing) it.clickable {
weightExpanded = !weightExpanded
} else it
}
Spacer(modifier = Modifier.weight(1f))
Icon(
imageVector = if (weightExpanded) Icons.Filled.KeyboardArrowUp else Icons.Filled.KeyboardArrowDown,
contentDescription = null,
tint = TextOnColouredButton
) {
Row(
verticalAlignment = CenterVertically,
modifier = Modifier.padding(4.dp)
) {
Text(
text = "$selectedWeight" + "x",
color = TextOnColouredButton,
style = MaterialTheme.typography.labelMedium
)
if (editing) {
DropdownMenu(
expanded = weightExpanded,
onDismissRequest = { weightExpanded = false }) {
weightsSelection.forEach { weight ->
Item(
name = weight.toString(),
onItemClick = {
selectedWeight = weight
weightExpanded = !weightExpanded
onWeightChange(selectedWeight)
}
)
}
}
Spacer(modifier = Modifier.weight(1f))
Icon(
imageVector = if (weightExpanded) Icons.Filled.KeyboardArrowUp else Icons.Filled.KeyboardArrowDown,
contentDescription = null,
tint = TextOnColouredButton
)
}
}
}
}
@@ -371,5 +475,47 @@ fun Item(name : String, onItemClick: (String) -> Unit){
)
}
data class GradeData(
val id: Int,
val color: Color,
var grade: Int,
var weight: Float,
var type: String
)
data class GradeData(val id: Int, val color: Color)
fun calculateAverage(grades: List<GradeData>): Float {
if (grades.isEmpty()) return 0.0f
var totalGrade = 0.0f
var totalWeights = 0.0f
var saTotalGrade = 0.0f
var saTotalWeights = 0.0f
grades.forEach { gradeData ->
if (gradeData.type != "Typ")
if (gradeData.type == "Schulaufgabe" && SAWeight != 0) {
saTotalGrade += gradeData.grade
saTotalWeights += 1
} else {
totalGrade += gradeData.grade * gradeData.weight
totalWeights += gradeData.weight
}
}
val smallAverage = totalGrade / totalWeights
val saAverage = saTotalGrade / saTotalWeights
return if (smallAverage.isNaN()) saAverage
else if (saAverage.isNaN()) smallAverage
else (smallAverage + saAverage * SAWeight) / (1 + SAWeight)
}
fun formatFloat(number: Float): String {
var formattedString = number.toString()
formattedString = formattedString.substring(0, if(formattedString.length == 3) 3 else 4)
return formattedString
}

View File

@@ -0,0 +1,31 @@
package com.schoolapp.cleverclass
import android.content.Context
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.preferencesDataStore
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
object GradeStoreManager {
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "grades_preferences")
suspend fun saveGrades(context: Context, grades: List<GradeData>, subject: String) {
context.dataStore.edit { preferences ->
val json = Gson().toJson(grades)
preferences[stringPreferencesKey(subject)] = json
}
}
fun getGrades(context: Context, subject: String): Flow<List<GradeData>> {
return context.dataStore.data.map { preferences ->
val json = preferences[stringPreferencesKey(subject)] ?: return@map emptyList()
val type = object : TypeToken<List<GradeData>>() {}.type
Gson().fromJson(json, type)
}
}
}

View File

@@ -74,20 +74,23 @@ fun NotenContent(activity: ComponentActivity){
)
Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
FachButton(color = Color(0xFF69F0AE), text = "Deutsch", 3.66f, activity)
FachButton(color = Color(0xFFEEFF41), text = "Mathe", 1.56f, activity)
FachButton(color = Color(0xFFFFAB40), text = "Englisch", 2.27f, activity)
FachButton(color = Color(0xFF18FFFF), text = "Latein", 4.75f, activity)
FachButton(color = Color(0xFFB2FF59), text = "Französisch", 2.33f, activity)
FachButton(color = Color(0xFF69F0AE), text = "Deutsch", 0, 3.66f, activity)
FachButton(color = Color(0xFFEEFF41), text = "Mathe",2, 1.56f, activity)
FachButton(color = Color(0xFFFFAB40), text = "Englisch",2, 2.27f, activity)
FachButton(color = Color(0xFF18FFFF), text = "Latein",2, 4.75f, activity)
FachButton(color = Color(0xFFB2FF59), text = "Französisch",2, 2.33f, activity)
}
}
}
@Composable
fun FachButton(color : Color, text : String, average : Float, activity : ComponentActivity){
fun FachButton(color : Color, text : String, saWeight: Int, average : Float, activity : ComponentActivity){
Button(
onClick = {
val intent = Intent(activity, FachActivity::class.java).apply { putExtra(FachActivity.ARGUMENT_KEY, text) }
val intent = Intent(activity, FachActivity::class.java).apply {
putExtra(FachActivity.NAME_KEY, text)
putExtra(FachActivity.WEIGHT_KEY, saWeight)
}
activity.startActivity(intent) },
shape = RoundedCornerShape(40),
colors = ButtonDefaults.outlinedButtonColors(containerColor = color),

View File

@@ -5,7 +5,7 @@
-->
<data-extraction-rules>
<cloud-backup>
<!-- TODO: Use <include> and <exclude> to control what is backed up.
<!-- Use <include> and <exclude> to control what is backed up.
<include .../>
<exclude .../>
-->