Compare commits

...

19 Commits

Author SHA1 Message Date
Paul
442ef70b8d update AboutActivity 2024-10-24 13:59:01 +02:00
BuildTools
ec3a95fc2d Merge remote-tracking branch 'origin/master' 2024-10-14 15:26:13 +02:00
BuildTools
b07cd27fce Why tf is "Index": 0 @ index = 1 and "Index": 2 @ index = 0 2024-10-14 15:25:58 +02:00
matthias
39c037a9ab delete empty line 2024-07-05 17:44:51 +02:00
matthias
f6956349e0 Small adjustment
3 month --> 3 months
2024-06-26 06:59:01 +02:00
BuildTools
a41bd9cbb3 Small adjustment 2024-06-21 16:52:28 +02:00
BuildTools
0db1e78cd4 Small adjustments 2024-06-16 13:16:56 +02:00
matthias
7bdcb7e3fb Added restart of app when first setup isn't done
when going back via phone feature, user sees the empty timetable
2024-06-09 23:46:58 +02:00
matthias
d36cadeb18 Fixed typo 2024-06-09 23:45:32 +02:00
BuildTools
1056dc39bf Add deleteAllData 2024-06-09 21:58:28 +02:00
matthias
15cadb76c6 Minor appearance adjustments
added scaling with 1.1f(lesson indexes & "Pause") / 0.8f(times)
2024-06-09 13:22:00 +02:00
matthias
41b54f15ea Major Bugfix
fixed removal of lessons
2024-06-09 13:20:31 +02:00
matthias
50eb19a464 minor code cleanup 2024-06-08 14:16:01 +02:00
matthias
4c3de94dd3 Stundenplan bugfixes
fixed indexing with wrong variable for breaks in core
added compatibility for lessons without subject, room or teacher
added horizontal scroll for too long user input
added info icon with alert dialog for scroll explanation
2024-06-08 14:15:43 +02:00
matthias
30ad4bed64 Stundenplan most definitely finished
Added Commenting to StundenplanActivity and TimeTableSetupActivity
2024-06-07 02:13:02 +02:00
BuildTools
f091451fe8 Add Ko-fi
Remove Langlebigstesisotop
Update stuff
2024-06-06 19:21:33 +02:00
1c665fea26 README.md updated
added android version minimum 8.0 as minimum api level defined in CleverClass/app/build.gradle.kts is 26
enlarged link to the license as version and release date belong to its name
2024-06-05 08:07:46 +00:00
matthias
41be804d5b Stundenplan commit for meeting
Setup most definitely done
Display still has some issues
2024-06-05 06:25:15 +02:00
matthias
173a3fc5a5 code cleanup
changed contentDescription = "" to null
2024-06-05 06:23:35 +02:00
13 changed files with 1026 additions and 514 deletions

View File

@@ -19,14 +19,14 @@ APK-Datei:
- Öffne die heruntergeladene APK-Datei auf deinem Android-Gerät - Öffne die heruntergeladene APK-Datei auf deinem Android-Gerät
- Folge den Anweisungen auf dem Bildschirm, um die Installation abzuschließen - Folge den Anweisungen auf dem Bildschirm, um die Installation abzuschließen
## Anforderungen ## Anforderungen
Betriebssystem: Android Betriebssystem: Android 8.0 oder neuer
## Autoren ## Autoren
- Paul Posch - Paul Posch
- Matthias Meyer - Matthias Meyer
- Jakub Szarko - Jakub Szarko
- Emilian Bührer - Emilian Bührer
## Lizenz ## Lizenz
Dieses Projekt steht unter der [GNU General Public License](LICENSE) Version 3, 29 June 2007. Dieses Projekt steht unter der [GNU General Public License Version 3, 29 June 2007](LICENSE).
## Sonstiges ## Sonstiges
Dieses Projekt ist als Schulprojekt inerhalb von 3 Monaten entstanden. Dieses Projekt ist als Schulprojekt inerhalb von 3 Monaten entstanden.

View File

