Compare commits

..

41 Commits

Author SHA1 Message Date
Paul
442ef70b8d update AboutActivity 2024-10-24 13:59:01 +02:00
BuildTools
ec3a95fc2d Merge remote-tracking branch 'origin/master' 2024-10-14 15:26:13 +02:00
BuildTools
b07cd27fce Why tf is "Index": 0 @ index = 1 and "Index": 2 @ index = 0 2024-10-14 15:25:58 +02:00
matthias
39c037a9ab delete empty line 2024-07-05 17:44:51 +02:00
matthias
f6956349e0 Small adjustment
3 month --> 3 months
2024-06-26 06:59:01 +02:00
BuildTools
a41bd9cbb3 Small adjustment 2024-06-21 16:52:28 +02:00
BuildTools
0db1e78cd4 Small adjustments 2024-06-16 13:16:56 +02:00
matthias
7bdcb7e3fb Added restart of app when first setup isn't done
when going back via phone feature, user sees the empty timetable
2024-06-09 23:46:58 +02:00
matthias
d36cadeb18 Fixed typo 2024-06-09 23:45:32 +02:00
BuildTools
1056dc39bf Add deleteAllData 2024-06-09 21:58:28 +02:00
matthias
15cadb76c6 Minor appearance adjustments
added scaling with 1.1f(lesson indexes & "Pause") / 0.8f(times)
2024-06-09 13:22:00 +02:00
matthias
41b54f15ea Major Bugfix
fixed removal of lessons
2024-06-09 13:20:31 +02:00
matthias
50eb19a464 minor code cleanup 2024-06-08 14:16:01 +02:00
matthias
4c3de94dd3 Stundenplan bugfixes
fixed indexing with wrong variable for breaks in core
added compatibility for lessons without subject, room or teacher
added horizontal scroll for too long user input
added info icon with alert dialog for scroll explanation
2024-06-08 14:15:43 +02:00
matthias
30ad4bed64 Stundenplan most definitely finished
Added Commenting to StundenplanActivity and TimeTableSetupActivity
2024-06-07 02:13:02 +02:00
BuildTools
f091451fe8 Add Ko-fi
Remove Langlebigstesisotop
Update stuff
2024-06-06 19:21:33 +02:00
1c665fea26 README.md updated
added android version minimum 8.0 as minimum api level defined in CleverClass/app/build.gradle.kts is 26
enlarged link to the license as version and release date belong to its name
2024-06-05 08:07:46 +00:00
matthias
41be804d5b Stundenplan commit for meeting
Setup most definitely done
Display still has some issues
2024-06-05 06:25:15 +02:00
matthias
173a3fc5a5 code cleanup
changed contentDescription = "" to null
2024-06-05 06:23:35 +02:00
BuildTools
718fe65a75 Add README
Update some stuff
2024-06-04 16:19:11 +02:00
BuildTools
e9ca2d8704 Add DSB Zoom
Minor changes PSE
2024-06-03 16:27:39 +02:00
BuildTools
75302eed36 Update Logo
Small changes in PSE
2024-06-02 22:02:30 +02:00
Matthias
ac7ce0ad3d Progressed with the Stundenplan
Progressed with the TimeTableSetupActivity -> (nearly done, only individual day setup left)
hardly progressed with the StundenplanActivity
switching back to Desktop tomorrow evening (progress from holidays)
2024-05-31 23:29:52 +02:00
Matthias
344aa1ef9f Merge remote-tracking branch 'origin/master' 2024-05-27 17:53:02 +02:00
Matthias
2aae27e77c fixed typo "Speichen" --> "Speichern" 2024-05-27 17:52:35 +02:00
2a6e970c71 app/src/main/assets/elements_data.json aktualisiert 2024-05-23 20:47:25 +00:00
BuildTools
e64a010e1d Fix PSE jsonIndex
Update About Page
2024-05-23 21:07:11 +02:00
cfdf00e0a5 app/src/main/assets/elements_data.json aktualisiert 2024-05-23 17:40:58 +00:00
matthias
d982d71f31 Started StundenplanActivity
began with the implementation of sharedPreferences
added LessonStoreManager
added TimeTableSetupActivity (-> + entries in the AndroidManifest.xml & strings.xml)
committed version of "Stundenplan" is unfinished and malfunctional (switching to Laptop for holidays)
2024-05-20 21:36:47 +02:00
BuildTools
25bbf0f6b9 PSE remove groups 2024-05-17 13:31:10 +02:00
BuildTools
c54a79e218 Polish PSE 2024-05-16 19:20:28 +02:00
BuildTools
7941081b90 Finish Mebis 2024-05-14 17:23:16 +02:00
BuildTools
b2e4a4f093 Start Mebis
Fix App crash DSBmobile
2024-04-30 17:16:02 +02:00
BuildTools
c16cc2d364 Finish DSBmobile 2024-04-25 19:28:37 +02:00
matthias
d3a9bf72c9 removed the ">>>" in the dialog
added Modifier.wrapContentWidth(unbounded = true) to dialogs
2024-04-25 16:26:43 +02:00
matthias
f2779cb442 Merge remote-tracking branch 'origin/master' 2024-04-24 21:52:11 +02:00
matthias
1012287c4c put PSE Boxes into loop
added colors list
changed the dialog assignment from list to a single var
added new data from elements_data.json into the dialog
2024-04-24 21:51:56 +02:00
matthias
d77c20e890 cosmetical change 2024-04-24 21:48:43 +02:00
matthias
2a32bcebcc Added "Gruppe", "Periode/Hauptquantenzahl" & "Schale" so PSE GUI is clean 2024-04-24 21:32:59 +02:00
BuildTools
98b68d2d70 Color adjustments 2024-04-24 20:25:52 +02:00
BuildTools
f669ba29f0 Fix back icon PSE 2024-04-23 16:36:23 +02:00
54 changed files with 3697 additions and 1882 deletions

