package com.schoolapp.cleverclass import android.app.TimePickerDialog import android.content.Context import android.content.Intent import android.icu.text.DecimalFormat import android.icu.text.DecimalFormatSymbols import android.icu.util.Calendar import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxHeight 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.text.KeyboardOptions import androidx.compose.foundation.text.selection.TextSelectionColors import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material.icons.outlined.Edit import androidx.compose.material.icons.outlined.Info import androidx.compose.material.ripple.LocalRippleTheme import androidx.compose.material.ripple.RippleAlpha import androidx.compose.material.ripple.RippleTheme import androidx.compose.material3.AlertDialog import androidx.compose.material3.Button import androidx.compose.material3.Checkbox import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.FilledTonalButton import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedButton import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Surface import androidx.compose.material3.Tab import androidx.compose.material3.TabRow import androidx.compose.material3.Text import androidx.compose.material3.TextFieldDefaults import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp import androidx.core.content.ContextCompat.startActivity import com.schoolapp.cleverclass.LessonStoreManager.getLessons import com.schoolapp.cleverclass.LessonStoreManager.saveLessons import com.schoolapp.cleverclass.ui.theme.CleverClassTheme import com.schoolapp.cleverclass.ui.theme.InputPrimaryColor import com.schoolapp.cleverclass.ui.theme.InputSecondaryColor import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import java.util.Locale private lateinit var activityType : String private lateinit var activityState : String private val listOfDays = listOf("Mo", "Di", "Mi", "Do", "Fr") //Custom ripple to remove effect when touching tabs private class CustomRippleTheme : RippleTheme { @Composable override fun defaultColor(): Color = Color.Unspecified @Composable override fun rippleAlpha(): RippleAlpha = RippleAlpha( draggedAlpha = 0f, focusedAlpha = 0f, hoveredAlpha = 0f, pressedAlpha = 0f ) } class TimeTableSetupActivity : ComponentActivity() { companion object{ const val TYPE_KEY = "type_key" const val STATE_KEY = "state_key" } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) //Defines setup type --> Time vs. Day activityType = intent.getStringExtra(TYPE_KEY).toString() //Defines curent day of week activityState = intent.getStringExtra(STATE_KEY).toString() //Load lessons of defined day from activityState var lessons = listOf() if (activityType == "DaySetup") { CoroutineScope(Dispatchers.IO).launch { getLessons(this@TimeTableSetupActivity, listOfDays[activityState.toInt()]).collect { savedLessons -> lessons = savedLessons.toList() } } } setContent { CleverClassTheme { Surface( modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background ) { TimeTableSetupContent(activity = this, lessons) } } } } } // Content of TimeTableSetup @OptIn(ExperimentalMaterial3Api::class) @Composable fun TimeTableSetupContent(activity: ComponentActivity, loadedLessons: List){ //One-time definition of the inputField colorscheme val inputFieldColors = TextFieldDefaults.outlinedTextFieldColors( textColor = MaterialTheme.colorScheme.onPrimaryContainer, containerColor = MaterialTheme.colorScheme.secondaryContainer, cursorColor = InputPrimaryColor, selectionColors = TextSelectionColors( handleColor = InputPrimaryColor, backgroundColor = InputSecondaryColor ), focusedBorderColor = InputSecondaryColor, unfocusedBorderColor = Color.Transparent ) //Declaration of most sharedPreferences-stored data val sharedPreferences = activity.getSharedPreferences("TimeTable", Context.MODE_PRIVATE) val editor = sharedPreferences.edit() //Length of one lesson var lessonLength by remember { mutableStateOf(sharedPreferences.getInt("lessonLength", 0).toString()) } //Number of breaks on one day var breakAmnt by remember { mutableStateOf(sharedPreferences.getInt("breakAmnt", 0).toString()) } //Beginning time of the first lesson (Format: hh.mm) var firstLesson by remember { mutableStateOf(sharedPreferences.getFloat("firstLesson", 0.0f)) } //Value to determine whether the first setup will be loaded again var setupDone by remember { mutableStateOf(sharedPreferences.getBoolean("setupDone", false)) } //AlertDialog var showInfo by remember { mutableStateOf(false) } //Variable for loaded lessons from onCreate() var lessons by remember { mutableStateOf(loadedLessons) } Column { TopAppBar( colors = TopAppBarDefaults.centerAlignedTopAppBarColors(MaterialTheme.colorScheme.primaryContainer), title = { Text(text = "") }, navigationIcon = { IconButton( onClick = { activity.finish() if (setupDone) { val intent = Intent(activity, StundenplanActivity::class.java) intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) startActivity(activity, intent, null) } else { val intent = Intent(activity, MainActivity::class.java) intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) startActivity(activity, intent, null) } } ) { Icon( imageVector = Icons.Filled.ArrowBack, contentDescription = null, modifier = Modifier.size(28.dp), tint = MaterialTheme.colorScheme.onPrimaryContainer ) } }, modifier = Modifier.fillMaxWidth() ) Column( verticalArrangement = Arrangement.Center, modifier = Modifier .fillMaxHeight() .verticalScroll(rememberScrollState()) ) { //Background Surface( color = MaterialTheme.colorScheme.primaryContainer, shape = RoundedCornerShape(20), modifier = Modifier.padding(16.dp) ) { Column(horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier.padding(16.dp) ) { //Title Row(verticalAlignment = Alignment.CenterVertically) { Text( text = "Stundenplan Setup", style = MaterialTheme.typography.labelMedium, color = MaterialTheme.colorScheme.onPrimaryContainer, modifier = Modifier.padding(10.dp) ) Spacer(modifier = Modifier.weight(1f)) //Info Button --> AlertDialog IconButton(onClick = { showInfo = !showInfo }) { Icon( imageVector = Icons.Outlined.Info, contentDescription = null, tint = MaterialTheme.colorScheme.onPrimaryContainer ) } } Spacer(modifier = Modifier.height(16.dp)) //Setup for basic time data (lesson/break length etc.) if (activityType == "TimeSetup") { //Time input begin of first lesson Text( text = "Beginn der 1. Stunde", modifier = Modifier.fillMaxWidth(), style = MaterialTheme.typography.labelMedium ) OutlinedButton( onClick = { val c = Calendar.getInstance() val mHour = c.get(Calendar.HOUR_OF_DAY) val mMinute = c.get(Calendar.MINUTE) val timePickerDialog = TimePickerDialog(activity, TimePickerDialog.OnTimeSetListener(function = { view, h, m -> firstLesson = h.toFloat() + (m.toFloat() / 100) editor.putFloat("firstLesson", firstLesson) }), mHour, mMinute, true) timePickerDialog.show() }, modifier = Modifier.align(Alignment.Start) ) { val locale = Locale.getDefault() val symbols = DecimalFormatSymbols(locale) symbols.decimalSeparator = ':' val decimalFormat = DecimalFormat("00.00", symbols) Text( text = decimalFormat.format(firstLesson) + " Uhr ", color = MaterialTheme.colorScheme.onPrimaryContainer) Icon(imageVector = Icons.Outlined.Edit, contentDescription = null) } Spacer(modifier = Modifier.height(16.dp)) //Duration of one lesson OutlinedTextField( value = lessonLength, onValueChange = { lessonLength = it }, label = { Text(text = "Länge einer Schulstunde in min") }, modifier = Modifier.fillMaxWidth(), singleLine = true, shape = RoundedCornerShape(20), textStyle = MaterialTheme.typography.labelMedium, colors = inputFieldColors, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.NumberPassword) ) Spacer(modifier = Modifier.height(16.dp)) //Input amount of breaks OutlinedTextField( value = breakAmnt, onValueChange = { breakAmnt = it}, label = { Text(text = "Anzahl der Pausen") }, modifier = Modifier.fillMaxWidth(), singleLine = true, shape = RoundedCornerShape(20), textStyle = MaterialTheme.typography.labelMedium, colors = inputFieldColors, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.NumberPassword) ) //Input of length and start time for each break if (breakAmnt != "") { //declaration of sharedPreferences for break data for (i in 0 until breakAmnt.toInt()) { var currentBreakStartTime by remember{ mutableStateOf(sharedPreferences.getFloat("break${i}StartTime", 0.0f)) } var currentBreakLength by remember{ mutableStateOf(sharedPreferences.getInt("break${i}Length", 0).toString()) } Spacer(modifier = Modifier.height(16.dp)) //Input start time (break) Text( text = "Startzeit der ${i + 1}. Pause:", modifier = Modifier.fillMaxWidth(), style = MaterialTheme.typography.labelMedium ) OutlinedButton( onClick = { val c = Calendar.getInstance() val mHour = c.get(Calendar.HOUR_OF_DAY) val mMinute = c.get(Calendar.MINUTE) val timePickerDialog = TimePickerDialog(activity, TimePickerDialog.OnTimeSetListener(function = { view, h, m -> currentBreakStartTime = h.toFloat() + (m.toFloat() / 100) editor.putFloat("break${i}StartTime", currentBreakStartTime) }), mHour, mMinute, true) timePickerDialog.show() }, modifier = Modifier.align(Alignment.Start) ) { val locale = Locale.getDefault() val symbols = DecimalFormatSymbols(locale) symbols.decimalSeparator = ':' val decimalFormat = DecimalFormat("00.00", symbols) Text( text = decimalFormat.format(currentBreakStartTime) + " Uhr ", color = MaterialTheme.colorScheme.onPrimaryContainer, style = MaterialTheme.typography.labelMedium ) Icon(imageVector = Icons.Outlined.Edit, contentDescription = null) } Spacer(modifier = Modifier.height(5.dp)) //Input duration (break) OutlinedTextField( value = currentBreakLength, onValueChange = { currentBreakLength = it if (currentBreakLength != "") { editor.putInt("break${i}Length", currentBreakLength.toInt()) }}, label = { Text(text = "Länge der ${i + 1}. Pause in min") }, modifier = Modifier.fillMaxWidth(), singleLine = true, shape = RoundedCornerShape(20), textStyle = MaterialTheme.typography.labelMedium, colors = inputFieldColors, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.NumberPassword) ) } } Spacer(modifier = Modifier.height(10.dp)) Row { //Go back to StundenplanActivity FilledTonalButton( onClick = { editor.putInt("lessonLength", lessonLength.toInt()) editor.putInt("breakAmnt", breakAmnt.toInt()) editor.apply() activity.finish() if (setupDone) { //Reopen of activity so values will be updated val intent = Intent(activity, StundenplanActivity::class.java) //Remove previous StundenplanActivity from activity stack so when going back later it won't exist twice intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) startActivity(activity, intent, null) } else { val intent = Intent(activity, MainActivity::class.java) intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) startActivity(activity, intent, null) } }, modifier = Modifier .padding(5.dp) .weight(1f) ) { Text( text = "Zurück", color = MaterialTheme.colorScheme.secondary, style = MaterialTheme.typography.labelMedium ) } Spacer(modifier = Modifier .width(5.dp) ) //Move on to DaySetup Button( onClick = { editor.putInt("lessonLength", lessonLength.toInt()) editor.putInt("breakAmnt", breakAmnt.toInt()) editor.apply() activity.finish() val intent = Intent(activity, TimeTableSetupActivity::class.java).apply { putExtra(TimeTableSetupActivity.TYPE_KEY, "DaySetup") //State 0 for indexing days (0..4) putExtra(TimeTableSetupActivity.STATE_KEY, "0") } startActivity(activity, intent, null) }, modifier = Modifier .padding(5.dp) .weight(1f) ) { Text( text = "Weiter", style = MaterialTheme.typography.labelMedium ) } } } //DaySetup for information on the lessons (teacher, subject, amount per day, etc.) else if (activityType == "DaySetup") { //State that defines the current day (e.g. for LessonData or the Tab highlighting) val state by remember { mutableStateOf(activityState.toInt()) } //SharedPreferences for the amount of hours of each day (indexed by state --> 0..4) var currentLessonAmount by remember { mutableStateOf(sharedPreferences.getInt("lessonAmount${listOfDays[state]}", 0).toString()) } //TabRow with Tabs to visualize the days; no onClick fun and thus the custom ripple theme CompositionLocalProvider(LocalRippleTheme provides CustomRippleTheme()) { TabRow(selectedTabIndex = state, containerColor = MaterialTheme.colorScheme.primaryContainer) { for (day in listOfDays) { Tab(selected = false, onClick = {}, modifier = Modifier.padding(5.dp) ) { Text( text = day, color = MaterialTheme.colorScheme.onPrimaryContainer, style = MaterialTheme.typography.labelMedium ) } } } } Spacer(modifier = Modifier.height(16.dp)) //Input of the amount of lessons on the current day OutlinedTextField( value = currentLessonAmount, onValueChange = { currentLessonAmount = it if (currentLessonAmount == "") { editor.putInt("lessonAmount${listOfDays[state]}", 0) } else { editor.putInt("lessonAmount${listOfDays[state]}", currentLessonAmount.toInt()) } }, label = { Text(text = "Anzahl der Schulstunden (${listOfDays[state]})") }, modifier = Modifier.fillMaxWidth(), singleLine = true, shape = RoundedCornerShape(20), textStyle = MaterialTheme.typography.labelMedium, colors = inputFieldColors, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.NumberPassword) ) //Creation of TextField for further input for the lessons themselves + saveUsage into DataStore --> saveLessonUsage(...) if (currentLessonAmount != "") { if (currentLessonAmount != "0") { //Creation/Removal of the current amount of LessonData object depending pn currentLessonAmount for (i in lessons.size until currentLessonAmount.toInt()) { lessons = lessons + LessonData("", "", "") } while (currentLessonAmount.toInt() < lessons.size) { lessons = lessons.dropLast(1) } lessons.forEachIndexed { index, lessonData -> //Input variables var currentSubject by remember { mutableStateOf(lessonData.subject) } var currentTeacher by remember { mutableStateOf(lessonData.teacher) } var currentRoom by remember { mutableStateOf(lessonData.room) } //Input of the subject of the current lesson OutlinedTextField( value = currentSubject, onValueChange = { currentSubject = it lessonData.subject = it saveLessonsUsage(activity, lessons, state) }, label = { Text(text = "Fach der ${index + 1}. Stunde") }, modifier = Modifier.fillMaxWidth(), singleLine = true, shape = RoundedCornerShape(20), textStyle = MaterialTheme.typography.labelMedium, colors = inputFieldColors, ) //Input of the room of the current lesson OutlinedTextField( value = currentRoom, onValueChange = { currentRoom = it lessonData.room = it saveLessonsUsage(activity, lessons, state) }, label = { Text(text = "Raum der ${index + 1}. Stunde") }, modifier = Modifier.fillMaxWidth(), singleLine = true, shape = RoundedCornerShape(20), textStyle = MaterialTheme.typography.labelMedium, colors = inputFieldColors, ) //Input of the teacher of the current lesson OutlinedTextField( value = currentTeacher, onValueChange = { currentTeacher = it lessonData.teacher = it saveLessonsUsage(activity, lessons, state) }, label = { Text(text = "Lehrer der ${index + 1}. Stunde") }, modifier = Modifier.fillMaxWidth(), singleLine = true, shape = RoundedCornerShape(20), textStyle = MaterialTheme.typography.labelMedium, colors = inputFieldColors, ) } saveLessonsUsage(activity, lessons, state) } //save an empty list into DataStore currentLessonAmount == 0 else { saveLessonsUsage(activity, emptyList(), state) } } Spacer(modifier = Modifier.height(16.dp)) //Checkbox on the friday page (state == 4) to prevent setup from automatically openening next time if (state == 4) { Box( modifier = Modifier .height(2.dp) .fillMaxWidth() .background(color = MaterialTheme.colorScheme.onPrimaryContainer) ) Row { Text( text = "Ertes Setup beendet?", modifier = Modifier.align(Alignment.CenterVertically), style = MaterialTheme.typography.labelMedium ) Checkbox(checked = setupDone, onCheckedChange = {setupDone = !setupDone}) } } //Go back Row { FilledTonalButton( onClick = { activity.finish() //To TimeSetup if (state == 0) { val intent = Intent(activity, TimeTableSetupActivity::class.java).apply { putExtra(TimeTableSetupActivity.TYPE_KEY, "TimeSetup") } startActivity(activity, intent, null) } //To the day before (STATE_KEY -= 1) else { val intent = Intent(activity, TimeTableSetupActivity::class.java).apply { putExtra(TimeTableSetupActivity.TYPE_KEY, "DaySetup") putExtra(TimeTableSetupActivity.STATE_KEY, "${state - 1}") } startActivity(activity, intent, null) } }, modifier = Modifier .padding(5.dp) .weight(1f) ) { Text( text = "Zurück", color = MaterialTheme.colorScheme.secondary, style = MaterialTheme.typography.labelMedium ) } Spacer(modifier = Modifier .width(5.dp) ) //Go forwards Button( onClick = { activity.finish() //To next day (STATE_KEY += 1) if (state != 4) { val intent = Intent(activity, TimeTableSetupActivity::class.java).apply { putExtra(TimeTableSetupActivity.TYPE_KEY, "DaySetup") putExtra(TimeTableSetupActivity.STATE_KEY, "${state + 1}") } startActivity(activity, intent, null) } //To Stundenplan else { editor.putBoolean("setupDone", setupDone) //Reopen of activity so values will be updated val intent = Intent(activity, StundenplanActivity::class.java) //Remove previous StundenplanActivity from activity stack so when going back later it won't exist twice intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) startActivity(activity, intent, null) } editor.apply() }, modifier = Modifier .padding(5.dp) .weight(1f) ) { Text( text = "Weiter", style = MaterialTheme.typography.labelMedium ) } } } } } } } //AlertDialog to inform the user of the possibility of changing all inputs later if (showInfo) { AlertDialog( onDismissRequest = { showInfo = false }, text = { Text( text = "Alle Eingaben können später geändert werden.", color = MaterialTheme.colorScheme.onPrimaryContainer, style = MaterialTheme.typography.labelMedium) }, confirmButton = { Text( text = "Schließen", color = MaterialTheme.colorScheme.secondary, style = MaterialTheme.typography.labelMedium, modifier = Modifier.clickable { showInfo = false } ) }, containerColor = MaterialTheme.colorScheme.primaryContainer, modifier = Modifier .padding(5.dp) ) } } //Function that implements the save of List fun saveLessonsUsage(context: Context, lessons: List, int: Int) { CoroutineScope(Dispatchers.IO).launch { saveLessons(context, lessons, listOfDays[int]) } }