@@ -23,6 +23,7 @@ android {
buildTypes { buildTypes {
release { release {
isMinifyEnabled = false isMinifyEnabled = false
isDebuggable = false
proguardFiles( proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"), getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro" "proguard-rules.pro"

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,15 @@
package com.schoolapp.cleverclass package com.schoolapp.cleverclass
import android.content.Intent
import android.net.Uri
import android.os.Bundle import android.os.Bundle
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
@@ -14,6 +20,7 @@ import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material3.Divider
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
@@ -23,7 +30,10 @@ import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.schoolapp.cleverclass.ui.theme.CleverClassTheme import com.schoolapp.cleverclass.ui.theme.CleverClassTheme
@@ -73,7 +83,35 @@ fun AboutContent(activity: ComponentActivity){
{ {
AboutTextElement("This Product is published under the\nGNU General Public License\nVersion 3, 29 June 2007", 16.dp) AboutTextElement("This Product is published under the\nGNU General Public License\nVersion 3, 29 June 2007", 16.dp)
AboutTextElement("Developed by:\n- Paul Posch\n- Matthias Meyer\n- Jakub Szarko\n- Emilian Bührer", 32.dp) AboutTextElement("Developed by:\n- Paul Posch\n- Matthias Meyer\n- Jakub Szarko\n- Emilian Bührer", 32.dp)
AboutTextElement("Fun Facts:\nThis app consists of 2.873 lines of code\nThe repository is 413KB big\nThe development took about 3 month", 64.dp) //TODO: Update before launch AboutTextElement("Fun Facts:\nThis app consists of 4.189 lines of code\nThe repository is 768KB big\nThe development took about 3 months", 64.dp)
Spacer(modifier = Modifier.height(32.dp))
Divider()
Box(contentAlignment = Alignment.CenterStart,
modifier = Modifier
.fillMaxWidth()
.clickable {
val intent =
Intent(Intent.ACTION_VIEW, Uri.parse("https://ko-fi.com/cleverclass"))
activity.startActivity(intent)
}
) {
Row {
Text(
text = "Support the Devs",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onBackground,
modifier = Modifier.padding(16.dp)
)
Spacer(modifier = Modifier.weight(1f))
Image(
painter = painterResource(R.drawable.baseline_open_in_browser_24),
contentDescription = null,
colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onBackground),
modifier = Modifier.padding(16.dp)
)
}
}
} }
} }
} }

View File

@@ -127,7 +127,19 @@ private fun decompressGZIPAndDecodeBase64(compressedData: String): String {
private fun downloadImagesTask(jsonResponse: JSONObject, mainDir : File) : Int { private fun downloadImagesTask(jsonResponse: JSONObject, mainDir : File) : Int {
clearFolder(mainDir) clearFolder(mainDir)
val jsonDaysObject = jsonResponse.getJSONArray("ResultMenuItems").getJSONObject(0).getJSONArray("Childs").getJSONObject(0).getJSONObject("Root").getJSONArray("Childs") var jsonDaysObject = jsonResponse.getJSONArray("ResultMenuItems")
for (i in 0 until jsonDaysObject.length()) {
if (jsonDaysObject.getJSONObject(i).get("Index") == 0) {
jsonDaysObject = jsonDaysObject.getJSONObject(i).getJSONArray("Childs")
break
}
}
for (i in 0 until jsonDaysObject.length()) {
if (jsonDaysObject.getJSONObject(i).get("Index") == 0) {
jsonDaysObject = jsonDaysObject.getJSONObject(i).getJSONObject("Root").getJSONArray("Childs")
break
}
}
for (d in 0 until jsonDaysObject.length()) { for (d in 0 until jsonDaysObject.length()) {
val jsonDayObject = jsonDaysObject.getJSONObject(d) val jsonDayObject = jsonDaysObject.getJSONObject(d)
@@ -201,20 +213,6 @@ private fun createDataFolder(context: Context) : File {
return folder return folder
} }
private fun clearFolder(directory: File) {
if (directory.exists()) {
val files = directory.listFiles()
if (files != null) {
for (file in files) {
if (file.isDirectory) {
clearFolder(file)
}
file.delete()
}
}
}
}
private fun isInternetAvailable(context: Context): Boolean { private fun isInternetAvailable(context: Context): Boolean {
val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val network = connectivityManager.activeNetwork val network = connectivityManager.activeNetwork

View File

@@ -86,11 +86,15 @@ fun MainContent(activity: ComponentActivity){
MainButton(onClick = { switchToActivity(activity, StundenplanActivity::class.java) }, color = Color(0xFFFF4081), text = "Stundenplan", sharedPreferences) MainButton(onClick = { switchToActivity(activity, StundenplanActivity::class.java) }, color = Color(0xFFFF4081), text = "Stundenplan", sharedPreferences)
MainButton(onClick = { switchToActivity(activity, NotenActivity::class.java) }, color = Color(0xFFE040FB), text = "Noten", sharedPreferences) MainButton(onClick = { switchToActivity(activity, NotenActivity::class.java) }, color = Color(0xFFE040FB), text = "Noten", sharedPreferences)
MainButton(onClick = { switchToActivity(activity, PSEActivity::class.java) }, color = Color(0xFF536DFE), text = "Periodensystem", sharedPreferences) MainButton(onClick = { switchToActivity(activity, PSEActivity::class.java) }, color = Color(0xFF536DFE), text = "Periodensystem", sharedPreferences)
MainButton(onClick = { MainButton(onClick = { switchToActivity(activity, DSBActivity::class.java) }, color = Color(0xFFFF6E40), text = "DSBmobile", sharedPreferences)
MainButton(
onClick = {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse("https://mebis.bycs.de/")) val intent = Intent(Intent.ACTION_VIEW, Uri.parse("https://mebis.bycs.de/"))
activity.startActivity(intent) activity.startActivity(intent)
}, color = Color(0xFF7C4DFF), text = "Mebis", sharedPreferences) },
MainButton(onClick = { switchToActivity(activity, DSBActivity::class.java) }, color = Color(0xFFFF6E40), text = "DSBmobile", sharedPreferences) color = Color(0xFF7C4DFF),
text = "Mebis", sharedPreferences
)
} }
} }
} }

View File

@@ -152,7 +152,7 @@ fun PSEMainContent(activity: ComponentActivity) {
}) { }) {
Icon( Icon(
imageVector = Icons.Outlined.Info, imageVector = Icons.Outlined.Info,
contentDescription = "", contentDescription = null,
modifier = Modifier.size(28.dp), modifier = Modifier.size(28.dp),
tint = MaterialTheme.colorScheme.onPrimaryContainer tint = MaterialTheme.colorScheme.onPrimaryContainer
) )
@@ -309,6 +309,5 @@ private fun readElementData(int: Int): String {
"künstlich:\n${elementData.get("künstlich")}\n\n" + "künstlich:\n${elementData.get("künstlich")}\n\n" +
"radioaktiv:\n${elementData.get("radioaktiv")}\n\n" + "radioaktiv:\n${elementData.get("radioaktiv")}\n\n" +
"Halbwertszeit:\n${elementData.get("Halbwertszeit")}\n\n" + "Halbwertszeit:\n${elementData.get("Halbwertszeit")}\n\n" +
"Strahlungsart:\n${elementData.get("Strahlungsart")}\n\n" + "Strahlungsart:\n${elementData.get("Strahlungsart")}"
"langlebigstes Isotop:\n${elementData.get("langlebigstes Isotop")}"
} }

View File

