Compare commits
63 Commits
c5d32a5add
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
442ef70b8d | ||
|
|
ec3a95fc2d | ||
|
|
b07cd27fce | ||
|
|
39c037a9ab | ||
|
|
f6956349e0 | ||
|
|
a41bd9cbb3 | ||
|
|
0db1e78cd4 | ||
|
|
7bdcb7e3fb | ||
|
|
d36cadeb18 | ||
|
|
1056dc39bf | ||
|
|
15cadb76c6 | ||
|
|
41b54f15ea | ||
|
|
50eb19a464 | ||
|
|
4c3de94dd3 | ||
|
|
30ad4bed64 | ||
|
|
f091451fe8 | ||
| 1c665fea26 | |||
|
|
41be804d5b | ||
|
|
173a3fc5a5 | ||
|
|
718fe65a75 | ||
|
|
e9ca2d8704 | ||
|
|
75302eed36 | ||
|
|
ac7ce0ad3d | ||
|
|
344aa1ef9f | ||
|
|
2aae27e77c | ||
| 2a6e970c71 | |||
|
|
e64a010e1d | ||
| cfdf00e0a5 | |||
|
|
d982d71f31 | ||
|
|
25bbf0f6b9 | ||
|
|
c54a79e218 | ||
|
|
7941081b90 | ||
|
|
b2e4a4f093 | ||
|
|
c16cc2d364 | ||
|
|
d3a9bf72c9 | ||
|
|
f2779cb442 | ||
|
|
1012287c4c | ||
|
|
d77c20e890 | ||
|
|
2a32bcebcc | ||
|
|
98b68d2d70 | ||
|
|
f669ba29f0 | ||
|
|
afc4f7e30a | ||
|
|
70408661a8 | ||
|
|
515e73bb94 | ||
|
|
4c4763dc9b | ||
|
|
f6a6d8c367 | ||
|
|
82dadd7e21 | ||
|
|
7116edc376 | ||
|
|
f37d597aef | ||
|
|
cd9e26fbe5 | ||
|
|
2e1f335a47 | ||
|
|
25a4992e43 | ||
|
|
39a24020d2 | ||
|
|
ee1239750f | ||
|
|
6e24a54682 | ||
|
|
7f9bacdf28 | ||
|
|
180d2b2532 | ||
|
|
c3c487ac54 | ||
|
|
90098cc83c | ||
|
|
888d737965 | ||
|
|
dbfed92a42 | ||
|
|
100dfd9c42 | ||
|
|
46f0ae21a6 |
2
.idea/compiler.xml
generated
@@ -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>
|
||||
16
.idea/deploymentTargetDropDown.xml
generated
@@ -1,16 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="deploymentTargetDropDown">
|
||||
<value>
|
||||
<entry key="All Tests">
|
||||
<State />
|
||||
</entry>
|
||||
<entry key="PSEActivity">
|
||||
<State />
|
||||
</entry>
|
||||
<entry key="app">
|
||||
<State />
|
||||
</entry>
|
||||
</value>
|
||||
</component>
|
||||
</project>
|
||||
4
.idea/gradle.xml
generated
@@ -4,8 +4,10 @@
|
||||
<component name="GradleSettings">
|
||||
<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
@@ -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">
|
||||
|
||||
30
README.md
@@ -1,2 +1,32 @@
|
||||
# CleverClass
|
||||
CleverClass ist eine Android-App, die Schülern hilft, ihre Schulnoten zu verwalten, ihren Durchschnitt zu berechnen, den Vertretungsplan von DSBmobile einzusehen, den Stundenplan zu speichern und anzuzeigen sowie ein Periodensystem zu nutzen.
|
||||
|
||||
## Funktionen und Verwendung
|
||||
- **Notenverwaltung:** Trage deine Schulnoten ein und berechne deinen Durchschnitt. Perfekt, um alle Noten aus der Schule übersichtlich zu speichern.
|
||||
- **Vertretungsplan:** Rufe den aktuellen Vertretungsplan von DSBmobile direkt in der App auf.
|
||||
- **Stundenplan:** Trage deinen Stundenplan ein, speichere und verwalte ihn in der App, um immer den Überblick zu behalten.
|
||||
- **Periodensystem:** Nutze das integrierte Periodensystem, um schnell Informationen zu chemischen Elementen nachzuschlagen.
|
||||
## Installation
|
||||
CleverClass kann einfach über den Play Store installiert werden oder durch das Herunterladen der APK-Datei:
|
||||
|
||||
Play Store:
|
||||
- Öffne den Google Play Store auf deinem Android-Gerät
|
||||
- Suche nach "CleverClass"
|
||||
- Tippe auf "Installieren"
|
||||
|
||||
APK-Datei:
|
||||
- Lade die APK-Datei von der [Website](https://google.com/) herunter
|
||||
- Öffne die heruntergeladene APK-Datei auf deinem Android-Gerät
|
||||
- Folge den Anweisungen auf dem Bildschirm, um die Installation abzuschließen
|
||||
## Anforderungen
|
||||
Betriebssystem: Android 8.0 oder neuer
|
||||
## Autoren
|
||||
- Paul Posch
|
||||
- Matthias Meyer
|
||||
- Jakub Szarko
|
||||
- Emilian Bührer
|
||||
## Lizenz
|
||||
Dieses Projekt steht unter der [GNU General Public License Version 3, 29 June 2007](LICENSE).
|
||||
|
||||
## Sonstiges
|
||||
Dieses Projekt ist als Schulprojekt inerhalb von 3 Monaten entstanden.
|
||||
@@ -23,6 +23,7 @@ android {
|
||||
buildTypes {
|
||||
release {
|
||||
isMinifyEnabled = false
|
||||
isDebuggable = false
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro"
|
||||
@@ -50,38 +51,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("androidx.datastore:datastore-preferences:1.0.0")
|
||||
implementation("com.google.code.gson:gson:2.9.0")
|
||||
implementation(platform("androidx.compose:compose-bom:2023.03.00"))
|
||||
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")
|
||||
}
|
||||
@@ -2,6 +2,9 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
@@ -12,6 +15,21 @@
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.CleverClass"
|
||||
tools:targetApi="31">
|
||||
<activity
|
||||
android:name=".DSBLoginActivity"
|
||||
android:exported="false"
|
||||
android:label="@string/title_activity_dsblogin"
|
||||
android:theme="@style/Theme.CleverClass" />
|
||||
<activity
|
||||
android:name=".DSBDayViewActivity"
|
||||
android:exported="false"
|
||||
android:label="@string/title_activity_dsbday_view"
|
||||
android:theme="@style/Theme.CleverClass" />
|
||||
<activity
|
||||
android:name=".DSBActivity"
|
||||
android:exported="false"
|
||||
android:label="@string/title_activity_dsbactivity"
|
||||
android:theme="@style/Theme.CleverClass" />
|
||||
<activity
|
||||
android:name=".FachActivity"
|
||||
android:exported="false"
|
||||
@@ -22,6 +40,11 @@
|
||||
android:exported="false"
|
||||
android:label="@string/title_activity_noten"
|
||||
android:theme="@style/Theme.CleverClass" />
|
||||
<activity
|
||||
android:name=".TimeTableSetupActivity"
|
||||
android:exported="false"
|
||||
android:label="@string/title_activity_timetable_setup"
|
||||
android:theme="@style/Theme.CleverClass" />
|
||||
<activity
|
||||
android:name=".StundenplanActivity"
|
||||
android:exported="false"
|
||||
|
||||
1892
app/src/main/assets/elements_data.json
Normal file
BIN
app/src/main/ic_launcher-playstore.png
Normal file
|
After Width: | Height: | Size: 58 KiB |
@@ -1,9 +1,15 @@
|
||||
package com.schoolapp.cleverclass
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
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.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
@@ -14,6 +20,7 @@ import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material3.Divider
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
@@ -23,7 +30,11 @@ import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
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 com.schoolapp.cleverclass.ui.theme.CleverClassTheme
|
||||
|
||||
@@ -50,14 +61,16 @@ fun AboutContent(activity: ComponentActivity){
|
||||
colors = TopAppBarDefaults.centerAlignedTopAppBarColors(MaterialTheme.colorScheme.primaryContainer),
|
||||
title = {
|
||||
Text(text = "About",
|
||||
style = MaterialTheme.typography.headlineSmall
|
||||
style = MaterialTheme.typography.headlineSmall,
|
||||
color = MaterialTheme.colorScheme.onPrimaryContainer
|
||||
)},
|
||||
navigationIcon = {
|
||||
IconButton(onClick = { activity.finish() }) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.ArrowBack,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(28.dp)
|
||||
modifier = Modifier.size(28.dp),
|
||||
tint = MaterialTheme.colorScheme.onPrimaryContainer
|
||||
)
|
||||
}
|
||||
},
|
||||
@@ -68,14 +81,46 @@ fun AboutContent(activity: ComponentActivity){
|
||||
.verticalScroll(rememberScrollState())
|
||||
.fillMaxWidth())
|
||||
{
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Text(text = "This Product is published under the\nGNU General Public License\nVersion 3, 29 June 2007",
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
modifier = Modifier.padding(start = 16.dp, end = 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("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))
|
||||
Text(text = "Developed by:\n- Paul Posch\n- Matthias Meyer\n- Jakub Szarko\n- Emilian Bührer",
|
||||
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)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AboutTextElement(text: String, space: Dp){
|
||||
Spacer(modifier = Modifier.height(space))
|
||||
Text(text = text,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onBackground,
|
||||
modifier = Modifier.padding(start = 16.dp, end = 16.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
254
app/src/main/java/com/schoolapp/cleverclass/DSBActivity.kt
Normal file
@@ -0,0 +1,254 @@
|
||||
package com.schoolapp.cleverclass
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.graphics.BitmapFactory
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
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.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.Refresh
|
||||
import androidx.compose.material3.Divider
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
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
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.asImageBitmap
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.schoolapp.cleverclass.ui.theme.CleverClassTheme
|
||||
import com.schoolapp.cleverclass.ui.theme.MiddleColor
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import org.json.JSONObject
|
||||
import java.io.File
|
||||
|
||||
|
||||
class DSBActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
val sharedPreferences = this.getSharedPreferences("DSBmobile", Context.MODE_PRIVATE)
|
||||
val username = sharedPreferences.getString("username", "").toString()
|
||||
val password = sharedPreferences.getString("password", "").toString()
|
||||
|
||||
setContent {
|
||||
CleverClassTheme {
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
color = MaterialTheme.colorScheme.background
|
||||
) {
|
||||
if(username.isEmpty() || password.isEmpty()) {
|
||||
val intent = Intent(this@DSBActivity, DSBLoginActivity::class.java).apply {
|
||||
putExtra(DSBLoginActivity.TYPE_KEY, "FirstTime")
|
||||
}
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
DSBContent(activity = this)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Content of DSBmobile
|
||||
@OptIn(ExperimentalMaterial3Api::class, DelicateCoroutinesApi::class)
|
||||
@Composable
|
||||
fun DSBContent(activity: ComponentActivity){
|
||||
val mainFolder = File(activity.filesDir, "dataDSB")
|
||||
val sharedPreferences = activity.getSharedPreferences("DSBmobile", Context.MODE_PRIVATE)
|
||||
|
||||
var loadingState by remember {
|
||||
mutableStateOf(-2)
|
||||
}
|
||||
var folders by remember {
|
||||
mutableStateOf(mainFolder.listFiles())
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
GlobalScope.launch(Dispatchers.IO) {
|
||||
loadingState = downloadDSB(activity)
|
||||
folders = mainFolder.listFiles()
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
TopAppBar(
|
||||
colors = TopAppBarDefaults.centerAlignedTopAppBarColors(MaterialTheme.colorScheme.primaryContainer),
|
||||
title = {
|
||||
Text(text = "DSBmobile",
|
||||
style = MaterialTheme.typography.headlineSmall,
|
||||
color = MaterialTheme.colorScheme.onPrimaryContainer
|
||||
)},
|
||||
navigationIcon = {
|
||||
IconButton(onClick = { activity.finish() }) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.ArrowBack,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(28.dp),
|
||||
tint = MaterialTheme.colorScheme.onPrimaryContainer
|
||||
)
|
||||
}
|
||||
},
|
||||
actions = {
|
||||
if(loadingState != -2) {
|
||||
IconButton(
|
||||
onClick = {
|
||||
loadingState = -2
|
||||
GlobalScope.launch(Dispatchers.IO) {
|
||||
loadingState = downloadDSB(activity)
|
||||
folders = mainFolder.listFiles()
|
||||
}
|
||||
}
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Refresh,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(28.dp),
|
||||
tint = MaterialTheme.colorScheme.onPrimaryContainer
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
|
||||
when (loadingState) {
|
||||
-2 -> {
|
||||
LoadingScreen()
|
||||
}
|
||||
NO_INTERNET_CONNECTION_CODE -> {
|
||||
DSBError("Keine Internetverbindung")
|
||||
}
|
||||
1 ->{
|
||||
DSBError("Login fehlgeschlagen")
|
||||
}
|
||||
0 -> {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier
|
||||
.verticalScroll(rememberScrollState())
|
||||
.fillMaxWidth()
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
|
||||
folders.forEach { folder ->
|
||||
DayPrefab(folder, activity)
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
Divider()
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
DSBError("Leider ist ein Problem aufgetreten\nFehlercode: $loadingState")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// changes state when Shared Preference is updated
|
||||
val observer = remember {
|
||||
SharedPreferences.OnSharedPreferenceChangeListener { _, key ->
|
||||
if (key == "password" || key == "username") {
|
||||
loadingState = -2
|
||||
|
||||
GlobalScope.launch(Dispatchers.IO) {
|
||||
loadingState = downloadDSB(activity)
|
||||
folders = mainFolder.listFiles()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// handles observer disposal
|
||||
DisposableEffect(Unit) {
|
||||
sharedPreferences.registerOnSharedPreferenceChangeListener(observer)
|
||||
onDispose {
|
||||
sharedPreferences.unregisterOnSharedPreferenceChangeListener(observer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun DSBError(message: String){
|
||||
Box(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentAlignment = Alignment.Center
|
||||
){
|
||||
Text(
|
||||
text = message,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onBackground
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun DayPrefab(folder: File, activity: Context){
|
||||
Box(
|
||||
contentAlignment = Alignment.Center,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(300.dp)
|
||||
.clickable {
|
||||
val intent = Intent(activity, DSBDayViewActivity::class.java).apply {
|
||||
putExtra(DSBDayViewActivity.FOLDER_KEY, folder.absolutePath)
|
||||
}
|
||||
activity.startActivity(intent)
|
||||
}
|
||||
) {
|
||||
val information = JSONObject(File(folder, "info.json").readText())
|
||||
val previewImageBitmap = BitmapFactory.decodeFile(File(folder, "0.jpg").absolutePath)
|
||||
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Text(
|
||||
text = "${information.get("FormatInfo")} - ${information.get("PlanInfo").toString().removeSuffix(".pdf")}",
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onBackground
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
Text(
|
||||
text = information.get("Date").toString(),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MiddleColor
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
Image(
|
||||
bitmap = previewImageBitmap.asImageBitmap(),
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
package com.schoolapp.cleverclass
|
||||
|
||||
import android.graphics.BitmapFactory
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.gestures.detectTransformGestures
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
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.lazy.LazyColumn
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.asImageBitmap
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.zIndex
|
||||
import com.schoolapp.cleverclass.ui.theme.CleverClassTheme
|
||||
import org.json.JSONObject
|
||||
import java.io.File
|
||||
import java.lang.Float.max
|
||||
import java.lang.Float.min
|
||||
|
||||
private lateinit var folder : File
|
||||
private lateinit var information : JSONObject
|
||||
|
||||
class DSBDayViewActivity : ComponentActivity() {
|
||||
companion object{
|
||||
const val FOLDER_KEY = "folder_key"
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
folder = File(intent.getStringExtra(FOLDER_KEY) ?: "")
|
||||
information = JSONObject(File(folder, "info.json").readText())
|
||||
|
||||
setContent {
|
||||
CleverClassTheme {
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
color = MaterialTheme.colorScheme.background
|
||||
) {
|
||||
DSBDayViewContent(activity = this)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Content of DSBDayView
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun DSBDayViewContent(activity: ComponentActivity){
|
||||
var scale by remember { mutableStateOf(1f) }
|
||||
var XOffset by remember { mutableStateOf(0f) }
|
||||
var YOffset by remember { mutableStateOf(0f) }
|
||||
|
||||
Column{
|
||||
TopAppBar(
|
||||
colors = TopAppBarDefaults.centerAlignedTopAppBarColors(MaterialTheme.colorScheme.primaryContainer),
|
||||
title = {
|
||||
Text(
|
||||
text = information.get("PlanInfo").toString().removeSuffix(".pdf"),
|
||||
style = MaterialTheme.typography.headlineSmall,
|
||||
color = MaterialTheme.colorScheme.onPrimaryContainer
|
||||
)},
|
||||
navigationIcon = {
|
||||
IconButton(onClick = { activity.finish() }) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.ArrowBack,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(28.dp),
|
||||
tint = MaterialTheme.colorScheme.onPrimaryContainer
|
||||
)
|
||||
}
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.zIndex(1f)
|
||||
)
|
||||
|
||||
val imageFiles = folder.listFiles { file -> file.extension == "jpg" }?.toList()
|
||||
|
||||
LazyColumn(
|
||||
userScrollEnabled = true,
|
||||
modifier = Modifier
|
||||
.padding(8.dp)
|
||||
.pointerInput(Unit) {
|
||||
detectTransformGestures { _, move, zoom, _ ->
|
||||
scale = max(min(zoom * scale, 2f), 1f)
|
||||
val maxXOffset = 520 * (scale - 1)
|
||||
val maxYOffset = 992 * (scale - 1)
|
||||
XOffset = max(min(XOffset + move.x, maxXOffset), -maxXOffset)
|
||||
YOffset = max(min(YOffset + move.y, maxYOffset), -maxYOffset)
|
||||
}
|
||||
}
|
||||
.graphicsLayer(
|
||||
scaleX = scale,
|
||||
scaleY = scale,
|
||||
translationX = XOffset,
|
||||
translationY = YOffset
|
||||
)
|
||||
) {
|
||||
imageFiles?.forEach {
|
||||
item {
|
||||
Image(
|
||||
bitmap = BitmapFactory.decodeFile(it.absolutePath).asImageBitmap(),
|
||||
contentDescription = null,
|
||||
contentScale = ContentScale.FillWidth,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
226
app/src/main/java/com/schoolapp/cleverclass/DSBDownloader.kt
Normal file
@@ -0,0 +1,226 @@
|
||||
package com.schoolapp.cleverclass
|
||||
|
||||
import android.content.Context
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.NetworkCapabilities
|
||||
import android.util.Base64
|
||||
import android.util.Log
|
||||
import org.json.JSONObject
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.File
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.URL
|
||||
import java.util.zip.GZIPInputStream
|
||||
import java.util.zip.GZIPOutputStream
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.FileOutputStream
|
||||
import java.net.SocketException
|
||||
import java.time.LocalDateTime
|
||||
import java.util.UUID
|
||||
|
||||
|
||||
suspend fun downloadDSB(context: Context): Int{
|
||||
val sharedPreferences = context.getSharedPreferences("DSBmobile", Context.MODE_PRIVATE)
|
||||
val username = sharedPreferences.getString("username", "").toString()
|
||||
val password = sharedPreferences.getString("password", "").toString()
|
||||
|
||||
if(!isInternetAvailable(context))
|
||||
return NO_INTERNET_CONNECTION_CODE
|
||||
|
||||
val mainDir = createDataFolder(context)
|
||||
|
||||
val response = downloadDataTask(username = username, password = password)
|
||||
if(response == "-1")
|
||||
return NO_INTERNET_CONNECTION_CODE
|
||||
val jsonResponse = JSONObject(response)
|
||||
if(jsonResponse.get("Resultcode") != 0)
|
||||
return jsonResponse.getInt("Resultcode")
|
||||
|
||||
return downloadImagesTask(jsonResponse, mainDir)
|
||||
}
|
||||
|
||||
private fun downloadDataTask(username: String, password: String) : String {
|
||||
return try {
|
||||
val requestBody = createRequestBody(username = username, password = password)
|
||||
val jsonRequest = createJsonRequest(requestBody)
|
||||
|
||||
val responseData = fetchData(jsonRequest)
|
||||
|
||||
responseData
|
||||
} catch (e: Exception) {
|
||||
Log.e("HTTP", "Error: ${e.message}")
|
||||
"-1"
|
||||
}
|
||||
}
|
||||
|
||||
private fun createRequestBody(username: String, password: String): String {
|
||||
val currentDateTime = LocalDateTime.now().toString()
|
||||
|
||||
val requestBody = JSONObject().apply {
|
||||
put("UserId", username)
|
||||
put("UserPw", password)
|
||||
put("AppVersion", "2.5.9")
|
||||
put("Language", "de")
|
||||
put("OsVersion", "28 8.0")
|
||||
put("AppId", UUID.randomUUID().toString())
|
||||
put("Device", "SM-G930F")
|
||||
put("BundleId", "de.heinekingmedia.dsbmobile")
|
||||
put("Date", currentDateTime)
|
||||
put("LastUpdate", currentDateTime)
|
||||
}
|
||||
|
||||
return Base64.encodeToString(GZIPCompress(requestBody.toString().toByteArray()), Base64.DEFAULT)
|
||||
}
|
||||
|
||||
private fun createJsonRequest(requestBody: String): JSONObject {
|
||||
return JSONObject().apply {
|
||||
put("req", JSONObject().apply {
|
||||
put("Data", requestBody)
|
||||
put("DataType", 1)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun fetchData(jsonRequest: JSONObject): String {
|
||||
val url = URL("https://app.dsbcontrol.de/JsonHandler.ashx/GetData")
|
||||
val connection = (url.openConnection() as HttpURLConnection).apply {
|
||||
requestMethod = "POST"
|
||||
setRequestProperty("Content-Type", "application/json")
|
||||
doOutput = true
|
||||
}
|
||||
|
||||
connection.outputStream.use { outputStream ->
|
||||
outputStream.write(jsonRequest.toString().toByteArray())
|
||||
}
|
||||
|
||||
return if (connection.responseCode == HttpURLConnection.HTTP_OK) {
|
||||
val responseData = connection.inputStream.bufferedReader().use { it.readText() }
|
||||
val compressedResponseData = JSONObject(responseData).getString("d")
|
||||
decompressGZIPAndDecodeBase64(compressedResponseData)
|
||||
} else {
|
||||
Log.e("HTTP", "Error response code: ${connection.responseCode}")
|
||||
""
|
||||
}
|
||||
}
|
||||
|
||||
private fun GZIPCompress(data: ByteArray): ByteArray {
|
||||
return ByteArrayOutputStream().use { outputStream ->
|
||||
GZIPOutputStream(outputStream).bufferedWriter().use { it.write(String(data)) }
|
||||
outputStream.toByteArray()
|
||||
}
|
||||
}
|
||||
|
||||
private fun decompressGZIPAndDecodeBase64(compressedData: String): String {
|
||||
val compressedByteArray = Base64.decode(compressedData, Base64.DEFAULT)
|
||||
val inputStream = ByteArrayInputStream(compressedByteArray)
|
||||
val outputStream = ByteArrayOutputStream()
|
||||
GZIPInputStream(inputStream).use { gzipInputStream ->
|
||||
val buffer = ByteArray(1024)
|
||||
var len: Int
|
||||
while (gzipInputStream.read(buffer).also { len = it } > 0) {
|
||||
outputStream.write(buffer, 0, len)
|
||||
}
|
||||
}
|
||||
return outputStream.toString("UTF-8")
|
||||
}
|
||||
|
||||
private fun downloadImagesTask(jsonResponse: JSONObject, mainDir : File) : Int {
|
||||
clearFolder(mainDir)
|
||||
|
||||
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()) {
|
||||
val jsonDayObject = jsonDaysObject.getJSONObject(d)
|
||||
val dayID = jsonDayObject.get("Id")
|
||||
|
||||
val day = File(mainDir, dayID.toString())
|
||||
day.mkdir()
|
||||
createDayInformationJson(day, jsonDayObject)
|
||||
|
||||
val jsonPagesObject = jsonDayObject.getJSONArray("Childs")
|
||||
|
||||
for (p in 0 until jsonPagesObject.length()) {
|
||||
val jsonPageObject = jsonPagesObject.getJSONObject(p)
|
||||
val pageIndex = jsonPageObject.get("Index")
|
||||
val imageLink = jsonPageObject.get("Detail").toString()
|
||||
|
||||
val downloadState = downloadImage(day, URL(imageLink), "$pageIndex.jpg")
|
||||
if (downloadState != 0)
|
||||
return downloadState
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
private fun createDayInformationJson(folder: File, jsonDayObject: JSONObject){
|
||||
val infoFile = File(folder, "info.json")
|
||||
val date = jsonDayObject.get("Date")
|
||||
val formatInfo = jsonDayObject.get("Title")
|
||||
val planInfo = jsonDayObject.getJSONArray("Childs").getJSONObject(0).get("Title")
|
||||
|
||||
val infoJson = JSONObject()
|
||||
infoJson.put("Date", date)
|
||||
infoJson.put("FormatInfo", formatInfo)
|
||||
infoJson.put("PlanInfo", planInfo)
|
||||
|
||||
infoFile.writeText(infoJson.toString())
|
||||
}
|
||||
|
||||
private fun downloadImage(folder: File, url: URL, fileName: String) : Int {
|
||||
val file = File(folder, fileName)
|
||||
|
||||
try {
|
||||
val connection = url.openConnection()
|
||||
val inputStream = connection.getInputStream()
|
||||
val outputStream = FileOutputStream(file)
|
||||
|
||||
val buffer = ByteArray(1024)
|
||||
var bytesRead: Int
|
||||
|
||||
while (inputStream.read(buffer).also { bytesRead = it } != -1) {
|
||||
outputStream.write(buffer, 0, bytesRead)
|
||||
}
|
||||
|
||||
inputStream.close()
|
||||
outputStream.close()
|
||||
}
|
||||
catch (e: SocketException){
|
||||
Log.d("ImageDownloader", e.toString())
|
||||
return NO_INTERNET_CONNECTION_CODE
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
private fun createDataFolder(context: Context) : File {
|
||||
val folderName = "dataDSB"
|
||||
val folder = File(context.filesDir, folderName)
|
||||
|
||||
if (!folder.exists()) {
|
||||
folder.mkdir()
|
||||
}
|
||||
return folder
|
||||
}
|
||||
|
||||
private fun isInternetAvailable(context: Context): Boolean {
|
||||
val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
||||
val network = connectivityManager.activeNetwork
|
||||
return try {
|
||||
val networkCapabilities = connectivityManager.getNetworkCapabilities(network)
|
||||
networkCapabilities?.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) ?: false
|
||||
} catch (e: SecurityException) {
|
||||
Log.e("Internet", e.toString())
|
||||
false
|
||||
}
|
||||
}
|
||||
316
app/src/main/java/com/schoolapp/cleverclass/DSBLoginActivity.kt
Normal file
@@ -0,0 +1,316 @@
|
||||
package com.schoolapp.cleverclass
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.compose.foundation.Image
|
||||
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.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.Delete
|
||||
import androidx.compose.material.icons.outlined.Info
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Surface
|
||||
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.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.res.painterResource
|
||||
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 com.schoolapp.cleverclass.ui.theme.CleverClassTheme
|
||||
import com.schoolapp.cleverclass.ui.theme.InputPrimaryColor
|
||||
import com.schoolapp.cleverclass.ui.theme.InputSecondaryColor
|
||||
|
||||
private lateinit var activityType : String
|
||||
|
||||
class DSBLoginActivity : ComponentActivity() {
|
||||
companion object{
|
||||
const val TYPE_KEY = "type_key"
|
||||
}
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
activityType = intent.getStringExtra(TYPE_KEY).toString()
|
||||
|
||||
setContent {
|
||||
CleverClassTheme {
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
color = MaterialTheme.colorScheme.background
|
||||
) {
|
||||
DSBLoginContent(activity = this)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Content of DSBLogin
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun DSBLoginContent(activity: ComponentActivity){
|
||||
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
|
||||
)
|
||||
|
||||
val sharedPreferences = activity.getSharedPreferences("DSBmobile", Context.MODE_PRIVATE)
|
||||
val editor = sharedPreferences.edit()
|
||||
|
||||
var username by remember {
|
||||
mutableStateOf(sharedPreferences.getString("username", "").toString())
|
||||
}
|
||||
var password by remember {
|
||||
mutableStateOf(sharedPreferences.getString("password", "").toString())
|
||||
}
|
||||
var showPassword by remember {
|
||||
mutableStateOf(false)
|
||||
}
|
||||
|
||||
var showInfo by remember {
|
||||
mutableStateOf(false)
|
||||
}
|
||||
var showDeleteConfirmation by remember {
|
||||
mutableStateOf(false)
|
||||
}
|
||||
|
||||
Column {
|
||||
TopAppBar(
|
||||
colors = TopAppBarDefaults.centerAlignedTopAppBarColors(MaterialTheme.colorScheme.primaryContainer),
|
||||
title = { Text(text = "") },
|
||||
navigationIcon = {
|
||||
IconButton(onClick = { activity.finish() }) {
|
||||
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())
|
||||
) {
|
||||
Surface(
|
||||
color = MaterialTheme.colorScheme.primaryContainer,
|
||||
shape = RoundedCornerShape(20),
|
||||
modifier = Modifier.padding(16.dp)
|
||||
) {
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier.padding(16.dp)
|
||||
) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Text(
|
||||
text = "DSBmobile Login Daten",
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
color = MaterialTheme.colorScheme.onPrimaryContainer,
|
||||
modifier = Modifier.padding(10.dp)
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
|
||||
if (activityType == "Settings")
|
||||
IconButton(onClick = { showDeleteConfirmation = !showDeleteConfirmation }) {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.Delete,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(28.dp),
|
||||
tint = MaterialTheme.colorScheme.onPrimaryContainer
|
||||
)
|
||||
}
|
||||
else if (activityType == "FirstTime")
|
||||
IconButton(onClick = { showInfo = !showInfo }) {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.Info,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.onPrimaryContainer
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
OutlinedTextField(
|
||||
value = username,
|
||||
onValueChange = { username = it },
|
||||
placeholder = {
|
||||
Text(
|
||||
text = "Benutzername",
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
color = MaterialTheme.colorScheme.primaryContainer
|
||||
)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
singleLine = true,
|
||||
shape = RoundedCornerShape(20),
|
||||
textStyle = MaterialTheme.typography.labelMedium,
|
||||
colors = inputFieldColors
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
OutlinedTextField(
|
||||
value = password,
|
||||
onValueChange = { password = it },
|
||||
placeholder = {
|
||||
Text(
|
||||
text = "Passwort",
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
color = MaterialTheme.colorScheme.primaryContainer
|
||||
)
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
singleLine = true,
|
||||
shape = RoundedCornerShape(20),
|
||||
textStyle = MaterialTheme.typography.labelMedium,
|
||||
colors = inputFieldColors,
|
||||
visualTransformation = if(showPassword) VisualTransformation.None else PasswordVisualTransformation(),
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password),
|
||||
trailingIcon = {
|
||||
IconButton(onClick = { showPassword = !showPassword })
|
||||
{
|
||||
Image(
|
||||
painter = painterResource(
|
||||
id = if(showPassword)
|
||||
R.drawable.baseline_visibility_24
|
||||
else
|
||||
R.drawable.baseline_visibility_off_24
|
||||
),
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
|
||||
Box(
|
||||
contentAlignment = Alignment.Center,
|
||||
modifier = Modifier.clickable {
|
||||
editor.putString("username", username)
|
||||
editor.putString("password", password)
|
||||
editor.apply()
|
||||
activity.finish()
|
||||
}
|
||||
) {
|
||||
Text(
|
||||
text = "Speichern",
|
||||
color = MaterialTheme.colorScheme.secondary,
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
modifier = Modifier.padding(10.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (showInfo) {
|
||||
AlertDialog(
|
||||
onDismissRequest = { showInfo = false },
|
||||
text = {
|
||||
Text(
|
||||
text = "Der Benutzername und das Passwort können später in den Einstellungen 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)
|
||||
)
|
||||
}
|
||||
|
||||
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("username", "")
|
||||
editor.putString("password", "")
|
||||
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
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.schoolapp.cleverclass
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences.Editor
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
@@ -20,9 +22,12 @@ import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.material.icons.filled.Check
|
||||
import androidx.compose.material.icons.filled.KeyboardArrowDown
|
||||
import androidx.compose.material.icons.filled.KeyboardArrowUp
|
||||
import androidx.compose.material.icons.outlined.Delete
|
||||
import androidx.compose.material.icons.outlined.Edit
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.BottomAppBar
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
@@ -37,6 +42,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
|
||||
@@ -45,18 +52,46 @@ 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
|
||||
|
||||
|
||||
private var SAWeight = 0
|
||||
private var name = "Test"
|
||||
|
||||
private var typesSelection = listOf("Test", "Ex", "Referat", "Mündlich", "Sonstiges")
|
||||
private val gradesSelection = listOf(1, 2, 3, 4, 5, 6)
|
||||
private 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()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
setContent {
|
||||
CleverClassTheme {
|
||||
@@ -64,7 +99,7 @@ class FachActivity : ComponentActivity() {
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
color = MaterialTheme.colorScheme.background
|
||||
) {
|
||||
GradeContent(activity = this, name = name)
|
||||
GradeContent(this, grades)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -73,7 +108,21 @@ class FachActivity : ComponentActivity() {
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun GradeContent(activity: ComponentActivity, name : String){
|
||||
fun GradeContent(activity: ComponentActivity, loadedGrades: List<GradeData>){
|
||||
val sharedPreferences = activity.getSharedPreferences("gradeAverages", Context.MODE_PRIVATE)
|
||||
val editor = sharedPreferences.edit()
|
||||
|
||||
var grades by remember {
|
||||
mutableStateOf(loadedGrades)
|
||||
}
|
||||
var average by remember {
|
||||
mutableStateOf(0.00f)
|
||||
}
|
||||
|
||||
var showDeleteConfirmation by remember {
|
||||
mutableStateOf(false)
|
||||
}
|
||||
|
||||
val colors = listOf(
|
||||
Color(0xFF536DFE),
|
||||
Color(0xFF448AFF),
|
||||
@@ -82,8 +131,15 @@ fun GradeContent(activity: ComponentActivity, name : String){
|
||||
Color(0xFF64FFDA),
|
||||
Color(0xFF69F0AE)
|
||||
)
|
||||
var grades by remember {
|
||||
mutableStateOf(emptyList<GradeData>())
|
||||
|
||||
LaunchedEffect(grades) {
|
||||
average = calculateAverage(grades, editor)
|
||||
}
|
||||
|
||||
DisposableEffect(activity) {
|
||||
onDispose {
|
||||
saveGradesStart(activity, grades)
|
||||
}
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
@@ -92,14 +148,30 @@ fun GradeContent(activity: ComponentActivity, name : String){
|
||||
colors = TopAppBarDefaults.centerAlignedTopAppBarColors(MaterialTheme.colorScheme.primaryContainer),
|
||||
title = {
|
||||
Text(text = name,
|
||||
style = MaterialTheme.typography.headlineSmall
|
||||
style = MaterialTheme.typography.headlineSmall,
|
||||
color = MaterialTheme.colorScheme.onPrimaryContainer
|
||||
)},
|
||||
navigationIcon = {
|
||||
IconButton(onClick = { activity.finish() }) {
|
||||
IconButton(onClick = {
|
||||
typesSelection = listOf("Test", "Ex", "Referat", "Mündlich", "Sonstiges")
|
||||
saveGradesStart(activity, grades)
|
||||
activity.finish()
|
||||
}) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.ArrowBack,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(28.dp)
|
||||
modifier = Modifier.size(28.dp),
|
||||
tint = MaterialTheme.colorScheme.onPrimaryContainer
|
||||
)
|
||||
}
|
||||
},
|
||||
actions = {
|
||||
IconButton(onClick = { showDeleteConfirmation = true }) {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.Delete,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(28.dp),
|
||||
tint = MaterialTheme.colorScheme.onPrimaryContainer
|
||||
)
|
||||
}
|
||||
},
|
||||
@@ -120,7 +192,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 = "Ø" + formatFloat(average),
|
||||
style = MaterialTheme.typography.headlineSmall)
|
||||
Spacer(modifier = Modifier.width(16.dp))
|
||||
}
|
||||
@@ -128,7 +201,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,
|
||||
@@ -142,23 +220,102 @@ fun GradeContent(activity: ComponentActivity, name : String){
|
||||
.verticalScroll(rememberScrollState()))
|
||||
{
|
||||
grades.forEach{ gradeData ->
|
||||
GradePrefab(onClose = { grades = grades.filter { it != gradeData } }, color = gradeData.color)
|
||||
GradePrefab(
|
||||
id = gradeData.id,
|
||||
onClose = {
|
||||
grades = grades.filter { it.id != gradeData.id }
|
||||
saveGradesStart(activity, grades) },
|
||||
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, editor)
|
||||
saveGradesStart(activity, grades) },
|
||||
onWeightChange = { weight ->
|
||||
grades.find { it.id == gradeData.id }?.weight = weight
|
||||
average = calculateAverage(grades, editor)
|
||||
saveGradesStart(activity, grades) },
|
||||
onTypeChange = { type ->
|
||||
grades.find { it.id == gradeData.id }?.type = type
|
||||
average = calculateAverage(grades, editor)
|
||||
saveGradesStart(activity, grades) }
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.height(216.dp))
|
||||
}
|
||||
}
|
||||
|
||||
if (showDeleteConfirmation){
|
||||
AlertDialog(
|
||||
onDismissRequest = { showDeleteConfirmation = false },
|
||||
text = { Text(text = "Möchten Sie wirklich alle Noten 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 {
|
||||
grades = emptyList()
|
||||
saveGradesStart(activity, grades)
|
||||
editor.putFloat(name, 0.0f).apply()
|
||||
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
|
||||
fun GradePrefab(onClose: () -> Unit, color: Color){
|
||||
var typeExpanded by remember {
|
||||
fun GradePrefab(
|
||||
id: Int,
|
||||
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(id) {
|
||||
mutableStateOf(false)
|
||||
}
|
||||
var gradeExpanded by remember {
|
||||
|
||||
var typeExpanded by remember(id) {
|
||||
mutableStateOf(false)
|
||||
}
|
||||
var weightExpanded by remember {
|
||||
var gradeExpanded by remember(id) {
|
||||
mutableStateOf(false)
|
||||
}
|
||||
var weightExpanded by remember(id) {
|
||||
mutableStateOf(false)
|
||||
}
|
||||
|
||||
var selectedType by remember(id) {
|
||||
mutableStateOf(type)
|
||||
}
|
||||
var selectedGrade by remember(id) {
|
||||
mutableStateOf(grade)
|
||||
}
|
||||
var selectedWeight by remember(id) {
|
||||
mutableStateOf(weight)
|
||||
}
|
||||
|
||||
// Background
|
||||
Surface(
|
||||
@@ -166,47 +323,90 @@ fun GradePrefab(onClose: () -> Unit, color: Color){
|
||||
color = color,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(128.dp)
|
||||
.height(120.dp)
|
||||
.padding(start = 16.dp, end = 16.dp, top = 16.dp)
|
||||
) {
|
||||
// Content
|
||||
Column(modifier = Modifier.padding(16.dp)) {
|
||||
// Top Row
|
||||
Row {
|
||||
// Type
|
||||
Surface(
|
||||
shape = RoundedCornerShape(10),
|
||||
color = color,
|
||||
border = BorderStroke(2.dp, TextOnColouredButton),
|
||||
border = if(editing) BorderStroke(2.dp, TextOnColouredButton) else null,
|
||||
modifier = Modifier
|
||||
.size(width = 128.dp, height = 32.dp)
|
||||
.clickable { typeExpanded = !typeExpanded }
|
||||
.size(width = 170.dp, height = 32.dp)
|
||||
.let { if (editing) it.clickable { typeExpanded = !typeExpanded } else it }
|
||||
) {
|
||||
Row(verticalAlignment = CenterVertically,
|
||||
modifier = Modifier.padding(4.dp)) {
|
||||
Text(text = "Typ", color = TextOnColouredButton, style = MaterialTheme.typography.labelMedium)
|
||||
DropdownMenu(expanded = typeExpanded, onDismissRequest = { typeExpanded = false }) {
|
||||
DropdownMenuItem(text = { Text(text = "Test") }, onClick = { /*TODO*/ })
|
||||
Text(text = selectedType, color = TextOnColouredButton, style = MaterialTheme.typography.labelMedium)
|
||||
|
||||
if(editing) {
|
||||
DropdownMenu(
|
||||
expanded = typeExpanded,
|
||||
onDismissRequest = { typeExpanded = false }
|
||||
){
|
||||
typesSelection.forEach { type ->
|
||||
Item(
|
||||
name = type,
|
||||
onItemClick = {
|
||||
selectedType = type
|
||||
typeExpanded = !typeExpanded
|
||||
onTypeChange(selectedType)
|
||||
}
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
Icon(imageVector = if (typeExpanded) Icons.Filled.KeyboardArrowUp else Icons.Filled.KeyboardArrowDown,
|
||||
contentDescription = null,
|
||||
tint = TextOnColouredButton)
|
||||
)
|
||||
}
|
||||
}
|
||||
// Delete
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Close,
|
||||
imageVector = if (typeExpanded) Icons.Filled.KeyboardArrowUp else Icons.Filled.KeyboardArrowDown,
|
||||
contentDescription = null,
|
||||
tint = TextOnColouredButton
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Buttons
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
if (editing) {
|
||||
// Delete
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.Delete,
|
||||
contentDescription = null,
|
||||
tint = TextOnColouredButton,
|
||||
modifier = Modifier
|
||||
.size(28.dp)
|
||||
.clickable { onClose() }
|
||||
)
|
||||
Spacer(modifier = Modifier.width(4.dp))
|
||||
// Confirm
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Check,
|
||||
contentDescription = null,
|
||||
tint = TextOnColouredButton,
|
||||
modifier = Modifier
|
||||
.size(28.dp)
|
||||
.clickable { editing = !editing }
|
||||
)
|
||||
}
|
||||
else{
|
||||
// Edit
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.Edit,
|
||||
contentDescription = null,
|
||||
tint = TextOnColouredButton,
|
||||
modifier = Modifier
|
||||
.size(28.dp)
|
||||
.clickable { editing = !editing }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
|
||||
// Bottom Row
|
||||
Row (verticalAlignment = CenterVertically) {
|
||||
// Grade
|
||||
Text(text = "Note:", color = TextOnColouredButton, style = MaterialTheme.typography.labelMedium)
|
||||
@@ -214,41 +414,94 @@ fun GradePrefab(onClose: () -> Unit, color: Color){
|
||||
Surface(
|
||||
shape = RoundedCornerShape(10),
|
||||
color = color,
|
||||
border = BorderStroke(2.dp, TextOnColouredButton),
|
||||
border = if(editing) BorderStroke(2.dp, TextOnColouredButton) else null,
|
||||
modifier = Modifier
|
||||
.size(width = 48.dp, height = 32.dp)
|
||||
.clickable { gradeExpanded = !gradeExpanded }
|
||||
.let { if (editing) it.clickable { gradeExpanded = !gradeExpanded } else it }
|
||||
) {
|
||||
Row(verticalAlignment = CenterVertically,
|
||||
modifier = Modifier.padding(4.dp)) {
|
||||
Text(text = "1", color = TextOnColouredButton, style = MaterialTheme.typography.labelMedium)
|
||||
DropdownMenu(expanded = gradeExpanded, onDismissRequest = { gradeExpanded = false }) {
|
||||
DropdownMenuItem(text = { Text(text = "Test") }, onClick = { /*TODO*/ })
|
||||
Text(text = selectedGrade.toString(), color = TextOnColouredButton, style = MaterialTheme.typography.labelMedium)
|
||||
|
||||
if(editing) {
|
||||
DropdownMenu(
|
||||
expanded = gradeExpanded,
|
||||
onDismissRequest = { gradeExpanded = false }) {
|
||||
gradesSelection.forEach { grade ->
|
||||
Item(
|
||||
name = grade.toString(),
|
||||
onItemClick = {
|
||||
selectedGrade = grade
|
||||
gradeExpanded = !gradeExpanded
|
||||
onGradeChange(selectedGrade)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
Icon(imageVector = if (gradeExpanded) Icons.Filled.KeyboardArrowUp else Icons.Filled.KeyboardArrowDown, contentDescription = null, tint = TextOnColouredButton)
|
||||
Icon(
|
||||
imageVector = if (gradeExpanded) Icons.Filled.KeyboardArrowUp else Icons.Filled.KeyboardArrowDown,
|
||||
contentDescription = null,
|
||||
tint = TextOnColouredButton
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Weight
|
||||
Spacer(modifier = Modifier.width(16.dp))
|
||||
Text(text = "Gewichtung:", color = TextOnColouredButton, style = MaterialTheme.typography.labelMedium)
|
||||
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 = BorderStroke(2.dp, TextOnColouredButton),
|
||||
border = if (editing) BorderStroke(2.dp, TextOnColouredButton) else null,
|
||||
modifier = Modifier
|
||||
.size(width = 64.dp, height = 32.dp)
|
||||
.clickable { weightExpanded = !weightExpanded }
|
||||
.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 = "1x", color = TextOnColouredButton, style = MaterialTheme.typography.labelMedium)
|
||||
DropdownMenu(expanded = weightExpanded, onDismissRequest = { weightExpanded = false }) {
|
||||
DropdownMenuItem(text = { Text(text = "Test") }, onClick = { /*TODO*/ })
|
||||
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)
|
||||
Icon(
|
||||
imageVector = if (weightExpanded) Icons.Filled.KeyboardArrowUp else Icons.Filled.KeyboardArrowDown,
|
||||
contentDescription = null,
|
||||
tint = TextOnColouredButton
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -256,5 +509,57 @@ fun GradePrefab(onClose: () -> Unit, color: Color){
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Item(name : String, onItemClick: (String) -> Unit){
|
||||
DropdownMenuItem(
|
||||
text = { Text(text = name, style = MaterialTheme.typography.labelMedium) },
|
||||
onClick = { onItemClick(name) }
|
||||
)
|
||||
}
|
||||
|
||||
data class GradeData(val id: Int, val color: Color)
|
||||
data class GradeData(
|
||||
val id: Int,
|
||||
val color: Color,
|
||||
var grade: Int,
|
||||
var weight: Float,
|
||||
var type: String
|
||||
)
|
||||
|
||||
fun calculateAverage(grades: List<GradeData>, editor: Editor): 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
|
||||
|
||||
|
||||
val finalGrade = if (smallAverage.isNaN()) saAverage
|
||||
else if (saAverage.isNaN()) smallAverage
|
||||
else (smallAverage + saAverage * SAWeight) / (1 + SAWeight)
|
||||
|
||||
editor.putFloat(name, finalGrade).apply()
|
||||
|
||||
return finalGrade
|
||||
}
|
||||
|
||||
fun saveGradesStart(context: Context, grades: List<GradeData>){
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
saveGrades(context, grades, name)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.schoolapp.cleverclass
|
||||
|
||||
import android.content.Context
|
||||
import androidx.datastore.core.DataStore
|
||||
import androidx.datastore.preferences.core.Preferences
|
||||
import androidx.datastore.preferences.core.edit
|
||||
import androidx.datastore.preferences.core.stringPreferencesKey
|
||||
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 LessonStoreManager {
|
||||
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "lesson_preferences")
|
||||
|
||||
suspend fun saveLessons(context: Context, lessons: List<LessonData>, day: String) {
|
||||
context.dataStore.edit { preferences ->
|
||||
val json = Gson().toJson(lessons)
|
||||
preferences[stringPreferencesKey(day)] = json
|
||||
}
|
||||
}
|
||||
|
||||
fun getLessons(context: Context, day: String): Flow<List<LessonData>> {
|
||||
return context.dataStore.data.map { preferences ->
|
||||
val json = preferences[stringPreferencesKey(day)] ?: return@map emptyList()
|
||||
val type = object : TypeToken<List<LessonData>>() {}.type
|
||||
Gson().fromJson(json, type)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package com.schoolapp.cleverclass
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
@@ -42,7 +43,6 @@ import com.schoolapp.cleverclass.ui.theme.TextOnColouredButton
|
||||
class MainActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
readData(applicationContext)
|
||||
setContent {
|
||||
CleverClassTheme {
|
||||
Surface(modifier = Modifier.fillMaxSize(),
|
||||
@@ -67,7 +67,7 @@ fun MainContent(activity: ComponentActivity){
|
||||
title = {
|
||||
Text(text = "CleverClass",
|
||||
style = MaterialTheme.typography.headlineMedium,
|
||||
color = MaterialTheme.colorScheme.onPrimary)
|
||||
color = MaterialTheme.colorScheme.onPrimaryContainer)
|
||||
},
|
||||
actions = {
|
||||
IconButton(
|
||||
@@ -75,7 +75,8 @@ fun MainContent(activity: ComponentActivity){
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Settings,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(28.dp))
|
||||
modifier = Modifier.size(28.dp),
|
||||
tint = MaterialTheme.colorScheme.onPrimaryContainer)
|
||||
}
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
@@ -85,8 +86,15 @@ fun MainContent(activity: ComponentActivity){
|
||||
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, PSEActivity::class.java) }, color = Color(0xFF536DFE), text = "Periodensystem", sharedPreferences)
|
||||
MainButton(onClick = { /*TODO: Mebis activity*/ }, color = Color(0xFF7C4DFF), text = "Mebis", sharedPreferences)
|
||||
MainButton(onClick = { /*TODO: DSBmobile activity*/ }, color = Color(0xFFFF6E40), text = "DSBmobile", sharedPreferences)
|
||||
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/"))
|
||||
activity.startActivity(intent)
|
||||
},
|
||||
color = Color(0xFF7C4DFF),
|
||||
text = "Mebis", sharedPreferences
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
package com.schoolapp.cleverclass
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.content.SharedPreferences.Editor
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
@@ -12,27 +16,79 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material.icons.outlined.Delete
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.BottomAppBar
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.Divider
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Surface
|
||||
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.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.unit.dp
|
||||
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
|
||||
|
||||
private val subjects = listOf(
|
||||
"Mathe", "Deutsch", "Englisch", "Französisch", "Latein",
|
||||
"Physik", "Chemie", "Biologie", "Informatik",
|
||||
"Geographie", "Wirtschaft", "Geschichte", "Politik und Gesellschaft",
|
||||
"Ethik", "Evangelisch", "Katholisch",
|
||||
"Kunst", "Musik", "Sport"
|
||||
)
|
||||
|
||||
private val saWeights = listOf(
|
||||
2, 2, 2, 2, 2,
|
||||
1, 1, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0,
|
||||
0, 0, 0
|
||||
)
|
||||
|
||||
private val colorsA = listOf(
|
||||
Color(0xFF536DFE),
|
||||
Color(0xFF18FFFF),
|
||||
Color(0xFF448AFF),
|
||||
Color(0xFF64FFDA),
|
||||
Color(0xFF40C4FF)
|
||||
)
|
||||
private val colorsB = listOf(
|
||||
Color(0xFFFFFF00),
|
||||
Color(0xFFFFAB40),
|
||||
Color(0xFFFFD740),
|
||||
Color(0xFFEEFF41)
|
||||
)
|
||||
private val colorsC = listOf(
|
||||
Color(0xFFFF5252),
|
||||
Color(0xFFFF4081),
|
||||
Color(0xFFFF6052),
|
||||
Color(0xFFFF6E40)
|
||||
)
|
||||
|
||||
|
||||
class NotenActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
@@ -54,40 +110,193 @@ class NotenActivity : ComponentActivity() {
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun NotenContent(activity: ComponentActivity){
|
||||
Column() {
|
||||
val sharedPreferences = activity.getSharedPreferences("gradeAverages", Context.MODE_PRIVATE)
|
||||
val editor = sharedPreferences.edit()
|
||||
|
||||
var allAverage by remember {
|
||||
mutableStateOf(0f)
|
||||
}
|
||||
|
||||
allAverage = calculateAllAverage(sharedPreferences)
|
||||
|
||||
var showDeleteConfirmation by remember {
|
||||
mutableStateOf(false)
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
colors = TopAppBarDefaults.centerAlignedTopAppBarColors(MaterialTheme.colorScheme.primaryContainer),
|
||||
title = {
|
||||
Text(text = "Noten",
|
||||
style = MaterialTheme.typography.headlineSmall
|
||||
style = MaterialTheme.typography.headlineSmall,
|
||||
color = MaterialTheme.colorScheme.onPrimaryContainer
|
||||
)},
|
||||
navigationIcon = {
|
||||
IconButton(onClick = { activity.finish() }) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.ArrowBack,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(28.dp)
|
||||
modifier = Modifier.size(28.dp),
|
||||
tint = MaterialTheme.colorScheme.onPrimaryContainer
|
||||
)
|
||||
}
|
||||
},
|
||||
actions = {
|
||||
IconButton(onClick = { showDeleteConfirmation = true }) {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.Delete,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(28.dp),
|
||||
tint = MaterialTheme.colorScheme.onPrimaryContainer
|
||||
)
|
||||
}
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
|
||||
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)
|
||||
},
|
||||
bottomBar = {
|
||||
BottomAppBar(
|
||||
containerColor = MaterialTheme.colorScheme.primaryContainer,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(64.dp)
|
||||
) {
|
||||
Row (modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.align(Alignment.CenterVertically)){
|
||||
Spacer(modifier = Modifier.width(16.dp))
|
||||
Text(text = "Gesamtschnitt:",
|
||||
style = MaterialTheme.typography.headlineSmall)
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
Text(
|
||||
text = "Ø" + formatFloat(allAverage),
|
||||
style = MaterialTheme.typography.headlineSmall)
|
||||
Spacer(modifier = Modifier.width(16.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
) {innerPadding ->
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(innerPadding)
|
||||
)
|
||||
{
|
||||
GroopTitle("Hauptfächer", true)
|
||||
for (i in 0..4){
|
||||
FachButton(
|
||||
color = colorsA[i],
|
||||
subject = subjects[i],
|
||||
saWeight = saWeights[i],
|
||||
activity = activity,
|
||||
sharedPreferences = sharedPreferences,
|
||||
onAvgChange = { allAverage = calculateAllAverage(sharedPreferences) }
|
||||
)
|
||||
}
|
||||
|
||||
GroopTitle("Naturwissenschaften", false)
|
||||
for (i in 5..8){
|
||||
FachButton(
|
||||
color = colorsB[i - 5],
|
||||
subject = subjects[i],
|
||||
saWeight = saWeights[i],
|
||||
activity = activity,
|
||||
sharedPreferences = sharedPreferences,
|
||||
onAvgChange = { allAverage = calculateAllAverage(sharedPreferences) }
|
||||
)
|
||||
}
|
||||
|
||||
GroopTitle("Gesellschaftswissenschaften", false)
|
||||
for (i in 9..12){
|
||||
FachButton(
|
||||
color = colorsC[i - 9],
|
||||
subject = subjects[i],
|
||||
saWeight = saWeights[i],
|
||||
activity = activity,
|
||||
sharedPreferences = sharedPreferences,
|
||||
onAvgChange = { allAverage = calculateAllAverage(sharedPreferences) }
|
||||
)
|
||||
}
|
||||
|
||||
GroopTitle("Religionen", false)
|
||||
for (i in 13..15){
|
||||
FachButton(
|
||||
color = colorsA[i - 13],
|
||||
subject = subjects[i],
|
||||
saWeight = saWeights[i],
|
||||
activity = activity,
|
||||
sharedPreferences = sharedPreferences,
|
||||
onAvgChange = { allAverage = calculateAllAverage(sharedPreferences) }
|
||||
)
|
||||
}
|
||||
|
||||
GroopTitle("Sonstiges", false)
|
||||
for (i in 16..18){
|
||||
FachButton(
|
||||
color = colorsB[i - 16],
|
||||
subject = subjects[i],
|
||||
saWeight = saWeights[i],
|
||||
activity = activity,
|
||||
sharedPreferences = sharedPreferences,
|
||||
onAvgChange = { allAverage = calculateAllAverage(sharedPreferences) }
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
}
|
||||
}
|
||||
|
||||
if (showDeleteConfirmation){
|
||||
AlertDialog(
|
||||
onDismissRequest = { showDeleteConfirmation = false },
|
||||
text = { Text(text = "Möchten Sie wirklich alle Noten aus Allen Fächern 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 {
|
||||
deleteAll(editor, 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
|
||||
fun FachButton(color : Color, text : String, average : Float, activity : ComponentActivity){
|
||||
fun FachButton(
|
||||
color : Color,
|
||||
subject : String,
|
||||
saWeight: Int,
|
||||
activity: ComponentActivity,
|
||||
sharedPreferences: SharedPreferences,
|
||||
onAvgChange: () -> Unit){
|
||||
var avg by remember {
|
||||
mutableStateOf(sharedPreferences.getFloat(subject, 0.0f))
|
||||
}
|
||||
|
||||
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, subject)
|
||||
putExtra(FachActivity.WEIGHT_KEY, saWeight)
|
||||
}
|
||||
activity.startActivity(intent) },
|
||||
shape = RoundedCornerShape(40),
|
||||
colors = ButtonDefaults.outlinedButtonColors(containerColor = color),
|
||||
@@ -97,13 +306,76 @@ fun FachButton(color : Color, text : String, average : Float, activity : Compone
|
||||
.padding(start = 16.dp, end = 16.dp, top = 16.dp)
|
||||
) {
|
||||
Row(modifier = Modifier.fillMaxWidth()) {
|
||||
Text(text = text,
|
||||
Text(text = subject,
|
||||
color = TextOnColouredButton,
|
||||
style = MaterialTheme.typography.labelMedium)
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
Text(text = "Ø$average",
|
||||
Text(text = if (avg != 0.0f) "Ø${formatFloat(avg)}" else "---",
|
||||
color = TextOnColouredButton,
|
||||
style = MaterialTheme.typography.labelMedium)
|
||||
}
|
||||
}
|
||||
|
||||
// changes state when Shared Preference is updated
|
||||
val observer = remember {
|
||||
SharedPreferences.OnSharedPreferenceChangeListener { _, key ->
|
||||
if (key == subject) {
|
||||
avg = sharedPreferences.getFloat(subject, 0.0f)
|
||||
onAvgChange()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// handles observer disposal
|
||||
DisposableEffect(Unit) {
|
||||
sharedPreferences.registerOnSharedPreferenceChangeListener(observer)
|
||||
onDispose {
|
||||
sharedPreferences.unregisterOnSharedPreferenceChangeListener(observer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun GroopTitle(name: String, top: Boolean){
|
||||
if(!top) {
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
Divider()
|
||||
}
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
Text(text = name, style = MaterialTheme.typography.headlineSmall, color = MaterialTheme.colorScheme.onBackground)
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
}
|
||||
|
||||
fun calculateAllAverage(sharedPreferences: SharedPreferences): Float{
|
||||
var total = 0.0f
|
||||
var number = 0
|
||||
val avgs = loadAllAverages(sharedPreferences)
|
||||
|
||||
avgs.forEach{ avg ->
|
||||
if (avg != 0.0f && !avg.isNaN()){
|
||||
total += avg
|
||||
number += 1
|
||||
}
|
||||
}
|
||||
|
||||
return (total / number)
|
||||
}
|
||||
|
||||
fun loadAllAverages(sharedPreferences: SharedPreferences): List<Float>{
|
||||
val avgs = emptyList<Float>().toMutableList()
|
||||
|
||||
subjects.forEach { name ->
|
||||
avgs += sharedPreferences.getFloat(name, 0.0f)
|
||||
}
|
||||
|
||||
return avgs
|
||||
}
|
||||
|
||||
fun deleteAll(editor: Editor, context: Context){
|
||||
subjects.forEach{name ->
|
||||
editor.putFloat(name, 0.0f).apply()
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
GradeStoreManager.saveGrades(context, emptyList(), name)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,36 +1,60 @@
|
||||
package com.schoolapp.cleverclass
|
||||
|
||||
import android.app.AlertDialog
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.horizontalScroll
|
||||
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.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.wrapContentWidth
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material.icons.outlined.Info
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
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.unit.dp
|
||||
import com.schoolapp.cleverclass.ui.theme.CleverClassTheme
|
||||
import org.json.JSONObject
|
||||
import java.io.InputStreamReader
|
||||
|
||||
class PSEActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContent {
|
||||
CleverClassTheme {
|
||||
Surface(modifier = Modifier.fillMaxSize(),
|
||||
color = MaterialTheme.colorScheme.background) {
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
color = MaterialTheme.colorScheme.background)
|
||||
{
|
||||
PSEContent(activity = this)
|
||||
}
|
||||
}
|
||||
@@ -38,17 +62,78 @@ class PSEActivity : ComponentActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PSEContent(activity: ComponentActivity){
|
||||
var loading by remember {
|
||||
mutableStateOf(true)
|
||||
}
|
||||
|
||||
LaunchedEffect(key1 = true) {
|
||||
loading = false
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
StaticLoadingScreen()
|
||||
} else {
|
||||
PSEMainContent(activity)
|
||||
}
|
||||
}
|
||||
|
||||
private lateinit var rootObj : JSONObject
|
||||
|
||||
private val colors = listOf(
|
||||
Color(0xFFBCA76D),
|
||||
Color(0xFFC65253),
|
||||
Color(0xFF337733),
|
||||
Color(0xFFDC2222),
|
||||
Color(0xFF129DC3),
|
||||
Color(0xFFDDCC69),
|
||||
Color(0xFF7044DD)
|
||||
)
|
||||
private val shells = listOf(
|
||||
"K", "L", "M", "N", "O", "P", "Q"
|
||||
)
|
||||
|
||||
private val elementNameList = listOf(
|
||||
"H", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "He",
|
||||
"Li", "Be", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "B", "C", "N", "O", "F", "Ne",
|
||||
"Na", "Mg", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "Al", "Si", "P", "S", "Cl", "Ar",
|
||||
"K", "Ca", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "Sc", "Ti", "V", "Cr", "Mn", "Fe", "Co", "Ni", "Cu", "Zn", "Ga", "Ge", "As", "Se", "Br", "Kr",
|
||||
"Rb", "Sr", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "Y", "Zr", "Nb", "Mo", "Tc", "Ru", "Rh", "Pd", "Ag", "Cd", "In", "Sn", "Sb", "Te", "I", "Xe",
|
||||
"Cs", "Ba", "La", "Ce", "Pr", "Nd", "Pm", "Sm", "Eu", "Gd", "Tb", "Dy", "Ho", "Er", "Tm", "Yb", "Lu", "Hf", "Ta", "W", "Re", "Os", "Ir", "Pt", "Au", "Hg", "Tl", "Pb", "Bi", "Po", "At", "Rn",
|
||||
"Fr", "Ra", "Ac", "Th", "Pa", "U", "Np", "Pu", "Am", "Cm", "Bk", "Cf", "Es", "Fm", "Md", "No", "Lr", "Rf", "Db", "Sg", "Bh", "Hs", "Mt", "Ds", "Rg", "Cn", "Nh", "Fl", "Mc", "Lv", "Ts", "Og"
|
||||
)
|
||||
private val elementButtonColors = listOf(
|
||||
0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0,
|
||||
1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1,
|
||||
2, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2, 2, 2, 2, 2, 2,
|
||||
3, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3,
|
||||
4, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4,
|
||||
5, 5, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5,
|
||||
6, 6, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6
|
||||
)
|
||||
|
||||
// Content of PSE
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun PSEContent(activity: ComponentActivity){
|
||||
Column() {
|
||||
fun PSEMainContent(activity: ComponentActivity) {
|
||||
val inputStream = activity.assets.open("elements_data.json")
|
||||
rootObj = JSONObject(InputStreamReader(inputStream).readText())
|
||||
|
||||
var showInfo by remember {
|
||||
mutableStateOf(false)
|
||||
}
|
||||
var dialogChooser by remember {
|
||||
mutableStateOf(-1)
|
||||
}
|
||||
Column {
|
||||
TopAppBar(
|
||||
colors = TopAppBarDefaults.centerAlignedTopAppBarColors(MaterialTheme.colorScheme.primaryContainer),
|
||||
title = {
|
||||
Text(
|
||||
text = "Periodensystem",
|
||||
style = MaterialTheme.typography.headlineSmall
|
||||
text = "Periodensystem der Elemente",
|
||||
style = MaterialTheme.typography.headlineSmall,
|
||||
color = MaterialTheme.colorScheme.onPrimaryContainer
|
||||
)
|
||||
},
|
||||
navigationIcon = {
|
||||
@@ -56,26 +141,173 @@ fun PSEContent(activity: ComponentActivity){
|
||||
Icon(
|
||||
imageVector = Icons.Filled.ArrowBack,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(28.dp)
|
||||
modifier = Modifier.size(28.dp),
|
||||
tint = MaterialTheme.colorScheme.onPrimaryContainer
|
||||
)
|
||||
}
|
||||
},
|
||||
actions = {
|
||||
IconButton(onClick = {
|
||||
showInfo = true
|
||||
}) {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.Info,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(28.dp),
|
||||
tint = MaterialTheme.colorScheme.onPrimaryContainer
|
||||
)
|
||||
}
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
|
||||
Column() {
|
||||
//region //Element dialog definitions
|
||||
val HBuilder = AlertDialog.Builder(activity)
|
||||
HBuilder.setIcon(null)
|
||||
HBuilder.setTitle(elements[0].data0)
|
||||
HBuilder.setMessage(elements[0].data0)
|
||||
HBuilder.setPositiveButton(R.string.dialog_schließen, null)
|
||||
//endregion
|
||||
Column(
|
||||
verticalArrangement = Arrangement.Center,
|
||||
modifier = Modifier
|
||||
.fillMaxHeight()
|
||||
.padding(3.dp)
|
||||
.verticalScroll(rememberScrollState())
|
||||
.horizontalScroll(rememberScrollState())
|
||||
) {
|
||||
var dialogCounter = 0
|
||||
|
||||
TextButton(
|
||||
onClick = { HBuilder.show() }) {
|
||||
Text(text = "H")
|
||||
for (row in 0..6) {
|
||||
Row {
|
||||
InformationBox(text = (row + 1).toString(), color = MaterialTheme.colorScheme.onBackground)
|
||||
|
||||
for (element in 0..31) {
|
||||
val index = row * 32 + element
|
||||
if (elementNameList[index] != "") {
|
||||
val elementJsonIndex = dialogCounter
|
||||
++dialogCounter
|
||||
ElementBox(arrayIndex = index, onClick = { dialogChooser = elementJsonIndex })
|
||||
} else {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(3.dp)
|
||||
.size(60.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
InformationBox(text = shells[row], color = colors[row])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dialogChooser != -1) {
|
||||
val dcValue = dialogChooser
|
||||
AlertDialog(
|
||||
onDismissRequest = { dialogChooser = -1 },
|
||||
text = {
|
||||
Text(
|
||||
modifier = Modifier
|
||||
.verticalScroll(rememberScrollState())
|
||||
.horizontalScroll(rememberScrollState()),
|
||||
text = readElementData(dcValue),
|
||||
color = MaterialTheme.colorScheme.onPrimaryContainer,
|
||||
style = MaterialTheme.typography.labelMedium
|
||||
)
|
||||
},
|
||||
confirmButton = {
|
||||
Text(
|
||||
text = "Schließen",
|
||||
color = MaterialTheme.colorScheme.secondary,
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
modifier = Modifier.clickable {
|
||||
dialogChooser = -1
|
||||
}
|
||||
)
|
||||
},
|
||||
containerColor = MaterialTheme.colorScheme.primaryContainer,
|
||||
modifier = Modifier
|
||||
.padding(5.dp)
|
||||
.wrapContentWidth(unbounded = true)
|
||||
)
|
||||
}
|
||||
if (showInfo) {
|
||||
AlertDialog(
|
||||
onDismissRequest = { showInfo = false },
|
||||
text = {
|
||||
Text(
|
||||
text = "Auf ein Element tippen, um genauere Informationen zu erhalten.\n\n" +
|
||||
"Verwendung im Querformat empfohlen",
|
||||
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)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ElementBox(arrayIndex: Int, onClick: () -> Unit){
|
||||
Box(modifier = Modifier
|
||||
.padding(3.dp)
|
||||
.size(60.dp)
|
||||
.background(
|
||||
color = colors[elementButtonColors[arrayIndex]],
|
||||
shape = RoundedCornerShape(5)
|
||||
)
|
||||
.clickable { onClick() }
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
text = elementNameList[arrayIndex],
|
||||
color = com.schoolapp.cleverclass.ui.theme.TextOnColouredButton,
|
||||
style = MaterialTheme.typography.labelLarge
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun InformationBox(text: String, color: Color){
|
||||
Box(modifier = Modifier
|
||||
.padding(3.dp)
|
||||
.size(60.dp)
|
||||
.border(
|
||||
width = 1.dp,
|
||||
color = MaterialTheme.colorScheme.primaryContainer
|
||||
)
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
text = text,
|
||||
color = color,
|
||||
style = MaterialTheme.typography.labelLarge
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun readElementData(int: Int): String {
|
||||
val elementData = rootObj.getJSONArray("elements").getJSONObject(int)
|
||||
return "Name:\n${elementData.get("Name")}\n\n" +
|
||||
"Ordnungszahl:\n${elementData.get("Ordnungszahl")}\n\n" +
|
||||
"Gruppe:\n${elementData.get("Gruppe")}\n\n" +
|
||||
"Periode/Hauptquantenzahl:\n${elementData.get("Periode/Hauptquantenzahl")}\n\n" +
|
||||
"Schale:\n${elementData.get("Schale")}\n\n" +
|
||||
"Molare Masse:\n${elementData.get("Molare Masse")}\n\n" +
|
||||
"Dichte:\n${elementData.get("Dichte")}\n\n" +
|
||||
"Elektronegativität:\n${elementData.get("Elektronegativität")}\n\n" +
|
||||
"Schmelztemperatur:\n${elementData.get("Schmelztemperatur")}\n\n" +
|
||||
"Siedetemperatur:\n${elementData.get("Siedetemperatur")}\n\n" +
|
||||
"künstlich:\n${elementData.get("künstlich")}\n\n" +
|
||||
"radioaktiv:\n${elementData.get("radioaktiv")}\n\n" +
|
||||
"Halbwertszeit:\n${elementData.get("Halbwertszeit")}\n\n" +
|
||||
"Strahlungsart:\n${elementData.get("Strahlungsart")}"
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
package com.schoolapp.cleverclass
|
||||
|
||||
import android.content.Context
|
||||
import java.io.BufferedReader
|
||||
import java.io.IOException
|
||||
import java.io.InputStreamReader
|
||||
|
||||
val elements = mutableListOf<PSEData>()
|
||||
var i = 0
|
||||
|
||||
class PSEData() {
|
||||
var data0: String = ""
|
||||
var data1: String = ""
|
||||
var data2: String = ""
|
||||
var data3: String = ""
|
||||
var data4: String = ""
|
||||
var data5: String = ""
|
||||
var data6: String = ""
|
||||
var data7: String = ""
|
||||
var data8: String = ""
|
||||
var data9: String = ""
|
||||
var data10: String = ""
|
||||
var data11: String = ""
|
||||
var data12: String = ""
|
||||
var data13: String = ""
|
||||
var data14: String = ""
|
||||
var data15: String = ""
|
||||
constructor(
|
||||
string0: String,
|
||||
string1: String,
|
||||
string2: String,
|
||||
string3: String,
|
||||
string4: String,
|
||||
string5: String,
|
||||
string6: String,
|
||||
string7: String,
|
||||
string8: String,
|
||||
string9: String,
|
||||
string10: String,
|
||||
string11: String,
|
||||
string12: String,
|
||||
string13: String,
|
||||
string14: String,
|
||||
string15: String
|
||||
) : this() {
|
||||
data0 = string0
|
||||
data1 = string1
|
||||
data2 = string2
|
||||
data3 = string3
|
||||
data4 = string4
|
||||
data5 = string5
|
||||
data6 = string6
|
||||
data7 = string7
|
||||
data8 = string8
|
||||
data9 = string9
|
||||
data10 = string10
|
||||
data11 = string11
|
||||
data12 = string12
|
||||
data13 = string13
|
||||
data14 = string14
|
||||
data15 = string15
|
||||
}
|
||||
}
|
||||
|
||||
fun readData(context: Context) {
|
||||
val elementInput = mutableListOf<String>()
|
||||
val file = "elements_data.txt"
|
||||
val assetManager = context.assets
|
||||
val inputStream = assetManager.open(file)
|
||||
try {
|
||||
BufferedReader(InputStreamReader(inputStream)).use { br ->
|
||||
br.lines().forEach {
|
||||
elementInput.add(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
if (i < 118) {
|
||||
while (i < 118) {
|
||||
val pseobject = PSEData(
|
||||
string0 = elementInput[(i * 16)],
|
||||
string1 = elementInput[(i * 16) + 1],
|
||||
string2 = elementInput[(i * 16) + 2],
|
||||
string3 = elementInput[(i * 16) + 3],
|
||||
string4 = elementInput[(i * 16) + 4],
|
||||
string5 = elementInput[(i * 16) + 5],
|
||||
string6 = elementInput[(i * 16) + 6],
|
||||
string7 = elementInput[(i * 16) + 7],
|
||||
string8 = elementInput[(i * 16) + 8],
|
||||
string9 = elementInput[(i * 16) + 9],
|
||||
string10 = elementInput[(i * 16) + 10],
|
||||
string11 = elementInput[(i * 16) + 11],
|
||||
string12 = elementInput[(i * 16) + 12],
|
||||
string13 = elementInput[(i * 16) + 13],
|
||||
string14 = elementInput[(i * 16) + 14],
|
||||
string15 = elementInput[(i * 16) + 15]
|
||||
)
|
||||
elements.add(pseobject)
|
||||
i++
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,19 +8,23 @@ import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.compose.foundation.clickable
|
||||
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.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.layout.wrapContentHeight
|
||||
import androidx.compose.foundation.layout.wrapContentSize
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Divider
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
@@ -32,8 +36,10 @@ import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
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.unit.dp
|
||||
@@ -61,6 +67,10 @@ fun SettingsContent(activity: ComponentActivity) {
|
||||
val sharedPreferences = activity.getSharedPreferences("Settings", Context.MODE_PRIVATE)
|
||||
val editor = sharedPreferences.edit()
|
||||
|
||||
var showDeleteConfirmation by remember {
|
||||
mutableStateOf(false)
|
||||
}
|
||||
|
||||
Column(modifier = Modifier.fillMaxSize()) {
|
||||
// Top AppBar
|
||||
TopAppBar(
|
||||
@@ -68,7 +78,8 @@ fun SettingsContent(activity: ComponentActivity) {
|
||||
title = {
|
||||
Text(
|
||||
text = "Einstellungen",
|
||||
style = MaterialTheme.typography.headlineSmall
|
||||
style = MaterialTheme.typography.headlineSmall,
|
||||
color = MaterialTheme.colorScheme.onPrimaryContainer
|
||||
)
|
||||
},
|
||||
navigationIcon = {
|
||||
@@ -76,7 +87,8 @@ fun SettingsContent(activity: ComponentActivity) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.ArrowBack,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(28.dp)
|
||||
modifier = Modifier.size(28.dp),
|
||||
tint = MaterialTheme.colorScheme.onPrimaryContainer
|
||||
)
|
||||
}
|
||||
},
|
||||
@@ -89,13 +101,45 @@ fun SettingsContent(activity: ComponentActivity) {
|
||||
.weight(1f)
|
||||
.verticalScroll(rememberScrollState())
|
||||
.fillMaxWidth()
|
||||
|
||||
) {
|
||||
Setting(text = "Stundenplan", sharedPreferences, editor)
|
||||
Setting(text = "Noten", sharedPreferences, editor)
|
||||
Setting(text = "Periodensystem", sharedPreferences, editor)
|
||||
Setting(text = "Mebis", sharedPreferences, editor)
|
||||
Setting(text = "DSBmobile", sharedPreferences, editor)
|
||||
Setting(text = "Mebis", sharedPreferences, editor)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
Divider()
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable {
|
||||
val intent = Intent(activity, DSBLoginActivity::class.java).apply {
|
||||
putExtra(DSBLoginActivity.TYPE_KEY, "Settings")
|
||||
}
|
||||
activity.startActivity(intent)
|
||||
}
|
||||
) {
|
||||
Text(
|
||||
text = "DSBmobile Daten",
|
||||
color = MaterialTheme.colorScheme.onBackground,
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
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
|
||||
@@ -106,20 +150,64 @@ fun SettingsContent(activity: ComponentActivity) {
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Divider()
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Text(
|
||||
text = "About",
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
modifier = Modifier.clickable {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Box(
|
||||
contentAlignment = Alignment.Center,
|
||||
modifier = Modifier
|
||||
.wrapContentSize()
|
||||
.clickable {
|
||||
val intent = Intent(activity, AboutActivity::class.java)
|
||||
activity.startActivity(intent)
|
||||
}
|
||||
) {
|
||||
Text(
|
||||
text = "About",
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onBackground,
|
||||
modifier = Modifier.padding(horizontal = 32.dp, vertical = 8.dp)
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.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
|
||||
fun Setting(text : String, sharedPreferences: SharedPreferences, editor: Editor){
|
||||
@@ -136,8 +224,10 @@ fun Setting(text : String, sharedPreferences: SharedPreferences, editor: Editor)
|
||||
}
|
||||
)
|
||||
Spacer(modifier = Modifier.width(16.dp))
|
||||
Text(text = text,
|
||||
color = MaterialTheme.colorScheme.onPrimaryContainer,
|
||||
style = MaterialTheme.typography.labelMedium)
|
||||
Text(
|
||||
text = text,
|
||||
color = MaterialTheme.colorScheme.onBackground,
|
||||
style = MaterialTheme.typography.labelMedium
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,29 @@
|
||||
package com.schoolapp.cleverclass
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.icu.text.DecimalFormat
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
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.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
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.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.material3.AlertDialog
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
@@ -18,20 +33,60 @@ import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
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.draw.scale
|
||||
import androidx.compose.ui.text.style.TextDecoration
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.content.ContextCompat.startActivity
|
||||
import com.schoolapp.cleverclass.LessonStoreManager.getLessons
|
||||
import com.schoolapp.cleverclass.ui.theme.CleverClassTheme
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
private val listOfDays = listOf("Mo", "Di", "Mi", "Do", "Fr")
|
||||
private val listOfBreakIndexes = mutableListOf<Int>()
|
||||
|
||||
class StundenplanActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
val sharedPreferences = this.getSharedPreferences("TimeTable", Context.MODE_PRIVATE)
|
||||
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 {
|
||||
CleverClassTheme {
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
color = MaterialTheme.colorScheme.background
|
||||
) {
|
||||
StundenplanContent(activity = this)
|
||||
//Auto-load setup, if incomplete
|
||||
if (!setupDone) {
|
||||
val intent = Intent(this@StundenplanActivity, TimeTableSetupActivity::class.java).apply {
|
||||
putExtra(TimeTableSetupActivity.TYPE_KEY, "TimeSetup")
|
||||
}
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
StundenplanContent(activity = this, lessons)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -41,28 +96,360 @@ class StundenplanActivity : ComponentActivity() {
|
||||
// Content of Stundenplan
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun StundenplanContent(activity: ComponentActivity){
|
||||
Column() {
|
||||
fun StundenplanContent(activity: ComponentActivity, loadedLessons: List<List<LessonData>>){
|
||||
//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(
|
||||
colors = TopAppBarDefaults.centerAlignedTopAppBarColors(MaterialTheme.colorScheme.primaryContainer),
|
||||
title = {
|
||||
Text(text = "Stundenplan",
|
||||
style = MaterialTheme.typography.headlineSmall
|
||||
style = MaterialTheme.typography.headlineSmall,
|
||||
color = MaterialTheme.colorScheme.onPrimaryContainer
|
||||
)},
|
||||
navigationIcon = {
|
||||
IconButton(onClick = { activity.finish() }) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.ArrowBack,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(28.dp)
|
||||
modifier = Modifier.size(28.dp),
|
||||
tint = MaterialTheme.colorScheme.onPrimaryContainer
|
||||
)
|
||||
}
|
||||
},
|
||||
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()
|
||||
)
|
||||
|
||||
Column() {
|
||||
//Start of the timetable --> One Row() with multiple Column() to create the table
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.verticalScroll(rememberScrollState())
|
||||
.horizontalScroll(rememberScrollState())
|
||||
) {
|
||||
//Left-handed lesson indexing with beginning time of each lesson or break
|
||||
InfoColumn(
|
||||
loadedLessons = loadedLessons,
|
||||
breakAmnt = breakAmnt,
|
||||
breaksStartTime = listOfBreakStartTimes,
|
||||
firstLesson = firstLesson,
|
||||
lessonLength = lessonLength,
|
||||
breaksLength = listOfBreakLengths
|
||||
)
|
||||
|
||||
//Creation of 5 Columns
|
||||
loadedLessons.forEachIndexed { index, dailyLessons ->
|
||||
//Width variable for variable Box sizes
|
||||
Column(modifier = Modifier.width(250.dp)) {
|
||||
//Additional extraIndex to count lessons without breaks getting into the way
|
||||
var extraIndex = 0
|
||||
|
||||
//Box with the day on top of each Column()
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(250.dp, 50.dp)
|
||||
.padding(3.dp)
|
||||
.align(Alignment.CenterHorizontally)
|
||||
.border(
|
||||
width = 1.dp,
|
||||
color = MaterialTheme.colorScheme.primaryContainer
|
||||
)
|
||||
) {
|
||||
Text(
|
||||
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++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Right-handed lesson indexing with beginning time of each lesson or break
|
||||
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
|
||||
}
|
||||
@@ -0,0 +1,711 @@
|
||||
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<LessonData>()
|
||||
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<LessonData>){
|
||||
//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<LessonData>
|
||||
fun saveLessonsUsage(context: Context, lessons: List<LessonData>, int: Int) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
saveLessons(context, lessons, listOfDays[int])
|
||||
}
|
||||
}
|
||||
117
app/src/main/java/com/schoolapp/cleverclass/Utilities.kt
Normal file
@@ -0,0 +1,117 @@
|
||||
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.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.res.painterResource
|
||||
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
|
||||
|
||||
fun formatFloat(number: Float): String {
|
||||
var formattedString = number.toString()
|
||||
|
||||
formattedString = formattedString.substring(0, if(formattedString.length == 3) 3 else 4)
|
||||
|
||||
if(number.isNaN())
|
||||
formattedString = "0.0"
|
||||
|
||||
return formattedString
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun LoadingScreen(){
|
||||
Box(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentAlignment = Alignment.Center
|
||||
){
|
||||
CircularProgressIndicator(color = MaterialTheme.colorScheme.secondary)
|
||||
}
|
||||
}
|
||||
@Composable
|
||||
fun StaticLoadingScreen(){
|
||||
Box(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentAlignment = Alignment.Center
|
||||
){
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Text(
|
||||
text = "Loading...",
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
modifier = Modifier.padding(start = 16.dp, end = 16.dp),
|
||||
color = MaterialTheme.colorScheme.onBackground
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.static_loading_icon),
|
||||
contentDescription = null,
|
||||
colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onBackground),
|
||||
contentScale = ContentScale.FillBounds
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
@@ -4,10 +4,22 @@ import androidx.compose.ui.graphics.Color
|
||||
|
||||
val Black100 = Color(0xFF000000)
|
||||
val Black90 = Color(0xFF1F1F1F)
|
||||
val Black70 = Color(0xFF333333)
|
||||
val Black80 = Color(0xFF333333)
|
||||
val Black70 = Color(0xFF4B4B4B)
|
||||
|
||||
val White100 = Color(0xFFFFFFFF)
|
||||
val White90 = Color(0xFFEBEBEB)
|
||||
val White70 = Color(0xFFD3D3D3)
|
||||
val White90 = Color(0xFFFAFAFA)
|
||||
val White80 = Color(0xFFD3D3D3)
|
||||
val White70 = Color(0xFFAFAFAF)
|
||||
|
||||
|
||||
val TextOnColouredButton = Color(0xFF121212)
|
||||
val MiddleColor = Color(0xFF808080)
|
||||
|
||||
val dismissLight = Color(0xFF536DFE)
|
||||
val dismissDark = Color(0xFF448AFF)
|
||||
|
||||
val InputPrimaryColor = Color(0xFF3F57E0)
|
||||
val InputSecondaryColor = Color(0x7E4154FF)
|
||||
|
||||
val testColor = Color(0xFF00FF00)
|
||||
|
||||
@@ -18,15 +18,23 @@ import androidx.core.view.WindowCompat
|
||||
|
||||
private val DarkColorScheme = darkColorScheme(
|
||||
background = Black90,
|
||||
primaryContainer = Black70,
|
||||
onPrimary = White100,
|
||||
primaryContainer = Black80,
|
||||
secondaryContainer = Black70,
|
||||
onPrimaryContainer = White100,
|
||||
secondary = dismissDark,
|
||||
onBackground = White100,
|
||||
outlineVariant = Black80,
|
||||
primary = Color(0xFF536DFE)
|
||||
)
|
||||
|
||||
private val LightColorScheme = lightColorScheme(
|
||||
background = White90,
|
||||
primaryContainer = White70,
|
||||
onPrimary = Black100,
|
||||
primaryContainer = White80,
|
||||
secondaryContainer = White70,
|
||||
onPrimaryContainer = Black100,
|
||||
secondary = dismissLight,
|
||||
onBackground = Black100,
|
||||
outlineVariant = White80,
|
||||
primary = Color(0xFF8396FF)
|
||||
)
|
||||
|
||||
@@ -50,7 +58,7 @@ fun CleverClassTheme(
|
||||
if (!view.isInEditMode) {
|
||||
SideEffect {
|
||||
val window = (view.context as Activity).window
|
||||
window.statusBarColor = Black70.toArgb()
|
||||
window.statusBarColor = Black80.toArgb()
|
||||
WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,13 @@ val Typography = Typography(
|
||||
lineHeight = 24.sp,
|
||||
letterSpacing = 0.5.sp
|
||||
),
|
||||
labelLarge = TextStyle(
|
||||
fontFamily = FontFamily(Font(R.font.arlrdbd, FontWeight.Normal)),
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 24.sp,
|
||||
lineHeight = 24.sp,
|
||||
letterSpacing = 0.5.sp
|
||||
),
|
||||
headlineMedium = TextStyle(
|
||||
fontFamily = FontFamily(Font(R.font.arlrdbd, FontWeight.Normal)),
|
||||
fontWeight = FontWeight.Normal,
|
||||
|
||||
@@ -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>
|
||||
5
app/src/main/res/drawable/baseline_visibility_24.xml
Normal 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="M12,4.5C7,4.5 2.73,7.61 1,12c1.73,4.39 6,7.5 11,7.5s9.27,-3.11 11,-7.5c-1.73,-4.39 -6,-7.5 -11,-7.5zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5zM12,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3z"/>
|
||||
</vector>
|
||||
5
app/src/main/res/drawable/baseline_visibility_off_24.xml
Normal 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="M12,7c2.76,0 5,2.24 5,5 0,0.65 -0.13,1.26 -0.36,1.83l2.92,2.92c1.51,-1.26 2.7,-2.89 3.43,-4.75 -1.73,-4.39 -6,-7.5 -11,-7.5 -1.4,0 -2.74,0.25 -3.98,0.7l2.16,2.16C10.74,7.13 11.35,7 12,7zM2,4.27l2.28,2.28 0.46,0.46C3.08,8.3 1.78,10.02 1,12c1.73,4.39 6,7.5 11,7.5 1.55,0 3.03,-0.3 4.38,-0.84l0.42,0.42L19.73,22 21,20.73 3.27,3 2,4.27zM7.53,9.8l1.55,1.55c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.66 1.34,3 3,3 0.22,0 0.44,-0.03 0.65,-0.08l1.55,1.55c-0.67,0.33 -1.41,0.53 -2.2,0.53 -2.76,0 -5,-2.24 -5,-5 0,-0.79 0.2,-1.53 0.53,-2.2zM11.84,9.02l3.15,3.15 0.02,-0.16c0,-1.66 -1.34,-3 -3,-3l-0.17,0.01z"/>
|
||||
</vector>
|
||||
@@ -1,170 +1,74 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
<vector
|
||||
android:height="108dp"
|
||||
android:width="108dp"
|
||||
android:viewportHeight="108"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillColor="#3DDC84"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#3DDC84"
|
||||
android:pathData="M0,0h108v108h-108z"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M9,0L9,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,0L19,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,0L29,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,0L39,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,0L49,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,0L59,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,0L69,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,0L79,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M89,0L89,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M99,0L99,108"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,9L108,9"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,19L108,19"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,29L108,29"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,39L108,39"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,49L108,49"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,59L108,59"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,69L108,69"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,79L108,79"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,89L108,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,99L108,99"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,29L89,29"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,39L89,39"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,49L89,49"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,59L89,59"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,69L89,69"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,79L89,79"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,19L29,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,19L39,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,19L49,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,19L59,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,19L69,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,19L79,89"
|
||||
android:strokeWidth="0.8"
|
||||
android:strokeColor="#33FFFFFF" />
|
||||
<path android:fillColor="#00000000" android:pathData="M9,0L9,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,0L19,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M29,0L29,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M39,0L39,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M49,0L49,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M59,0L59,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M69,0L69,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M79,0L79,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M89,0L89,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M99,0L99,108"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,9L108,9"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,19L108,19"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,29L108,29"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,39L108,39"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,49L108,49"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,59L108,59"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,69L108,69"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,79L108,79"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,89L108,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M0,99L108,99"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,29L89,29"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,39L89,39"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,49L89,49"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,59L89,59"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,69L89,69"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M19,79L89,79"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M29,19L29,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M39,19L39,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M49,19L49,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M59,19L59,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M69,19L69,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
<path android:fillColor="#00000000" android:pathData="M79,19L79,89"
|
||||
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||
</vector>
|
||||
|
||||
9
app/src/main/res/drawable/static_loading_icon.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="36dp"
|
||||
android:height="36dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960">
|
||||
<path
|
||||
android:pathData="M480,880q-83,0 -156,-31.5T197,763q-54,-54 -85.5,-127T80,480q0,-83 31.5,-156T197,197q54,-54 127,-85.5T480,80q83,0 156,31.5T763,197q54,54 85.5,127T880,480q0,83 -31.5,156T763,763q-54,54 -127,85.5T480,880ZM480,800q64,0 123,-24t104,-69L480,480v-320q-134,0 -227,93t-93,227q0,134 93,227t227,93Z"
|
||||
android:fillColor="#e8eaed"/>
|
||||
</vector>
|
||||
5
app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@mipmap/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
5
app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@mipmap/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
||||
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
||||
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 3.0 KiB |
BIN
app/src/main/res/mipmap-hdpi/ic_launcher_background.webp
Normal file
|
After Width: | Height: | Size: 410 B |
BIN
app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp
Normal file
|
After Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 5.1 KiB |
|
Before Width: | Height: | Size: 982 B After Width: | Height: | Size: 1.9 KiB |
BIN
app/src/main/res/mipmap-mdpi/ic_launcher_background.webp
Normal file
|
After Width: | Height: | Size: 292 B |
BIN
app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 4.1 KiB |
BIN
app/src/main/res/mipmap-xhdpi/ic_launcher_background.webp
Normal file
|
After Width: | Height: | Size: 554 B |
BIN
app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp
Normal file
|
After Width: | Height: | Size: 6.7 KiB |
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 7.2 KiB |
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 6.4 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher_background.webp
Normal file
|
After Width: | Height: | Size: 900 B |
BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp
Normal file
|
After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 9.0 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.webp
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp
Normal file
|
After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 16 KiB |
@@ -6,5 +6,8 @@
|
||||
<string name="title_activity_stundenplan">StundenplanActivity</string>
|
||||
<string name="title_activity_noten">NotenActivity</string>
|
||||
<string name="title_activity_fach">FachActivity</string>
|
||||
<string name="dialog_schließen">Schließen</string>
|
||||
<string name="title_activity_dsbactivity">DSBActivity</string>
|
||||
<string name="title_activity_dsbday_view">DSBDayViewActivity</string>
|
||||
<string name="title_activity_dsblogin">DSBLoginActivity</string>
|
||||
<string name="title_activity_timetable_setup">TimetableSetupActivity</string>
|
||||
</resources>
|
||||
@@ -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 .../>
|
||||
-->
|
||||
|
||||