View File

@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="deploymentTargetDropDown">
<value>
<entry key="app">
<State />
</entry>
</value>
</component>
</project>

View File

@@ -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.

View File

@@ -23,6 +23,7 @@ android {
buildTypes {
release {
isMinifyEnabled = false
isDebuggable = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
@@ -57,7 +58,6 @@ dependencies {
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("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"))

View File

@@ -15,16 +15,16 @@
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=".MebisActivity"
android:exported="false"
android:label="@string/title_activity_mebis"
android:theme="@style/Theme.CleverClass" />
<activity
android:name=".DSBActivity"
android:exported="false"
@@ -40,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"

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

View File

@@ -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
@@ -70,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",
style = MaterialTheme.typography.bodySmall,
modifier = Modifier.padding(start = 16.dp, end = 16.dp))
Divider()
Box(contentAlignment = Alignment.CenterStart,
modifier = Modifier
.fillMaxWidth()
.clickable {
val intent =
Intent(Intent.ACTION_VIEW, Uri.parse("https://ko-fi.com/cleverclass"))
activity.startActivity(intent)
}
) {
Row {
Text(
text = "Support the Devs",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onBackground,
modifier = Modifier.padding(16.dp)
)
Spacer(modifier = Modifier.weight(1f))
Image(
painter = painterResource(R.drawable.baseline_open_in_browser_24),
contentDescription = null,
colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onBackground),
modifier = Modifier.padding(16.dp)
)
}
}
}
}
}
@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))
}

View File