@@ -24,6 +24,7 @@ import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Divider import androidx.compose.material3.Divider
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
@@ -35,8 +36,10 @@ import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
@@ -64,6 +67,10 @@ fun SettingsContent(activity: ComponentActivity) {
val sharedPreferences = activity.getSharedPreferences("Settings", Context.MODE_PRIVATE) val sharedPreferences = activity.getSharedPreferences("Settings", Context.MODE_PRIVATE)
val editor = sharedPreferences.edit() val editor = sharedPreferences.edit()
var showDeleteConfirmation by remember {
mutableStateOf(false)
}
Column(modifier = Modifier.fillMaxSize()) { Column(modifier = Modifier.fillMaxSize()) {
// Top AppBar // Top AppBar
TopAppBar( TopAppBar(
@@ -94,14 +101,14 @@ fun SettingsContent(activity: ComponentActivity) {
.weight(1f) .weight(1f)
.verticalScroll(rememberScrollState()) .verticalScroll(rememberScrollState())
.fillMaxWidth() .fillMaxWidth()
) { ) {
Setting(text = "Stundenplan", sharedPreferences, editor) Setting(text = "Stundenplan", sharedPreferences, editor)
Setting(text = "Noten", sharedPreferences, editor) Setting(text = "Noten", sharedPreferences, editor)
Setting(text = "Periodensystem", sharedPreferences, editor) Setting(text = "Periodensystem", sharedPreferences, editor)
Setting(text = "Mebis", sharedPreferences, editor)
Setting(text = "DSBmobile", sharedPreferences, editor) Setting(text = "DSBmobile", sharedPreferences, editor)
Setting(text = "Mebis", sharedPreferences, editor)
Spacer(modifier = Modifier.height(16.dp)) Spacer(modifier = Modifier.height(16.dp))
Divider() Divider()
Box( Box(
modifier = Modifier modifier = Modifier
@@ -120,6 +127,19 @@ fun SettingsContent(activity: ComponentActivity) {
modifier = Modifier.padding(16.dp) modifier = Modifier.padding(16.dp)
) )
} }
Divider()
Box(
modifier = Modifier
.fillMaxWidth()
.clickable { showDeleteConfirmation = true }
) {
Text(
text = "Alle Daten löschen",
color = MaterialTheme.colorScheme.onBackground,
style = MaterialTheme.typography.labelMedium,
modifier = Modifier.padding(16.dp)
)
}
} }
// About Button // About Button
@@ -150,6 +170,43 @@ fun SettingsContent(activity: ComponentActivity) {
Spacer(modifier = Modifier.height(8.dp)) Spacer(modifier = Modifier.height(8.dp))
} }
} }
if(showDeleteConfirmation){
AlertDialog(
onDismissRequest = { showDeleteConfirmation = false },
text = {
Text(
text = "Möchten Sie wirklich alle Daten aus dieser App löschen?\n" +
"Dieser Vorgang kann nicht rückgängig gemacht werden.",
color = MaterialTheme.colorScheme.onPrimaryContainer,
style = MaterialTheme.typography.labelMedium
)
},
confirmButton = {
Row(modifier = Modifier.fillMaxWidth()){
Text(
text = "Löschen",
color = MaterialTheme.colorScheme.secondary,
style = MaterialTheme.typography.labelMedium,
modifier = Modifier.clickable {
deleteAllData(activity)
showDeleteConfirmation = false
}
)
Spacer(modifier = Modifier.weight(1f))
Text(
text = "Abbrechen",
color = MaterialTheme.colorScheme.secondary,
style = MaterialTheme.typography.labelMedium,
modifier = Modifier.clickable { showDeleteConfirmation = false }
)
}
},
containerColor = MaterialTheme.colorScheme.primaryContainer
)
}
} }
@Composable @Composable

View File