@@ -2,6 +2,7 @@ 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
@@ -20,7 +21,6 @@ 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.CircularProgressIndicator
import androidx.compose.material3.Divider
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
@@ -31,6 +31,7 @@ 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
@@ -54,12 +55,23 @@ 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)
}
}
@@ -71,13 +83,20 @@ class DSBActivity : ComponentActivity() {
@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(1)
mutableStateOf(-2)
}
var folders by remember {
mutableStateOf(mainFolder.listFiles())
}
LaunchedEffect(Unit) {
GlobalScope.launch(Dispatchers.IO) {
loadingState = downloadDSB(activity)
folders = mainFolder.listFiles()
}
}
@@ -100,12 +119,13 @@ fun DSBContent(activity: ComponentActivity){
}
},
actions = {
if(loadingState != 1) {
if(loadingState != -2) {
IconButton(
onClick = {
loadingState = 1
loadingState = -2
GlobalScope.launch(Dispatchers.IO) {
loadingState = downloadDSB(activity)
folders = mainFolder.listFiles()
}
}
) {
@@ -122,13 +142,16 @@ fun DSBContent(activity: ComponentActivity){
)
when (loadingState) {
1 -> {
DSBLoading()
-2 -> {
LoadingScreen()
}
-1 -> {
DSBNoInternet()
NO_INTERNET_CONNECTION_CODE -> {
DSBError("Keine Internetverbindung")
}
else -> {
1 ->{
DSBError("Login fehlgeschlagen")
}
0 -> {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
@@ -137,9 +160,7 @@ fun DSBContent(activity: ComponentActivity){
) {
Spacer(modifier = Modifier.height(32.dp))
val mainFolder = File(activity.filesDir, "dataDSB")
mainFolder.listFiles()?.forEach { folder ->
folders.forEach { folder ->
DayPrefab(folder, activity)
Spacer(modifier = Modifier.height(32.dp))
Divider()
@@ -147,28 +168,43 @@ fun DSBContent(activity: ComponentActivity){
}
}
}
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 DSBLoading(){
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
){
CircularProgressIndicator()
}
}
@Composable
fun DSBNoInternet(){
fun DSBError(message: String){
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
){
Text(
text = "Keine Internetverbindung",
text = message,
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onBackground
)

View File

@@ -5,6 +5,7 @@ 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
@@ -12,8 +13,7 @@ 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.verticalScroll
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material3.ExperimentalMaterial3Api
@@ -25,13 +25,22 @@ 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
@@ -64,12 +73,16 @@ class DSBDayViewActivity : ComponentActivity() {
@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(),
text = information.get("PlanInfo").toString().removeSuffix(".pdf"),
style = MaterialTheme.typography.headlineSmall,
color = MaterialTheme.colorScheme.onPrimaryContainer
)},
@@ -83,24 +96,43 @@ fun DSBDayViewContent(activity: ComponentActivity){
)
}
},
modifier = Modifier.fillMaxWidth()
modifier = Modifier
.fillMaxWidth()
.zIndex(1f)
)
val imageFiles = folder.listFiles { file -> file.extension == "jpg" }?.toList()
Column(
LazyColumn(
userScrollEnabled = true,
modifier = Modifier
.padding(8.dp)
.verticalScroll(rememberScrollState())
.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 {
Image(
bitmap = BitmapFactory.decodeFile(it.absolutePath).asImageBitmap(),
contentDescription = null,
contentScale = ContentScale.FillWidth,
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(8.dp))
item {
Image(
bitmap = BitmapFactory.decodeFile(it.absolutePath).asImageBitmap(),
contentDescription = null,
contentScale = ContentScale.FillWidth,
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(8.dp))
}
}
}
}

View File

@@ -14,26 +14,34 @@ 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 -1
return NO_INTERNET_CONNECTION_CODE
val mainDir = createDataFolder(context)
val jsonResponse = downloadDataTask()
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")
downloadImagesTask(jsonResponse, mainDir)
return 0
return downloadImagesTask(jsonResponse, mainDir)
}
private fun downloadDataTask() : String {
private fun downloadDataTask(username: String, password: String) : String {
return try {
val requestBody = createRequestBody()
val requestBody = createRequestBody(username = username, password = password)
val jsonRequest = createJsonRequest(requestBody)
val responseData = fetchData(jsonRequest)
@@ -45,9 +53,7 @@ private fun downloadDataTask() : String {
}
}
private fun createRequestBody(): String {
val username = "149002"
val password = "Vertretungsplan"
private fun createRequestBody(username: String, password: String): String {
val currentDateTime = LocalDateTime.now().toString()
val requestBody = JSONObject().apply {
@@ -118,11 +124,22 @@ private fun decompressGZIPAndDecodeBase64(compressedData: String): String {
return outputStream.toString("UTF-8")
}
private fun downloadImagesTask(jsonResponse: String, mainDir : File){
private fun downloadImagesTask(jsonResponse: JSONObject, mainDir : File) : Int {
clearFolder(mainDir)
val jsonResponseObject = JSONObject(jsonResponse)
val jsonDaysObject = jsonResponseObject.getJSONArray("ResultMenuItems").getJSONObject(0).getJSONArray("Childs").getJSONObject(0).getJSONObject("Root").getJSONArray("Childs")
var jsonDaysObject = jsonResponse.getJSONArray("ResultMenuItems")
for (i in 0 until jsonDaysObject.length()) {
if (jsonDaysObject.getJSONObject(i).get("Index") == 0) {
jsonDaysObject = jsonDaysObject.getJSONObject(i).getJSONArray("Childs")
break
}
}
for (i in 0 until jsonDaysObject.length()) {
if (jsonDaysObject.getJSONObject(i).get("Index") == 0) {
jsonDaysObject = jsonDaysObject.getJSONObject(i).getJSONObject("Root").getJSONArray("Childs")
break
}
}
for (d in 0 until jsonDaysObject.length()) {
val jsonDayObject = jsonDaysObject.getJSONObject(d)
@@ -139,9 +156,12 @@ private fun downloadImagesTask(jsonResponse: String, mainDir : File){
val pageIndex = jsonPageObject.get("Index")
val imageLink = jsonPageObject.get("Detail").toString()
downloadImage(day, URL(imageLink), "$pageIndex.jpg")
val downloadState = downloadImage(day, URL(imageLink), "$pageIndex.jpg")
if (downloadState != 0)
return downloadState
}
}
return 0
}
private fun createDayInformationJson(folder: File, jsonDayObject: JSONObject){
@@ -158,22 +178,29 @@ private fun createDayInformationJson(folder: File, jsonDayObject: JSONObject){
infoFile.writeText(infoJson.toString())
}
private fun downloadImage(folder: File, url: URL, fileName: String){
private fun downloadImage(folder: File, url: URL, fileName: String) : Int {
val file = File(folder, fileName)
val connection = url.openConnection()
val inputStream = connection.getInputStream()
val outputStream = FileOutputStream(file)
try {
val connection = url.openConnection()
val inputStream = connection.getInputStream()
val outputStream = FileOutputStream(file)
val buffer = ByteArray(1024)
var bytesRead: Int
val buffer = ByteArray(1024)
var bytesRead: Int
while (inputStream.read(buffer).also { bytesRead = it } != -1) {
outputStream.write(buffer, 0, bytesRead)
while (inputStream.read(buffer).also { bytesRead = it } != -1) {
outputStream.write(buffer, 0, bytesRead)
}
inputStream.close()
outputStream.close()
}
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 {
@@ -186,23 +213,14 @@ private fun createDataFolder(context: Context) : File {
return folder
}
private fun clearFolder(directory: File) {
if (directory.exists()) {
val files = directory.listFiles()
if (files != null) {
for (file in files) {
if (file.isDirectory) {
clearFolder(file)
}
file.delete()
}
}
}
}
private fun isInternetAvailable(context: Context): Boolean {
val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val network = connectivityManager.activeNetwork
val networkCapabilities = connectivityManager.getNetworkCapabilities(network)
return networkCapabilities?.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) ?: false
return try {
val networkCapabilities = connectivityManager.getNetworkCapabilities(network)
networkCapabilities?.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) ?: false
} catch (e: SecurityException) {
Log.e("Internet", e.toString())
false
}
}

View 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
)
}
}

View File

@@ -89,7 +89,6 @@ class FachActivity : ComponentActivity() {
CoroutineScope(Dispatchers.IO).launch {
getGrades(this@FachActivity, name).collect { savedGrades ->
grades = savedGrades.toMutableList()
println()
}
}
@@ -513,7 +512,7 @@ fun GradePrefab(
@Composable
fun Item(name : String, onItemClick: (String) -> Unit){
DropdownMenuItem(
text = { Text(text = name) },
text = { Text(text = name, style = MaterialTheme.typography.labelMedium) },
onClick = { onItemClick(name) }
)
}

View File

@@ -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)
}
}
}

View File

@@ -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
@@ -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 = { switchToActivity(activity, MebisActivity::class.java) }, color = Color(0xFF7C4DFF), text = "Mebis", 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
)
}
}
}

View File

@@ -1,70 +0,0 @@
package com.schoolapp.cleverclass
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.size
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.ui.Modifier
import androidx.compose.ui.unit.dp
import com.schoolapp.cleverclass.ui.theme.CleverClassTheme
class MebisActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
CleverClassTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
MebisContent(activity = this)
}
}
}
}
}
// Content of Mebis
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MebisContent(activity: ComponentActivity){
Column() {
TopAppBar(
colors = TopAppBarDefaults.centerAlignedTopAppBarColors(MaterialTheme.colorScheme.primaryContainer),
title = {
Text(text = "Mebis",
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()
)
Column() {
}
}
}

View File

@@ -114,8 +114,11 @@ fun NotenContent(activity: ComponentActivity){
val editor = sharedPreferences.edit()
var allAverage by remember {
mutableStateOf(calculateAllAverage(loadAllAverages(sharedPreferences)))
mutableStateOf(0f)
}
allAverage = calculateAllAverage(sharedPreferences)
var showDeleteConfirmation by remember {
mutableStateOf(false)
}
@@ -189,7 +192,7 @@ fun NotenContent(activity: ComponentActivity){
saWeight = saWeights[i],
activity = activity,
sharedPreferences = sharedPreferences,
onAvgChange = { allAverage = calculateAllAverage(loadAllAverages(sharedPreferences)) }
onAvgChange = { allAverage = calculateAllAverage(sharedPreferences) }
)
}
@@ -201,7 +204,7 @@ fun NotenContent(activity: ComponentActivity){
saWeight = saWeights[i],
activity = activity,
sharedPreferences = sharedPreferences,
onAvgChange = { allAverage = calculateAllAverage(loadAllAverages(sharedPreferences)) }
onAvgChange = { allAverage = calculateAllAverage(sharedPreferences) }
)
}
@@ -213,7 +216,7 @@ fun NotenContent(activity: ComponentActivity){
saWeight = saWeights[i],
activity = activity,
sharedPreferences = sharedPreferences,
onAvgChange = { allAverage = calculateAllAverage(loadAllAverages(sharedPreferences)) }
onAvgChange = { allAverage = calculateAllAverage(sharedPreferences) }
)
}
@@ -225,7 +228,7 @@ fun NotenContent(activity: ComponentActivity){
saWeight = saWeights[i],
activity = activity,
sharedPreferences = sharedPreferences,
onAvgChange = { allAverage = calculateAllAverage(loadAllAverages(sharedPreferences)) }
onAvgChange = { allAverage = calculateAllAverage(sharedPreferences) }
)
}
@@ -237,7 +240,7 @@ fun NotenContent(activity: ComponentActivity){
saWeight = saWeights[i],
activity = activity,
sharedPreferences = sharedPreferences,
onAvgChange = { allAverage = calculateAllAverage(loadAllAverages(sharedPreferences)) }
onAvgChange = { allAverage = calculateAllAverage(sharedPreferences) }
)
}
@@ -323,7 +326,7 @@ fun FachButton(
}
}
// handles observer creation and disposal
// handles observer disposal
DisposableEffect(Unit) {
sharedPreferences.registerOnSharedPreferenceChangeListener(observer)
onDispose {
@@ -343,9 +346,10 @@ fun GroopTitle(name: String, top: Boolean){
Spacer(modifier = Modifier.height(12.dp))
}
fun calculateAllAverage(avgs: List<Float>): Float{
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()){
@@ -353,6 +357,7 @@ fun calculateAllAverage(avgs: List<Float>): Float{
number += 1
}
}
return (total / number)
}

View File

@@ -1,12 +1,13 @@
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.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
@@ -15,7 +16,9 @@ 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
@@ -30,6 +33,7 @@ import androidx.compose.material3.Text
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
@@ -47,8 +51,10 @@ class PSEActivity : ComponentActivity() {
super.onCreate(savedInstanceState)
setContent {
CleverClassTheme {
Surface(modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background) {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background)
{
PSEContent(activity = this)
}
}
@@ -56,34 +62,70 @@ 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){
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)
}
val elementConstructorList = 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"
)
val elementButtonCurrent = 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, 1,
2, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 4, 5, 6, 7, 8, 9,
10, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 12, 13, 14, 15, 16, 17,
18, 19, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,33, 34, 35,
36, 37, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85,
86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117
)
Column {
TopAppBar(
colors = TopAppBarDefaults.centerAlignedTopAppBarColors(MaterialTheme.colorScheme.primaryContainer),
@@ -98,8 +140,8 @@ fun PSEContent(activity: ComponentActivity){
IconButton(onClick = { activity.finish() }) {
Icon(
imageVector = Icons.Filled.ArrowBack,
contentDescription = "",
modifier = Modifier.size(60.dp),
contentDescription = null,
modifier = Modifier.size(28.dp),
tint = MaterialTheme.colorScheme.onPrimaryContainer
)
}
@@ -110,7 +152,7 @@ fun PSEContent(activity: ComponentActivity){
}) {
Icon(
imageVector = Icons.Outlined.Info,
contentDescription = "",
contentDescription = null,
modifier = Modifier.size(28.dp),
tint = MaterialTheme.colorScheme.onPrimaryContainer
)
@@ -120,217 +162,37 @@ fun PSEContent(activity: ComponentActivity){
)
Column(
verticalArrangement = Arrangement.Center,
modifier = Modifier
.fillMaxHeight()
.padding(3.dp)
.verticalScroll(rememberScrollState())
.horizontalScroll(rememberScrollState())
) {
Row {
for (i in 0 .. 31) {
if (elementConstructorList[i] != "") {
Box(modifier = Modifier
.padding(3.dp)
.size(60.dp)
.background(color = Color(0xFFBCA76D))
.clickable { dialogChooser = elementButtonCurrent[i] }
) {
Text(modifier = Modifier.align(Alignment.Center), text = elementConstructorList[i], color = com.schoolapp.cleverclass.ui.theme.TextOnColouredButton)
var dialogCounter = 0
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)
)
}
} else {
Box(
modifier = Modifier
.padding(3.dp)
.size(60.dp)
)
}
InformationBox(text = shells[row], color = colors[row])
}
}
Row {
for (i in 32 .. 63)
if (elementConstructorList[i] != "") {
Box(modifier = Modifier
.padding(3.dp)
.size(60.dp)
.background(color = Color(0xFFC65253))
.clickable { dialogChooser = elementButtonCurrent[i] }
) {
Text(modifier = Modifier.align(Alignment.Center), text = elementConstructorList[i], color = com.schoolapp.cleverclass.ui.theme.TextOnColouredButton)
}
} else {
Box(
modifier = Modifier
.padding(3.dp)
.size(60.dp)
)
}
}
Row {
for (i in 64 .. 95)
if (elementConstructorList[i] != "") {
Box(modifier = Modifier
.padding(3.dp)
.size(60.dp)
.background(color = Color(0xFF337733))
.clickable { dialogChooser = elementButtonCurrent[i] }
) {
Text(modifier = Modifier.align(Alignment.Center), text = elementConstructorList[i], color = com.schoolapp.cleverclass.ui.theme.TextOnColouredButton)
}
} else {
Box(
modifier = Modifier
.padding(3.dp)
.size(60.dp)
)
}
}
Row {
for (i in 96 .. 127)
if (elementConstructorList[i] != "") {
if (i in 112..121) {
Box(modifier = Modifier
.padding(3.dp)
.size(60.dp)
.background(color = Color(0xFF337733))
.clickable { dialogChooser = elementButtonCurrent[i] }
) {
Text(modifier = Modifier.align(Alignment.Center), text = elementConstructorList[i], color = com.schoolapp.cleverclass.ui.theme.TextOnColouredButton)
}
}
else {
Box(modifier = Modifier
.padding(3.dp)
.size(60.dp)
.background(color = Color(0xFFDC2222))
.clickable { dialogChooser = elementButtonCurrent[i] }
) {
Text(modifier = Modifier.align(Alignment.Center), text = elementConstructorList[i], color = com.schoolapp.cleverclass.ui.theme.TextOnColouredButton)
}
}
} else {
Box(
modifier = Modifier
.padding(3.dp)
.size(60.dp)
)
}
}
Row {
for (i in 128..159)
if (elementConstructorList[i] != "") {
if (i in 144..153) {
Box(modifier = Modifier
.padding(3.dp)
.size(60.dp)
.background(color = Color(0xFFDC2222))
.clickable { dialogChooser = elementButtonCurrent[i] }
) {
Text(modifier = Modifier.align(Alignment.Center), text = elementConstructorList[i], color = com.schoolapp.cleverclass.ui.theme.TextOnColouredButton)
}
}
else {
Box(modifier = Modifier
.padding(3.dp)
.size(60.dp)
.background(color = Color(0xFF129DC3))
.clickable { dialogChooser = elementButtonCurrent[i] }
) {
Text(modifier = Modifier.align(Alignment.Center), text = elementConstructorList[i], color = com.schoolapp.cleverclass.ui.theme.TextOnColouredButton)
}
}
} else {
Box(
modifier = Modifier
.padding(3.dp)
.size(60.dp)
)
}
}
Row {
for (i in 160..191)
if (elementConstructorList[i] != "") {
when (i) {
in 162..175 -> {
Box(modifier = Modifier
.padding(3.dp)
.size(60.dp)
.background(color = Color(0xFFDC2222))
.clickable { dialogChooser = elementButtonCurrent[i] }
) {
Text(modifier = Modifier.align(Alignment.Center), text = elementConstructorList[i], color = com.schoolapp.cleverclass.ui.theme.TextOnColouredButton)
}
}
in 176..185 -> {
Box(modifier = Modifier
.padding(3.dp)
.size(60.dp)
.background(color = Color(0xFF129DC3))
.clickable { dialogChooser = elementButtonCurrent[i] }
) {
Text(modifier = Modifier.align(Alignment.Center), text = elementConstructorList[i], color = com.schoolapp.cleverclass.ui.theme.TextOnColouredButton)
}
}
else -> {
Box(modifier = Modifier
.padding(3.dp)
.size(60.dp)
.background(color = Color(0xFFDDCC69))
.clickable { dialogChooser = elementButtonCurrent[i] }
) {
Text(modifier = Modifier.align(Alignment.Center), text = elementConstructorList[i], color = com.schoolapp.cleverclass.ui.theme.TextOnColouredButton)
}
}
}
} else {
Box(
modifier = Modifier
.padding(3.dp)
.size(60.dp)
)
}
}
Row {
for (i in 192 .. 223)
if (elementConstructorList[i] != "") {
when (i) {
in 194..207 -> {
Box(modifier = Modifier
.padding(3.dp)
.size(60.dp)
.background(color = Color(0xFF129DC3))
.clickable { dialogChooser = elementButtonCurrent[i] }
) {
Text(modifier = Modifier.align(Alignment.Center), text = elementConstructorList[i], color = com.schoolapp.cleverclass.ui.theme.TextOnColouredButton)
}
}
in 208..217 -> {
Box(modifier = Modifier
.padding(3.dp)
.size(60.dp)
.background(color = Color(0xFFDDCC69))
.clickable { dialogChooser = elementButtonCurrent[i] }
) {
Text(modifier = Modifier.align(Alignment.Center), text = elementConstructorList[i], color = com.schoolapp.cleverclass.ui.theme.TextOnColouredButton)
}
}
else -> {
Box(modifier = Modifier
.padding(3.dp)
.size(60.dp)
.background(color = Color(0xFF7044DD))
.clickable { dialogChooser = elementButtonCurrent[i] }
) {
Text(modifier = Modifier.align(Alignment.Center), text = elementConstructorList[i], color = com.schoolapp.cleverclass.ui.theme.TextOnColouredButton)
}
}
}
} else {
Box(
modifier = Modifier
.padding(3.dp)
.size(60.dp)
)
}
}
}
}
@@ -343,9 +205,11 @@ fun PSEContent(activity: ComponentActivity){
modifier = Modifier
.verticalScroll(rememberScrollState())
.horizontalScroll(rememberScrollState()),
text = readElementData(activity, dcValue),
text = readElementData(dcValue),
color = MaterialTheme.colorScheme.onPrimaryContainer,
style = MaterialTheme.typography.labelMedium) },
style = MaterialTheme.typography.labelMedium
)
},
confirmButton = {
Text(
text = "Schließen",
@@ -359,6 +223,7 @@ fun PSEContent(activity: ComponentActivity){
containerColor = MaterialTheme.colorScheme.primaryContainer,
modifier = Modifier
.padding(5.dp)
.wrapContentWidth(unbounded = true)
)
}
if (showInfo) {
@@ -366,9 +231,12 @@ fun PSEContent(activity: ComponentActivity){
onDismissRequest = { showInfo = false },
text = {
Text(
text = "Verwendung im Querformat empfohlen",
text = "Auf ein Element tippen, um genauere Informationen zu erhalten.\n\n" +
"Verwendung im Querformat empfohlen",
color = MaterialTheme.colorScheme.onPrimaryContainer,
style = MaterialTheme.typography.labelMedium) },
style = MaterialTheme.typography.labelMedium
)
},
confirmButton = {
Text(
text = "Schließen",
@@ -384,24 +252,62 @@ fun PSEContent(activity: ComponentActivity){
.padding(5.dp)
)
}
}
fun readElementData(context: Context, int: Int): String {
val file = "elements_data.json"
val assetManager = context.assets
val inputStream = assetManager.open(file)
val rootObj = JSONObject(InputStreamReader(inputStream).readText())
val elementDataArray = rootObj.getJSONArray("elements")
val elementData = elementDataArray.getJSONObject(int)
return "Name\n >>> ${elementData.get("Name")}\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" +
"radioaktiv\n >>> ${elementData.get("radioaktiv")}\n\n" +
"Halbwertszeit\n >>> ${elementData.get("Halbwertszeit")}\n\n" +
"Strahlungsart\n >>> ${elementData.get("Strahlungsart")}\n\n" +
"künstlich\n >>> ${elementData.get("künstlich")}\n\n" +
"langlebigstes Isotop\n >>> ${elementData.get("langlebigstes Isotop")}"
@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")}"
}

View File

@@ -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(
@@ -91,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
@@ -108,21 +150,64 @@ fun SettingsContent(activity: ComponentActivity) {
horizontalAlignment = Alignment.CenterHorizontally
) {
Divider()
Spacer(modifier = Modifier.height(16.dp))
Text(
text = "About",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onBackground,
modifier = Modifier.clickable {
val intent = Intent(activity, AboutActivity::class.java)
activity.startActivity(intent)
}
)
Spacer(modifier = Modifier.height(16.dp))
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(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){
@@ -139,8 +224,10 @@ fun Setting(text : String, sharedPreferences: SharedPreferences, editor: Editor)
}
)
Spacer(modifier = Modifier.width(16.dp))
Text(text = text,
Text(
text = text,
color = MaterialTheme.colorScheme.onBackground,
style = MaterialTheme.typography.labelMedium)
style = MaterialTheme.typography.labelMedium
)
}
}

View File

@@ -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,8 +96,24 @@ 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 = {
@@ -60,11 +131,325 @@ fun StundenplanContent(activity: ComponentActivity){
)
}
},
actions ={
//Info button
IconButton(
onClick = { showInfo = !showInfo }) {
Icon(
imageVector = Icons.Outlined.Info,
contentDescription = null,
modifier = Modifier.size(28.dp),
tint = MaterialTheme.colorScheme.onPrimaryContainer
)
}
//Edit button
IconButton(
onClick = {
val intent = Intent(activity, TimeTableSetupActivity::class.java).apply {
putExtra(TimeTableSetupActivity.TYPE_KEY, "TimeSetup")
}
startActivity(activity, intent, null)
}) {
Icon(
imageVector = Icons.Outlined.Edit,
contentDescription = null,
modifier = Modifier.size(28.dp),
tint = MaterialTheme.colorScheme.onPrimaryContainer
)
}
},
modifier = Modifier.fillMaxWidth()
)
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
}

View File

@@ -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])
}
}

View File

@@ -1,5 +1,34 @@
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()
@@ -10,3 +39,79 @@ fun formatFloat(number: Float): String {
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)
}

View File

@@ -4,11 +4,14 @@ 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)
@@ -16,4 +19,7 @@ val MiddleColor = Color(0xFF808080)
val dismissLight = Color(0xFF536DFE)
val dismissDark = Color(0xFF448AFF)
val InputPrimaryColor = Color(0xFF3F57E0)
val InputSecondaryColor = Color(0x7E4154FF)
val testColor = Color(0xFF00FF00)