@@ -2,17 +2,28 @@ package com.schoolapp.cleverclass
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.icu.text.DecimalFormat
import android.os.Bundle import android.os.Bundle
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.outlined.Edit
import androidx.compose.material.icons.outlined.Info
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
@@ -22,23 +33,24 @@ import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.draw.scale
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.core.content.ContextCompat.startActivity
import com.schoolapp.cleverclass.LessonStoreManager.getLessons import com.schoolapp.cleverclass.LessonStoreManager.getLessons
import com.schoolapp.cleverclass.ui.theme.CleverClassTheme import com.schoolapp.cleverclass.ui.theme.CleverClassTheme
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
private val listOfDays = listOf("mon", "tue", "wed", "thu", "fri", "sat", "sun") private val listOfDays = listOf("Mo", "Di", "Mi", "Do", "Fr")
private val lessonGrabberIndex = 0 private val listOfBreakIndexes = mutableListOf<Int>()
class StundenplanActivity : ComponentActivity() { class StundenplanActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@@ -47,12 +59,26 @@ class StundenplanActivity : ComponentActivity() {
val sharedPreferences = this.getSharedPreferences("TimeTable", Context.MODE_PRIVATE) val sharedPreferences = this.getSharedPreferences("TimeTable", Context.MODE_PRIVATE)
val setupDone = sharedPreferences.getBoolean("setupDone", false) val setupDone = sharedPreferences.getBoolean("setupDone", false)
val lessons = MutableList(5) { listOf<LessonData>() }
//Loading saved lessons when NOT auto-loading the setup
if (setupDone) {
listOfDays.forEachIndexed { index, dayLessons ->
CoroutineScope(Dispatchers.IO).launch {
getLessons(this@StundenplanActivity, dayLessons).collect { savedLessons ->
lessons[index] = savedLessons.toMutableList()
}
}
}
}
setContent { setContent {
CleverClassTheme { CleverClassTheme {
Surface( Surface(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background color = MaterialTheme.colorScheme.background
) { ) {
//Auto-load setup, if incomplete
if (!setupDone) { if (!setupDone) {
val intent = Intent(this@StundenplanActivity, TimeTableSetupActivity::class.java).apply { val intent = Intent(this@StundenplanActivity, TimeTableSetupActivity::class.java).apply {
putExtra(TimeTableSetupActivity.TYPE_KEY, "TimeSetup") putExtra(TimeTableSetupActivity.TYPE_KEY, "TimeSetup")
@@ -60,7 +86,7 @@ class StundenplanActivity : ComponentActivity() {
startActivity(intent) startActivity(intent)
} }
StundenplanContent(activity = this) StundenplanContent(activity = this, lessons)
} }
} }
} }
@@ -70,8 +96,24 @@ class StundenplanActivity : ComponentActivity() {
// Content of Stundenplan // Content of Stundenplan
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun StundenplanContent(activity: ComponentActivity){ fun StundenplanContent(activity: ComponentActivity, loadedLessons: List<List<LessonData>>){
Column() { //Declaration of most sharedPreferences-stored data (same SharedPreferences as in TimeTableSetupActivity.kt)
val sharedPreferences = activity.getSharedPreferences("TimeTable", Context.MODE_PRIVATE)
val lessonLength = sharedPreferences.getInt("lessonLength", 0)
val firstLesson = sharedPreferences.getFloat("firstLesson", 0.0f).toString()
val breakAmnt = sharedPreferences.getInt("breakAmnt", 0)
val listOfBreakStartTimes = mutableListOf<String>()
val listOfBreakLengths = mutableListOf<Int>()
for (i in 0 until breakAmnt) {
listOfBreakStartTimes.add(sharedPreferences.getFloat("break${i}StartTime", 0.0f).toString())
listOfBreakLengths.add(sharedPreferences.getInt("break${i}Length", 0))
}
var showInfo by remember {
mutableStateOf(false)
}
Column {
TopAppBar( TopAppBar(
colors = TopAppBarDefaults.centerAlignedTopAppBarColors(MaterialTheme.colorScheme.primaryContainer), colors = TopAppBarDefaults.centerAlignedTopAppBarColors(MaterialTheme.colorScheme.primaryContainer),
title = { title = {
@@ -89,50 +131,325 @@ fun StundenplanContent(activity: ComponentActivity){
) )
} }
}, },
actions ={
//Info button
IconButton(
onClick = { showInfo = !showInfo }) {
Icon(
imageVector = Icons.Outlined.Info,
contentDescription = null,
modifier = Modifier.size(28.dp),
tint = MaterialTheme.colorScheme.onPrimaryContainer
)
}
//Edit button
IconButton(
onClick = {
val intent = Intent(activity, TimeTableSetupActivity::class.java).apply {
putExtra(TimeTableSetupActivity.TYPE_KEY, "TimeSetup")
}
startActivity(activity, intent, null)
}) {
Icon(
imageVector = Icons.Outlined.Edit,
contentDescription = null,
modifier = Modifier.size(28.dp),
tint = MaterialTheme.colorScheme.onPrimaryContainer
)
}
},
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) )
Column() { //Start of the timetable --> One Row() with multiple Column() to create the table
listOfDays.forEach() {currentDay -> Row(
Day(currentDay) modifier = Modifier
} .verticalScroll(rememberScrollState())
} .horizontalScroll(rememberScrollState())
} ) {
} //Left-handed lesson indexing with beginning time of each lesson or break
InfoColumn(
data class LessonData( loadedLessons = loadedLessons,
val subject: String, breakAmnt = breakAmnt,
val teacher: String, breaksStartTime = listOfBreakStartTimes,
val room: String firstLesson = firstLesson,
lessonLength = lessonLength,
breaksLength = listOfBreakLengths
) )
@Composable //Creation of 5 Columns
fun Day(day: String) { loadedLessons.forEachIndexed { index, dailyLessons ->
var lessons = emptyList<LessonData>() //Width variable for variable Box sizes
val context = LocalContext.current Column(modifier = Modifier.width(250.dp)) {
Column { //Additional extraIndex to count lessons without breaks getting into the way
LaunchedEffect(lessons, context) { var extraIndex = 0
getLessons(context, day).collect { savedLessons ->
lessons = savedLessons.toMutableList()
}
}
lessons.forEach() {
Lesson(subject = it.subject, teacher = it.teacher, room = it.room)
}
}
}
@Composable //Box with the day on top of each Column()
fun Lesson(subject: String, teacher: String, room: String) { Box(
Box(modifier = Modifier modifier = Modifier
.size(200.dp, 100.dp) .size(250.dp, 50.dp)
.padding(3.dp) .padding(3.dp)
.align(Alignment.CenterHorizontally)
.border(
width = 1.dp,
color = MaterialTheme.colorScheme.primaryContainer
)
) { ) {
Text(modifier = Modifier.align(Alignment.Center), Text(
text = "$subject\n@$room\n/w$teacher") text = listOfDays[index],
modifier = Modifier.align(Alignment.Center),
style = MaterialTheme.typography.labelMedium
)
}
//Core timetable
//One Box for each lesson or break
for (i in 0 until (dailyLessons.size + breakAmnt)) {
//If the current index is saved as a break in listOfBreakIndexes --> Spacer Box
if (listOfBreakIndexes.indexOf(i) != -1) {
Box(
modifier = Modifier
.size(250.dp, 100.dp)
)
}
//Else creation of a Box containing all the information from the LessonData object
else if (dailyLessons != emptyList<LessonData>() && dailyLessons.size != extraIndex) {
LessonBox(subject = dailyLessons[extraIndex].subject, teacher = dailyLessons[extraIndex].teacher, room = dailyLessons[extraIndex].room)
//ExtraIndex counted up only here as it does only index the lessons and NOT the breaks
extraIndex++
}
}
} }
} }
fun currentDay(): String { //Right-handed lesson indexing with beginning time of each lesson or break
return "hi" InfoColumn(
loadedLessons = loadedLessons,
breakAmnt = breakAmnt,
breaksStartTime = listOfBreakStartTimes,
firstLesson = firstLesson,
lessonLength = lessonLength,
breaksLength = listOfBreakLengths
)
}
}
if (showInfo) {
AlertDialog(
onDismissRequest = { showInfo = false },
text = {
Text(
text = "Die Breite der Spalten ist auf eine bestimmte Größe festgelegt.\n\n" +
"Zu lange Eingaben können horizontal gescrollt 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)
)
}
}
//Data class to save objects via DataShare containing information about lessons
data class LessonData(
var subject: String,
var teacher: String,
var room: String
)
//Implementation of Box with a darker border than the other Boxes
// contains the information from the corresponding LessonData object
@Composable
fun LessonBox(subject: String, teacher: String, room: String) {
Box(
modifier = Modifier
.size(250.dp, 100.dp)
.padding(3.dp)
.border(
width = 1.dp,
color = MaterialTheme.colorScheme.onPrimaryContainer
),
contentAlignment = Alignment.Center
) {
Column {
if (subject != "") {
Text(
modifier = Modifier
.horizontalScroll(rememberScrollState())
.align(Alignment.CenterHorizontally)
.padding(horizontal = 5.dp),
text = subject,
style = MaterialTheme.typography.labelMedium
)
}
if (room != "") {
Text(
modifier = Modifier
.horizontalScroll(rememberScrollState())
.align(Alignment.CenterHorizontally)
.padding(horizontal = 5.dp),
text = room,
style = MaterialTheme.typography.labelMedium
)
}
if (teacher != "") {
Text(
modifier = Modifier
.horizontalScroll(rememberScrollState())
.align(Alignment.CenterHorizontally)
.padding(horizontal = 5.dp),
text = teacher,
style = MaterialTheme.typography.labelMedium
)
}
}
}
}
//Left-/Right-Handed info Column
@Composable
fun InfoColumn(loadedLessons: List<List<LessonData>>, breakAmnt: Int, breaksStartTime: List<String>, firstLesson: String, lessonLength: Int, breaksLength: List<Int>) {
Column {
//Determining how much lessons the longest day of the week has
var maxLessons = 0
for (dailyLessons in loadedLessons) {
if (dailyLessons.size > maxLessons) {
maxLessons = dailyLessons.size
}
}
//Variable for time management with custom break times
var lessonTimeIncrement = 0
//Variable for decreasing the number in the InfoColumn after a "Pause" String
var lessonNegIncrement = 0
//Additional extraIndex to count lessons without breaks getting into the way
var extraIndex = 0
//Clear the list of brake indexes needed earlier in the timetable core
listOfBreakIndexes.drop(listOfBreakIndexes.size)
//Spacer Box
Box(
modifier = Modifier
.size(100.dp, 50.dp)
.padding(3.dp)
)
//Loop for creating Boxes on the side
for (i in 0 until (maxLessons + breakAmnt)) {
//Boolean to avoid double creation of the same Box
var loopDone = false
//Loop through all saved break start time
for (breakTime in breaksStartTime) {
if (!loopDone) {
//Variable = minutes of the currently looked at break
var formattedBreakSubstring = breakTime.substringAfter('.')
//When the Float wos like XX.n, it the n-Part will be n0 (fix for values 10, 20, etc.)
if (formattedBreakSubstring == "1" || formattedBreakSubstring == "2" || formattedBreakSubstring == "3" || formattedBreakSubstring == "4" || formattedBreakSubstring == "5") {
formattedBreakSubstring += "0"
}
//Format start time of the break the same way as the lesson start time is formatted
val formattedBreakTime = "@" + DecimalFormat("00").format(breakTime.substringBefore('.').toInt()) + ":" + DecimalFormat("00").format(formattedBreakSubstring.toInt())
//Check whether the start time of the currently looked at brake and lesson are the sa,e
if (substringHelper(extraIndex, firstLesson, lessonLength, lessonTimeIncrement) == formattedBreakTime) {
Box(
modifier = Modifier
.size(100.dp)
.padding(3.dp)
.border(
width = 1.dp,
color = MaterialTheme.colorScheme.primaryContainer
)
) {
//"Pause" String instead of indexing
Text(
modifier = Modifier
.align(Alignment.Center)
.scale(1.1f),
text = "Pause",
textDecoration = TextDecoration.Underline,
style = MaterialTheme.typography.labelLarge
)
//String with the starting time of the current break
Text(
modifier = Modifier
.align(Alignment.BottomCenter)
.padding(3.dp)
.scale(0.8f),
text = formattedBreakTime,
style = MaterialTheme.typography.labelMedium
)
//Add length of current break to the increment
lessonTimeIncrement += breaksLength[breaksStartTime.indexOf(breakTime)]
//Add info that one moe brake exists to the indexing increment
lessonNegIncrement++
//Add the extraIndex (lessonIndex) into the listOfBreakIndexes list to include breaks in timetable core
listOfBreakIndexes.add(i)
//Prevent double creation of Boxes
loopDone = true
}
}
//When looped through all breaks, but none fitted
else if (breakTime == breaksStartTime.last()) {
Box(
modifier = Modifier
.size(100.dp)
.padding(3.dp)
.border(
width = 1.dp,
color = MaterialTheme.colorScheme.primaryContainer
)
) {
//String with the indexing of the lessons
Text(
modifier = Modifier
.align(Alignment.Center)
.scale(1.1f),
text = "${i + 1 - lessonNegIncrement}:",
textDecoration = TextDecoration.Underline,
style = MaterialTheme.typography.labelLarge
)
//String with the starting time of the lesson
Text(
modifier = Modifier
.align(Alignment.BottomCenter)
.padding(3.dp)
.scale(0.8f),
text = substringHelper(extraIndex, firstLesson, lessonLength, lessonTimeIncrement),
style = MaterialTheme.typography.labelMedium
)
//Prevent double creation of Boxes
loopDone = true
//Counting up the lesson only index
extraIndex++
}
}
}
}
}
}
}
//Function that moves the formatting and calculation of the lesson start times away from in-line
fun substringHelper (int: Int, eductString: String, lessonLength: Int, increment: Int): String {
//Variable = minutes of the entered Time
var subString = eductString.substringAfter('.')
//Fix of 10, 20, etc.
if (subString == "1" || subString == "2" || subString == "3" || subString == "4" || subString == "5") {
subString += "0"
}
//Calculation based on start time of the first hour, lesson length, indexing of the current lesson (extraIndex!) and the increment calculated from the break times
//Formatting to "@hh:mm"
val productString = "@" + DecimalFormat("00").format((((subString.toInt() + int * lessonLength) + increment) / 60) + eductString.substringBefore('.').toInt()) + ":" + DecimalFormat("00").format((((subString.toInt()) + int * lessonLength) + increment) % 60)
//Return statement to use the formatted and calculated product in the timetable
return productString
} }

View File

@@ -7,11 +7,8 @@ import android.icu.text.DecimalFormat
import android.icu.text.DecimalFormatSymbols import android.icu.text.DecimalFormatSymbols
import android.icu.util.Calendar import android.icu.util.Calendar
import android.os.Bundle import android.os.Bundle
import android.widget.TimePicker
import android.widget.Toast
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
@@ -25,6 +22,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.KeyboardOptions
@@ -32,66 +30,90 @@ import androidx.compose.foundation.text.selection.TextSelectionColors
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.outlined.Delete import androidx.compose.material.icons.outlined.Edit
import androidx.compose.material.icons.outlined.Info 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.AlertDialog
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.Checkbox
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FilledTonalButton
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.material3.Tab
import androidx.compose.material3.TabRow
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextFieldDefaults import androidx.compose.material3.TextFieldDefaults
import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.currentCompositionLocalContext import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.scale
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.unit.dp 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.CleverClassTheme
import com.schoolapp.cleverclass.ui.theme.InputPrimaryColor import com.schoolapp.cleverclass.ui.theme.InputPrimaryColor
import com.schoolapp.cleverclass.ui.theme.InputSecondaryColor import com.schoolapp.cleverclass.ui.theme.InputSecondaryColor
import org.intellij.lang.annotations.JdkConstants.CalendarMonth import kotlinx.coroutines.CoroutineScope
import android.text.format.DateFormat import kotlinx.coroutines.Dispatchers
import androidx.compose.foundation.layout.width import kotlinx.coroutines.launch
import androidx.compose.material.icons.outlined.Edit
import androidx.compose.material3.FilledTonalButton
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.TextButton
import androidx.compose.ui.focus.focusModifier
import androidx.compose.ui.text.TextStyle
import java.util.Locale import java.util.Locale
import android.app.Activity
import androidx.compose.material3.Checkbox
import androidx.compose.material3.Tab
import androidx.compose.material3.TabRow
import androidx.core.content.ContextCompat.startActivity
import com.schoolapp.cleverclass.PSEActivity
import kotlinx.coroutines.selects.select
private lateinit var activityType : String 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() { class TimeTableSetupActivity : ComponentActivity() {
companion object{ companion object{
const val TYPE_KEY = "type_key" const val TYPE_KEY = "type_key"
const val STATE_KEY = "state_key"
} }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
//Defines setup type --> Time vs. Day
activityType = intent.getStringExtra(TYPE_KEY).toString() 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<LessonData>()
if (activityType == "DaySetup") {
CoroutineScope(Dispatchers.IO).launch {
getLessons(this@TimeTableSetupActivity, listOfDays[activityState.toInt()]).collect { savedLessons ->
lessons = savedLessons.toList()
}
}
}
setContent { setContent {
CleverClassTheme { CleverClassTheme {
@@ -99,7 +121,7 @@ class TimeTableSetupActivity : ComponentActivity() {
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background color = MaterialTheme.colorScheme.background
) { ) {
TimeTableSetupContent(activity = this) TimeTableSetupContent(activity = this, lessons)
} }
} }
} }
@@ -110,7 +132,8 @@ class TimeTableSetupActivity : ComponentActivity() {
// Content of TimeTableSetup // Content of TimeTableSetup
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun TimeTableSetupContent(activity: ComponentActivity){ fun TimeTableSetupContent(activity: ComponentActivity, loadedLessons: List<LessonData>){
//One-time definition of the inputField colorscheme
val inputFieldColors = TextFieldDefaults.outlinedTextFieldColors( val inputFieldColors = TextFieldDefaults.outlinedTextFieldColors(
textColor = MaterialTheme.colorScheme.onPrimaryContainer, textColor = MaterialTheme.colorScheme.onPrimaryContainer,
containerColor = MaterialTheme.colorScheme.secondaryContainer, containerColor = MaterialTheme.colorScheme.secondaryContainer,
@@ -123,40 +146,63 @@ fun TimeTableSetupContent(activity: ComponentActivity){
unfocusedBorderColor = Color.Transparent unfocusedBorderColor = Color.Transparent
) )
//Declaration of most sharedPreferences-stored data
val sharedPreferences = activity.getSharedPreferences("TimeTable", Context.MODE_PRIVATE) val sharedPreferences = activity.getSharedPreferences("TimeTable", Context.MODE_PRIVATE)
val editor = sharedPreferences.edit() val editor = sharedPreferences.edit()
//Length of one lesson
var lessonLength by remember { var lessonLength by remember {
mutableStateOf(sharedPreferences.getInt("lessonLength", 0).toString()) mutableStateOf(sharedPreferences.getInt("lessonLength", 0).toString())
} }
//Number of breaks on one day
var breakAmnt by remember { var breakAmnt by remember {
mutableStateOf(sharedPreferences.getInt("breakAmnt", 0).toString()) mutableStateOf(sharedPreferences.getInt("breakAmnt", 0).toString())
} }
//Beginning time of the first lesson (Format: hh.mm)
var firstLesson by remember { var firstLesson by remember {
mutableStateOf(sharedPreferences.getFloat("firstLesson", 0.0f)) mutableStateOf(sharedPreferences.getFloat("firstLesson", 0.0f))
} }
//Value to determine whether the first setup will be loaded again
var setupDone by remember { var setupDone by remember {
mutableStateOf(sharedPreferences.getBoolean("setupDone", false)) mutableStateOf(sharedPreferences.getBoolean("setupDone", false))
} }
//AlertDialog
var showInfo by remember { var showInfo by remember {
mutableStateOf(false) mutableStateOf(false)
} }
var showDeleteConfirmation by remember {
mutableStateOf(false) //Variable for loaded lessons from onCreate()
var lessons by remember {
mutableStateOf(loadedLessons)
} }
val listOfDays = listOf("Mo", "Di", "Mi", "Do", "Fr")
Column { Column {
TopAppBar( TopAppBar(
colors = TopAppBarDefaults.centerAlignedTopAppBarColors(MaterialTheme.colorScheme.primaryContainer), colors = TopAppBarDefaults.centerAlignedTopAppBarColors(MaterialTheme.colorScheme.primaryContainer),
title = { Text(text = "") }, title = { Text(text = "") },
navigationIcon = { navigationIcon = {
IconButton(onClick = { activity.finish() }) { 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( Icon(
imageVector = Icons.Filled.ArrowBack, imageVector = Icons.Filled.ArrowBack,
contentDescription = null, contentDescription = null,
@@ -174,6 +220,7 @@ fun TimeTableSetupContent(activity: ComponentActivity){
.fillMaxHeight() .fillMaxHeight()
.verticalScroll(rememberScrollState()) .verticalScroll(rememberScrollState())
) { ) {
//Background
Surface( Surface(
color = MaterialTheme.colorScheme.primaryContainer, color = MaterialTheme.colorScheme.primaryContainer,
shape = RoundedCornerShape(20), shape = RoundedCornerShape(20),
@@ -182,6 +229,7 @@ fun TimeTableSetupContent(activity: ComponentActivity){
Column(horizontalAlignment = Alignment.CenterHorizontally, Column(horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.padding(16.dp) modifier = Modifier.padding(16.dp)
) { ) {
//Title
Row(verticalAlignment = Alignment.CenterVertically) { Row(verticalAlignment = Alignment.CenterVertically) {
Text( Text(
text = "Stundenplan Setup", text = "Stundenplan Setup",
@@ -192,16 +240,7 @@ fun TimeTableSetupContent(activity: ComponentActivity){
Spacer(modifier = Modifier.weight(1f)) Spacer(modifier = Modifier.weight(1f))
if (activityType == "Settings") //Info Button --> AlertDialog
IconButton(onClick = { showDeleteConfirmation = !showDeleteConfirmation }) {
Icon(
imageVector = Icons.Outlined.Delete,
contentDescription = null,
modifier = Modifier.size(28.dp),
tint = MaterialTheme.colorScheme.onPrimaryContainer
)
}
else if (activityType == "TimeSetup")
IconButton(onClick = { showInfo = !showInfo }) { IconButton(onClick = { showInfo = !showInfo }) {
Icon( Icon(
imageVector = Icons.Outlined.Info, imageVector = Icons.Outlined.Info,
@@ -213,7 +252,9 @@ fun TimeTableSetupContent(activity: ComponentActivity){
Spacer(modifier = Modifier.height(16.dp)) Spacer(modifier = Modifier.height(16.dp))
//Setup for basic time data (lesson/break length etc.)
if (activityType == "TimeSetup") { if (activityType == "TimeSetup") {
//Time input begin of first lesson
Text( Text(
text = "Beginn der 1. Stunde", text = "Beginn der 1. Stunde",
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
@@ -237,21 +278,20 @@ fun TimeTableSetupContent(activity: ComponentActivity){
symbols.decimalSeparator = ':' symbols.decimalSeparator = ':'
val decimalFormat = DecimalFormat("00.00", symbols) val decimalFormat = DecimalFormat("00.00", symbols)
Text(decimalFormat.format(firstLesson) + " Uhr ") Text(
text = decimalFormat.format(firstLesson) + " Uhr ",
color = MaterialTheme.colorScheme.onPrimaryContainer)
Icon(imageVector = Icons.Outlined.Edit, contentDescription = null) Icon(imageVector = Icons.Outlined.Edit, contentDescription = null)
} }
Spacer(modifier = Modifier.height(16.dp)) Spacer(modifier = Modifier.height(16.dp))
//Duration of one lesson
OutlinedTextField( OutlinedTextField(
value = lessonLength, value = lessonLength,
onValueChange = { lessonLength = it }, onValueChange = { lessonLength = it },
label = { label = { Text(text = "Länge einer Schulstunde in min") },
Text(
text = "Länge einer Schulstunde in min"
)
},
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
singleLine = true, singleLine = true,
shape = RoundedCornerShape(20), shape = RoundedCornerShape(20),
@@ -262,14 +302,11 @@ fun TimeTableSetupContent(activity: ComponentActivity){
Spacer(modifier = Modifier.height(16.dp)) Spacer(modifier = Modifier.height(16.dp))
//Input amount of breaks
OutlinedTextField( OutlinedTextField(
value = breakAmnt, value = breakAmnt,
onValueChange = { breakAmnt = it}, onValueChange = { breakAmnt = it},
label = { label = { Text(text = "Anzahl der Pausen") },
Text(
text = "Anzahl der Pausen"
)
},
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
singleLine = true, singleLine = true,
shape = RoundedCornerShape(20), shape = RoundedCornerShape(20),
@@ -278,17 +315,20 @@ fun TimeTableSetupContent(activity: ComponentActivity){
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.NumberPassword) keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.NumberPassword)
) )
if (breakAmnt != "0" && breakAmnt != "") { //Input of length and start time for each break
if (breakAmnt != "") {
//declaration of sharedPreferences for break data
for (i in 0 until breakAmnt.toInt()) { for (i in 0 until breakAmnt.toInt()) {
var currentBrakeStartTime by remember{ var currentBreakStartTime by remember{
mutableStateOf(sharedPreferences.getFloat("Brake${i}StartTime", 0.0f)) mutableStateOf(sharedPreferences.getFloat("break${i}StartTime", 0.0f))
} }
var currentBrakeLength by remember{ var currentBreakLength by remember{
mutableStateOf(sharedPreferences.getInt("Brake${i}Length", 0).toString()) mutableStateOf(sharedPreferences.getInt("break${i}Length", 0).toString())
} }
Spacer(modifier = Modifier.height(16.dp)) Spacer(modifier = Modifier.height(16.dp))
//Input start time (break)
Text( Text(
text = "Startzeit der ${i + 1}. Pause:", text = "Startzeit der ${i + 1}. Pause:",
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
@@ -300,8 +340,8 @@ fun TimeTableSetupContent(activity: ComponentActivity){
val mHour = c.get(Calendar.HOUR_OF_DAY) val mHour = c.get(Calendar.HOUR_OF_DAY)
val mMinute = c.get(Calendar.MINUTE) val mMinute = c.get(Calendar.MINUTE)
val timePickerDialog = TimePickerDialog(activity, TimePickerDialog.OnTimeSetListener(function = { view, h, m -> val timePickerDialog = TimePickerDialog(activity, TimePickerDialog.OnTimeSetListener(function = { view, h, m ->
currentBrakeStartTime = h.toFloat() + (m.toFloat() / 100) currentBreakStartTime = h.toFloat() + (m.toFloat() / 100)
editor.putFloat("Brake${i}StartTime", currentBrakeStartTime) editor.putFloat("break${i}StartTime", currentBreakStartTime)
}), mHour, mMinute, true) }), mHour, mMinute, true)
timePickerDialog.show() timePickerDialog.show()
}, },
@@ -312,23 +352,26 @@ fun TimeTableSetupContent(activity: ComponentActivity){
symbols.decimalSeparator = ':' symbols.decimalSeparator = ':'
val decimalFormat = DecimalFormat("00.00", symbols) val decimalFormat = DecimalFormat("00.00", symbols)
Text(decimalFormat.format(currentBrakeStartTime) + " Uhr ") Text(
text = decimalFormat.format(currentBreakStartTime) + " Uhr ",
color = MaterialTheme.colorScheme.onPrimaryContainer,
style = MaterialTheme.typography.labelMedium
)
Icon(imageVector = Icons.Outlined.Edit, contentDescription = null) Icon(imageVector = Icons.Outlined.Edit, contentDescription = null)
} }
Spacer(modifier = Modifier.height(5.dp)) Spacer(modifier = Modifier.height(5.dp))
//Input duration (break)
OutlinedTextField( OutlinedTextField(
value = currentBrakeLength, value = currentBreakLength,
onValueChange = { onValueChange = {
currentBrakeLength = it currentBreakLength = it
editor.putInt("Brake${i}Length", currentBrakeLength.toInt())}, if (currentBreakLength != "") {
label = { editor.putInt("break${i}Length", currentBreakLength.toInt())
Text( }},
text = "Länge der ${i + 1}. Pause in min" label = { Text(text = "Länge der ${i + 1}. Pause in min") },
)
},
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
singleLine = true, singleLine = true,
shape = RoundedCornerShape(20), shape = RoundedCornerShape(20),
@@ -342,107 +385,27 @@ fun TimeTableSetupContent(activity: ComponentActivity){
Spacer(modifier = Modifier.height(10.dp)) Spacer(modifier = Modifier.height(10.dp))
Row { Row {
//Go back to StundenplanActivity
FilledTonalButton( FilledTonalButton(
onClick = {
activity.finish()
},
modifier = Modifier
.padding(5.dp)
.weight(1f)
) {
Text(
text = "Abbrechen",
color = MaterialTheme.colorScheme.secondary,
style = MaterialTheme.typography.labelMedium
)
}
Spacer(modifier = Modifier
.width(5.dp)
)
Button(
onClick = { onClick = {
editor.putInt("lessonLength", lessonLength.toInt()) editor.putInt("lessonLength", lessonLength.toInt())
if (breakAmnt == "0") {
editor.putInt("breakAmnt", -1)
}
else {
editor.putInt("breakAmnt", breakAmnt.toInt()) editor.putInt("breakAmnt", breakAmnt.toInt())
}
editor.apply() editor.apply()
activity.finish() activity.finish()
val intent = Intent(activity, TimeTableSetupActivity::class.java).apply { if (setupDone) {
putExtra(TimeTableSetupActivity.TYPE_KEY, "DaySetup") //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) startActivity(activity, intent, null)
},
modifier = Modifier
.padding(5.dp)
.weight(1f)
) {
Text(
text = "Weiter",
style = MaterialTheme.typography.labelMedium
)
}
}
}
else if (activityType == "DaySetup") {
var state by remember {
mutableStateOf(0)
}
TabRow(selectedTabIndex = state,
containerColor = MaterialTheme.colorScheme.primaryContainer) {
for (day in listOfDays) {
Tab(selected = false,
onClick = {
state = listOfDays.indexOf(day) },
modifier = Modifier.padding(5.dp),
) {
Text(
text = day,
color = MaterialTheme.colorScheme.onPrimaryContainer,
style = MaterialTheme.typography.labelMedium
)
}
sharedPreferences.getInt("AnzahlStunden${day}", 0)
}
}
when (state) {
0 -> {
Text(text = "Montag")
}
1 -> {
Text(text = "Dienstag")
}
2 -> {
Text(text = "Mittwoch")
}
3 -> {
Text(text = "Donnerstag")
}
4 -> {
Text(text = "Freitag")
}
}
Row () {
Text(
text = "Setup beendet? ",
modifier = Modifier.align(Alignment.CenterVertically),
style = MaterialTheme.typography.labelMedium
)
Checkbox(checked = setupDone, onCheckedChange = {setupDone = !setupDone})
}
Row {
FilledTonalButton(
onClick = {
activity.finish()
val intent = Intent(activity, TimeTableSetupActivity::class.java).apply {
putExtra(TimeTableSetupActivity.TYPE_KEY, "TimeSetup")
} }
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) startActivity(activity, intent, null)
}
}, },
modifier = Modifier modifier = Modifier
.padding(5.dp) .padding(5.dp)
@@ -459,18 +422,252 @@ fun TimeTableSetupContent(activity: ComponentActivity){
.width(5.dp) .width(5.dp)
) )
//Move on to DaySetup
Button( Button(
onClick = { onClick = {
editor.putBoolean("setupDone", setupDone) editor.putInt("lessonLength", lessonLength.toInt())
editor.putInt("breakAmnt", breakAmnt.toInt())
editor.apply() editor.apply()
activity.finish() 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 modifier = Modifier
.padding(5.dp) .padding(5.dp)
.weight(1f) .weight(1f)
) { ) {
Text( Text(
text = "Speichern", 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 style = MaterialTheme.typography.labelMedium
) )
} }
@@ -481,6 +678,7 @@ fun TimeTableSetupContent(activity: ComponentActivity){
} }
} }
//AlertDialog to inform the user of the possibility of changing all inputs later
if (showInfo) { if (showInfo) {
AlertDialog( AlertDialog(
onDismissRequest = { showInfo = false }, onDismissRequest = { showInfo = false },
@@ -503,49 +701,11 @@ fun TimeTableSetupContent(activity: ComponentActivity){
.padding(5.dp) .padding(5.dp)
) )
} }
if (showDeleteConfirmation) {
AlertDialog(
onDismissRequest = { showDeleteConfirmation = false },
text = {
Text(
text = "Möchten Sie wirklich die Anmeldedaten löschen?",
color = MaterialTheme.colorScheme.onPrimaryContainer,
style = MaterialTheme.typography.labelMedium
)
},
confirmButton = {
Row(modifier = Modifier.fillMaxWidth()){
Text(
text = "Bestätigen",
color = MaterialTheme.colorScheme.secondary,
style = MaterialTheme.typography.labelMedium,
modifier = Modifier.clickable {
editor.putString("maxLessons", "")
editor.apply()
showDeleteConfirmation = false
activity.finish()
}
)
Spacer(modifier = Modifier.weight(1f))
Text(
text = "Abbrechen",
color = MaterialTheme.colorScheme.secondary,
style = MaterialTheme.typography.labelMedium,
modifier = Modifier.clickable { showDeleteConfirmation = false }
)
}
},
containerColor = MaterialTheme.colorScheme.primaryContainer
)
}
} }
@Composable //Function that implements the save of List<LessonData>
fun TestButton(text: String, onClick: ()-> Unit){ fun saveLessonsUsage(context: Context, lessons: List<LessonData>, int: Int) {
Button(onClick = onClick,) { CoroutineScope(Dispatchers.IO).launch {
Text(text = text) saveLessons(context, lessons, listOfDays[int])
} }
} }

View File

@@ -1,5 +1,9 @@
package com.schoolapp.cleverclass package com.schoolapp.cleverclass
import android.app.Activity
import android.content.Context
import android.content.Intent
import androidx.activity.ComponentActivity
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
@@ -17,6 +21,10 @@ import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.io.File
const val NO_INTERNET_CONNECTION_CODE = -1 const val NO_INTERNET_CONNECTION_CODE = -1
@@ -64,3 +72,46 @@ fun StaticLoadingScreen(){
} }
} }
} }
fun deleteAllData(activity: ComponentActivity){
val sharedPrefs = listOf("Settings", "DSBmobile", "gradeAverages", "TimeTable")
for (prefName in sharedPrefs) {
val sharedPreferences = activity.getSharedPreferences(prefName, Context.MODE_PRIVATE)
val editor = sharedPreferences.edit()
editor.clear()
editor.apply()
}
clearFolder(activity.filesDir)
clearFolder(activity.cacheDir)
CoroutineScope(Dispatchers.Main).launch {
restartApp(activity)
}
}
fun clearFolder(directory: File) {
if (directory.exists()) {
val files = directory.listFiles()
if (files != null) {
for (file in files) {
if (file.isDirectory) {
clearFolder(file)
}
file.delete()
}
}
}
}
fun restartApp(context: Context) {
val packageManager = context.packageManager
val intent = packageManager.getLaunchIntentForPackage(context.packageName)
intent?.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
intent?.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
context.startActivity(intent)
if (context is Activity) {
context.finish()
}
Runtime.getRuntime().exit(0)
}

View File

@@ -8,9 +8,9 @@ val Black80 = Color(0xFF333333)
val Black70 = Color(0xFF4B4B4B) val Black70 = Color(0xFF4B4B4B)
val White100 = Color(0xFFFFFFFF) val White100 = Color(0xFFFFFFFF)
val White90 = Color(0xFFEBEBEB) val White90 = Color(0xFFFAFAFA)
val White80 = Color(0xFFD3D3D3) val White80 = Color(0xFFD3D3D3)
val White70 = Color(0xFFBBBBBB) val White70 = Color(0xFFAFAFAF)
val TextOnColouredButton = Color(0xFF121212) val TextOnColouredButton = Color(0xFF121212)

View File

@@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#000000"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M19,4L5,4c-1.11,0 -2,0.9 -2,2v12c0,1.1 0.89,2 2,2h4v-2L5,18L5,8h14v10h-4v2h4c1.1,0 2,-0.9 2,-2L21,6c0,-1.1 -0.89,-2 -2,-2zM12,10l-4,4h3v6h2v-6h3l-4,-4z"/>
</vector>