View File

@@ -18,21 +18,23 @@ import androidx.core.view.WindowCompat
private val DarkColorScheme = darkColorScheme(
background = Black90,
primaryContainer = Black70,
primaryContainer = Black80,
secondaryContainer = Black70,
onPrimaryContainer = White100,
secondary = dismissDark,
onBackground = White100,
outlineVariant = Black70,
outlineVariant = Black80,
primary = Color(0xFF536DFE)
)
private val LightColorScheme = lightColorScheme(
background = White90,
primaryContainer = White70,
primaryContainer = White80,
secondaryContainer = White70,
onPrimaryContainer = Black100,
secondary = dismissLight,
onBackground = Black100,
outlineVariant = White70,
outlineVariant = White80,
primary = Color(0xFF8396FF)
)
@@ -56,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
}
}

View File

@@ -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,

View File

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

View File

@@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#000000"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="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>

View File

@@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#000000"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="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>

View File

@@ -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"
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" />
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: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>

View 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>

View 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>

View 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>

View File

@@ -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>

View File

@@ -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>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 410 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 982 B

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 292 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 554 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 900 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -7,6 +7,7 @@
<string name="title_activity_noten">NotenActivity</string>
<string name="title_activity_fach">FachActivity</string>
<string name="title_activity_dsbactivity">DSBActivity</string>
<string name="title_activity_mebis">MebisActivity</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>